3

I have the following problem. Say you want to write a generic function that can take a lambda expression. I understand that if the parameter is of type std::function, then I could not only use lambdas, but also functions and even pointers to functions. So at a first step, I did the following:

void print(std::function<void(int, int)> fn) {
  fn(1,2);
}

int main() {

  print([](int i, int j) { std::cout << j <<','<<i<<'\n'; });
  return 0;
}

Now the problem is that I want to make this function generic, meaning that I don't want the lambda expression to have only two parameters. So I tried changing the signature of the print function to something more generic like:

template <class function_type>
void print(function_type fn);

But now the problem is that the function takes ANY object and I'm not ok with that. But the main problem is that, I have no idea how many parameters the object fn can accept.

So in a way I'm looking for a compile time way to determine how many arguments fn has, and if possible to change the type of fn to std::function. And then, given that I know the number of parameters that fn accepts, is there a generic way to pack an arbitrary number of parameters to be passed to fn? I don't even know if this is possible within C++11. What I mean is that given the number of arguments, is there a way to pack parameters to pass to fn? So that if there are two arguments, then I would call

fn(arg1, arg2);

if there are three:

fn(arg1, arg2, arg3);

and so on.

Thank you all for your insight.

aa

ildjarn
  • 62,044
  • 9
  • 127
  • 211
aaragon
  • 2,314
  • 4
  • 26
  • 60
  • How do you expect to implement this for different function aritys? – GManNickG Apr 04 '13 at 09:17
  • What values are you going to pass for the parameters? – ecatmur Apr 04 '13 at 09:21
  • I'll pass only integral types, and all the same. But the class is templated by the number of parameters. – aaragon Apr 04 '13 at 10:38
  • What class is templated? There's no class in your question. – ecatmur Apr 04 '13 at 11:50
  • Because I simplified the code to explain the problem. What I have is an object that can be constructed by passing a functor, or a lambda expression. For example this object is templated by a dimension, so if it's 1D then I would have to call the functor with only one parameter, if it's 2D with 2 parameters and so on... – aaragon Apr 04 '13 at 12:32

3 Answers3

2

The following snippets might be useful.

This gives the number of arguments that a std::function takes

template <typename Signature>
struct count_args;

template <typename Ret, typename... Args>
struct count_args<std::function<Ret(Args...)>> {
    static constexpr size_t value = sizeof...(Args);
};

For example the following code compiles (clang 3.2, gcc 4.7.2 and icc 13.1.0)

static_assert(count_args<std::function<void()        >>::value == 0, "Ops!");
static_assert(count_args<std::function<void(int)     >>::value == 1, "Ops!");
static_assert(count_args<std::function<void(int, int)>>::value == 2, "Ops!");

As far as I understand, you want to call the function object passing the correct number of arguments, right? Then for each argument we need to provide a value which is convertible to its type. A solution with this generality is very hard (or even impossible). Hence, I'll present two alternatives.

1 Each argument is a value initialized object of its type. (This is what ecatmur suggested.)

template <typename Ret, typename... Args>
Ret call(const std::function<Ret(Args...)>& f) {
    return f(Args{}...); // for the intel compiler replace {} with ()
}

2 A fixed value is given and all the arguments are implicitly initialized from this value:

template <typename Ret, typename... Args, typename Val, typename... Vals>
typename std::enable_if<sizeof...(Args) == sizeof...(Vals), Ret>::type
call(const std::function<Ret(Args...)>& f, const Val&, const Vals&... vals) {
    return f(vals...);
}

template <typename Ret, typename... Args, typename Val, typename... Vals>
typename std::enable_if<(sizeof...(Args) > sizeof...(Vals)), Ret>::type
call(const std::function<Ret(Args...)>& f, const Val& val, const Vals&... vals) {
    return call(f, val, val, vals...);
}

The three overloads are unambiguous and can be used as the following examples show:

{
    std::function<char()> f = []() -> char {
        std::cout << "f() ";
        return 'A';
    };
    std::cout << call(f)    << std::endl; // calls f()
    std::cout << call(f, 0) << std::endl; // calls f()
}
{
    std::function<char(int)> f = [](int i) -> char {
        std::cout << "f(" << i << ") ";
        return 'B';
    };
    std::cout << call(f)    << std::endl; // calls f(0)
    std::cout << call(f, 1) << std::endl; // calls f(1)
}
{
    std::function<char(int, int)> f = [](int i, int j) -> char {
        std::cout << "f(" << i << "," << j << ") ";
        return 'C';
    };
    std::cout << call(f)    << std::endl; // calls f(0, 0)
    std::cout << call(f, 2) << std::endl; // calls f(2, 2)
}
Community
  • 1
  • 1
Cassio Neri
  • 19,583
  • 7
  • 46
  • 68
0

Yes you can pack as many parameters to fn as you wish using variadic templates.

template <class function_type, class... Args>
void print(function_type fn, Args... args)
{
    //Call fn with args
    fn(std::forward<Args>(args...));
}

To find out how many args there are in the parameter pack, you can use sizeof...(args).

Tony The Lion
  • 61,704
  • 67
  • 242
  • 415
  • Hi Tony, thanks for your answer. The print function, however, takes a single argument (the functor or lambda expression). What I need is a way to determine with how many parameters I can call fn. Is this even possible? For example, imagine that print is a member function of a class, and depending on the number of parameters that fn accepts, the class will call fn with that number of parameters. – aaragon Apr 04 '13 at 08:58
0

To determine the signature of a callable, you can use the solution from Inferring the call signature of a lambda or arbitrary callable for "make_function". You can then package the callable into a std::function, or create a tag and use parameter inference:

template<typename T> struct tag {};

template<typename F, typename... Args>
void print_impl(F &&fn, tag<void(Args...)>) {
  fn(Args{}...);
}

template<typename F>
void print(F &&fn) {
  print_impl(std::forward<F>(fn), tag<get_signature<F>>{});
}

Note this uses value-initialised arguments; if you want anything more complex you can build a std::tuple<Args...> and pass that along, invoking it per "unpacking" a tuple to call a matching function pointer.

Community
  • 1
  • 1
ecatmur
  • 152,476
  • 27
  • 293
  • 366
  • Hi ecatmur, I'm having problems understanding that code. Given that I know the signature of the functor, I still need to know the number of parameters of that signature at compile time to trigger the right function call to the functor, correct? Or I'm missing something fundamental? What I need is this: let's have an object that can be constructed by passing a functor, or a lambda expression. For example this object is templated by a dimension, so if it's 1D then I would have to call the functor with only one parameter, if it's 2D with 2 parameters and so on. – aaragon Apr 04 '13 at 09:46
  • @AlejandroMarcosAragon no, the number of parameters can be inferred from the computed signature. – ecatmur Apr 04 '13 at 11:51