How to determine the number of parameters of the std :: function?

I have the following problem. Suppose 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 use not only lambdas, but also functions and even function pointers. So, in the 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 this function to be shared, which means that I do not want the lambda expression to have only two parameters. So I tried to change the signature of the print function to something more general, for example:

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

But now the problem is that the function accepts ANY object, and I'm not okay with that. But the main problem is that I do not know how many parameters the fn object can take.

So, to some extent, I am looking for a compilation method to determine how many arguments fn has and, if possible, change the type of fn to std :: function. And then, given that I know the number of parameters that fn takes, is there a general way to pack an arbitrary number of parameters that should be passed to fn? I don’t even know if this is possible in C ++ 11. I mean, given the number of arguments, is there a way to package the parameters for going to fn? So, if there are two arguments, I would call

 fn(arg1, arg2); 

if there are three of them:

 fn(arg1, arg2, arg3); 

etc.

Thank you all for your understanding.

aa

+2
source share
3 answers

The following snippets may be helpful.

This gives the number of arguments 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 compilations (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 a function object that passes the correct number of arguments, right? Then for each argument we need to provide a value that can be converted to its type. A solution with this community is very difficult (or even impossible). Therefore, I will introduce two alternatives.

1 Each argument is an initialized value of an 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 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...); } 

Three overloads are unambiguous and can be used in the following examples:

 { 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) } 
+1
source

Yes, you can pack as many parameters in fn as you want using variable 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 arguments are contained in the parameter package, you can use sizeof...(args) .

0
source

To determine the signature of the called, you can use the solution from calling the signature of the call lambda or randomly called for the "make_function" . Then you can package the called in std::function or create a tag and use the output of the parameter:

 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 that this uses initialized value arguments; if you need something more complex, you can build std::tuple<Args...> and pass it by calling it for "unpacking" the tuple to call the pointer to the corresponding function .

0
source

All Articles