13

How can I create a variadic template function with std::function as a function parameter that accepts a variadic number of arguments? I tried to reduce the problem to a MWE:

#include <functional>

template <class T> void run(std::function<void(T *)> fun, T *obj) { fun(obj); }

template <class T, class... Args>
void run_variadic(std::function<void(T *, Args...)> fun, T *obj, Args... args) {
  fun(obj, args...);
}

struct Foo {
  void bar() {}
};

int main() {
  Foo foo;
  std::function<void(Foo *)> fun = &Foo::bar;

  run(fun, &foo);                     // works
  run<Foo>(&Foo::bar, &foo);          // works
  run_variadic(fun, &foo);            // works
  run_variadic<Foo>(&Foo::bar, &foo); // does not compile
}

It seems like the mere presence of the variadic template parameter in run_variadic makes it impossible to directly call it with a member function pointer. clang's error message is as follows:

main.cpp:21:3: error: no matching function for call to 'run_variadic'
  run_variadic<Foo>(&Foo::bar, &foo); // does not compile
  ^~~~~~~~~~~~~~~~~
main.cpp:6:6: note: candidate template ignored: could not match 'function<void (Foo *, type-parameter-0-1...)>' against 'void (Foo::*)()'
void run_variadic(std::function<void(T *, Args...)> fun, T *obj, Args&&... args) {
     ^
1 error generated.

Any suggestions on how I can fix run_variadic so that I do not have to go through the extra std::function object?

Background

I have a class hierarchy as

template <class T> class Abstract { ... };
class UnrelatedStuff { ... };
class Derived : public Abstract<UnrelatedStuff> { ... };

There are multiple Derived classes that all have to implement one or more methods to loop over a range of elements. The loop looks something like

#pragma omp parallel for
for (ZFSId i = begin; i != end; ++i) {
  callMemFun(i, and, other, args);
}

All loops should be OpenMP-accelerated. I want the accelerator stuff factored out and not repeated in each method of Derived that uses a loop, so that I only have to change one place if e.g. OpenMP would switch to OpenACC.

Thus I am looking for a way to put the loop (and its decoration) in its own function. Moving it to the Abstract base class is not an option either, since the loops are performance-critical and I cannot have an abstract function call in each loop iteration.

  • This is just a MWE, in the real code the `run` method contains a parallel loop and calls `fun` in each loop iteration. – Michael Schlottke-Lakemper Apr 09 '14 at 14:35
  • 1
    Your problem seems to be that the compiler doesn't know what `Args...` is when trying to map the member function pointer with the `std::function`, since `Args...` hasn't been deduced yet (it's further to the right). – gnzlbg Apr 09 '14 at 15:08
  • 3
    Don't use `std::function` this way. It doesn't work, and would gain you very little even if it did. – R. Martinho Fernandes Apr 09 '14 at 15:23
  • Also, the parameters provided are unlikely to match those of the `function` parameters _exactly_. You'll probably have additional qualifiers and conversions and whatnot. – Mooing Duck Apr 09 '14 at 20:03
  • @MooingDuck but even if you fix that e.g. using `std::decay_t` or some `unqualified_t` trait, you'll still be in trouble. – gnzlbg Apr 09 '14 at 20:13

2 Answers2

15

You are almost always certainly better off abstracting away the function object:

template <class Functor, class... Args>
void run(Functor&& f, Args&&... args) {
  f(std::forward<Args>(args)...);
}

This allows you to do the right thing at call site:

// function object is a lambda that binds to a member function:
run([&](auto... args) { foo.bar(args...); } /*, bar takes no args...*/);

I prefer a lambda to std::function or std::bind but you can also use those if they are already available:

run(std::function<void(Foo *)>{&Foo::bar}, &foo);
run(std::bind(&Foo::bar, &foo));
run(std::mem_fn(&Foo::bar), foo);

I provide a full example program below.

You have now edited the question with new information regarding what you are trying to do.

I'm pretty sure that you don't want to do this, since the OpenMP/OpenACC pragmas like parallel for usually require extra annotations for delivering reasonable performance, and they depend on what you are exactly trying to do at call site.

Still, if you really really want to go this route you can write your own for_each algorithm and dispatch according to an ExecutionAgent (see N3874 and N3731). If OpenMP, TBB, OpenACC parallel task are too slow, you can also easily provide overloads based on e.g. an ExecutionPolicy like this:

template<class RandomAccessRange, class Functor, 
         class ExecutionPolicy = execution::serial_t>
void for_each(RandomAccessRange&& r, Functor&& f, 
              ExecutionPolicy&& ex = ExecutionPolicy{}) {
  detail::for_each_(std::forward<RandomAccessRange>(r), 
                    std::forward<Functor>(f), 
                    std::forward<ExecutionPolicy>(ex));
}

And then you can implement overloads of for_each_ for each execution policy, e.g.:

namespace detail {

template<class RandomAccessRange, class Functor>
void for_each(RandomAccessRange&& r, Functor&& f, execution::serial_t) {
  boost::for_each(std::forward<RandomAccessRange>(r), std::forward<Functor>(f));
}

template<class RandomAccessRange, class Functor>
void for_each(RandomAccessRange&& r, Functor&& f, execution::openmp_t) {
  #pragma omp parallel for
  for (auto&& v : r) { f(v); } 
}

template<class RandomAccessRange, class Functor>
void for_each(RandomAccessRange&& r, Functor&& f, execution::openacc_t) {
  #pragma acc parallel for
  for (auto&& v : r) { f(v); } 
}

template<class RandomAccessRange, class Functor>
void for_each(RandomAccessRange&& r, Functor&& f, execution::tbb_t) {
  tbb::parallel_for_each(std::begin(std::forward<RandomAccessRange>(r)),
                         std::end(std::forward<RandomAccessRange>(r)),
                         std::forward<Functor>(f)); 
}

}  // namespace detail

Note that the ExecutionPolicy is just a tag, i.e.:

namespace execution {
  struct serial_t {}; static const constexpr serial_t serial{};
  struct openmp_t {}; static const constexpr openmp_t openmp{};
  struct openacc_t {}; static const constexpr openacc_t openacc{};
  struct tbb_t {}; static const constexpr tbb_t tbb{};
}  // namespace execution

This will at least give you an efficient TBB backend even tho the OpenMP/OpenACC performance will be mediocre at best. You can take a look at the parallel implementation of libstdc++ where they use OpenMP. Their for_each algorithm is over 1000 lines of code and uses work-stealing.

Full example program:

#include <functional>

template <class Functor, class... Args>
void run(Functor&& f, Args&&... args) {
  f(std::forward<Args>(args)...);
}

struct Foo { void bar() {} };

int main() {
  Foo foo;

  run([&](auto... args) { foo.bar(args...); } /*, bar takes no args*/);
  run(std::function<void(Foo *)>{ &Foo::bar}, &foo);
  run(std::bind(&Foo::bar, &foo));
  run(std::mem_fn(&Foo::bar), foo);
}
gnzlbg
  • 7,135
  • 5
  • 53
  • 106
  • The lambda syntax is C++14, I assume? – Michael Schlottke-Lakemper Apr 09 '14 at 17:40
  • Also, is there a way to make this approach more terse? I will have to call `run` multiple times in different classes and I'd rather not repeat the same lambda pattern (or `std::bind` etc.) every time. – Michael Schlottke-Lakemper Apr 09 '14 at 17:45
  • 1
    @MichaelSchlottke use `std::mem_fn` to create the function object. If you need type erasure you can also store it in a `std::function` once and use it everywhere. If you don't need type erasure, you can probably write function objects instead of normal functions. If you need to wrap normal functions as functions objects... lambdas are as good as it gets, in particular for member functions, since they need to store the `this` pointer somehow, and be called using special syntax (`.*`, `->*`). Without more details about what exactly are you trying to achieve, it is hard to give you better advice. – gnzlbg Apr 09 '14 at 19:56
  • @MichaelSchlottke answering your first comment: that is a variadic C++14 lambda for your variadic function (i've assumed you need to bind a variadic function from your example). You can emulate it in C++11 using `std::bind`, which returns a polymorphic function object that can take any number of arguments, if e.g. you bind a variadic member function to an object. – gnzlbg Apr 09 '14 at 20:09
  • I've updated my question to provide more details on what I'm trying to achieve. – Michael Schlottke-Lakemper Apr 10 '14 at 06:51
  • How `run(std::function{&Foo::bar}, &foo);` works? Does it even compile? – BЈовић Apr 10 '14 at 08:17
  • @BЈовић of course, I've updated my answer with a full program that shows it. – gnzlbg Apr 10 '14 at 15:18
  • @BЈовић if your question is _how exactly does it works_, you just create a std::function object from a pointer to member function that takes a pointer to the object as a first argument, and since run is variadic, it will pass `&foo` to the function object. – gnzlbg Apr 10 '14 at 15:52
5

To answer your comment on the previous answer, that answer can be adapted to support pointers to member functions in the way that you've asked for. The previous answer already works for all callable objects, but not directly with a pointer to member function because those are not callable with the usual f(args) syntax. The following version uses tag dispatch to distinguish between pointers to member functions and traditional callable objects, applying the call syntax appropriate to each case.

template <class Functor, class... Args>
void run_helper(std::false_type, Functor f, Args&&... args)
{
    f(std::forward<Args>(args)...);
}

template <class Functor, class Arg0, class... Args>
void run_helper(std::true_type, Functor f, Arg0&& arg0, Args&&... args)
{
    (std::forward<Arg0>(arg0).*f)(std::forward<Args>(args)...);
}

template <class Functor, class... Args>
void run(Functor f, Args&&... args)
{
    run_helper(typename std::is_member_pointer<Functor>::type(),
               f, std::forward<Args>(args)...);
}

This can be used in all the same ways as the previous answer could, but also supports directly passing in a pointer to member function:

run(&Foo::bar, foo);

It even works with overloaded member functions and member functions which are templates, if you explicitly instantiate the run template to bind to a particular overloaded function or function template instantiation.

Live example: http://ideone.com/vsBS4H

Oktalist
  • 14,336
  • 3
  • 43
  • 63
  • 1
    Upvote since this is the _other_ alternative. The downside here is that if `run` does something non-trivial, you'll probably end up duplicating functionality in `run_helper`. Without more information about what the OP is trying to achieve, it is hard to recommend one alternative over the other :/ – gnzlbg Apr 09 '14 at 20:02
  • 1
    In C++17 you can use [`std::invoke`](https://en.cppreference.com/w/cpp/utility/functional/invoke) which works with all callable objects including pointers to members. – Oktalist Sep 25 '19 at 17:02