Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I generate as many parameters as is the arity of a function?

Tags:

Suppose I have the following function, that takes a function as a parameter.

template <typename F> void test_func(F f) {     // typedef typename function_traits<F>::return_type T;     typedef int T;      std::mt19937 rng(std::time(0));     std::uniform_int_distribution<T> uint_dist10(0, std::numeric_limits<T>::max());      f(uint_dist10(rng), uint_dist10(rng)); // Problem! } 

Usage would be:

int foo(int, int) { return 0; } int bar(int, int, int, int) { return 0; }  int main() {     test_func(foo);     // test_func(bar); } 

Just like foo and bar, I have several functions that return T, and take some amount of parameters of type T. I would like test_func to generate as many calls to my RNG as the function f takes parameters. In other words, we can assume T is always an integer type, and that each parameter will be the same, i.e. a function call to an RNG.

Using function_traits (such as the ones in Boost), I can fetch the return type of F, and that helps a little. Roughly, my question is

How can I generate a needed amount of function calls so that it matches the arity of the function F?

Before C++11, I would have looked at Boost.Preprocessor, or maybe relied on template specialization. Is there a nicer way of doing it now?

like image 842
Juho Avatar asked May 14 '14 08:05

Juho


People also ask

How many parameters are possible in a function?

This limit is determined by stacked frame size, which is set by OS. Theoretically you can set these max stack size to 8192 bits. Each variable takes up 32 bits then you could pass 256 parameters.

What is arity in programming?

Arity (/ˈærɪti/ ( listen)) is the number of arguments or operands taken by a function, operation or relation in logic, mathematics, and computer science. In mathematics, arity may also be named rank, but this word can have many other meanings in mathematics.

What is arity in JavaScript?

Arity (from Latin) is the term used to refer to the number of arguments or operands in a function or operation, respectively. You're most likely to come across this word in the realm of JavaScript when it's used to mention the number of arguments expected by a JavaScript function.

What is fixed arity?

Fixed arity function is the most popular kind present in almos tall programming languages. Fixed arity function must be called with the same number of arguments as the number of parameters specified in its declaration. Definite arity function must be called with a finite number of arguments.


2 Answers

First define a meta function called arity to compute arity of the function (it is just a simple implementation; can be improved to compute arity of functors also. See my answer here.):

template<typename F>  struct arity;  template<typename R, typename ...Args>  struct arity<R (*)(Args...)> {     static const std::size_t value = sizeof ... (Args); }; 

then define another meta function called genseq to generate a compile time sequence of integral values:

template<int ... N> struct seq {     using type = seq<N...>;      template<int I>     struct push_back : seq<N..., I> {}; };  template<int N> struct genseq : genseq<N-1>::type::template push_back<N-1> {};  template<> struct genseq<0> : seq<> {};  template<int N> using genseq_t = typename genseq<N>::type;  //Just a friendly alias! 

then a function invoker as:

template<typename F, typename ArgEvaluator, int ...N> void invoke(seq<N...>, F f, ArgEvaluator arg_evaluator) {     using arg_type = decltype(arg_evaluator());      constexpr std::size_t arity = sizeof ... (N);      arg_type args[] { (N, arg_evaluator()) ... }; //enforce order of evaluation      f( args[N] ... ); } 

And then your code would become this:

template <typename F> void test_func(F f) {     // typedef typename function_traits<F>::return_type T;     typedef int T;      std::mt19937 rng(std::time(0));     std::uniform_int_distribution<T> uint_dist10(0, std::numeric_limits<T>::max());      //f(uint_dist10(rng), uint_dist10(rng)); // Problem!        auto arg_evaluator = [&]() mutable { return uint_dist10(rng); };       invoke(genseq_t<arity<F>::value>(), f, arg_evaluator); } 

Here is a sample demo.

Hope that helps.

like image 81
Nawaz Avatar answered Oct 09 '22 11:10

Nawaz


No need for complicated meta calculations.

template <typename Ret, typename ... T> void test_func (Ret f (T...)) {   std::mt19937 rng(std::time(0));   f((std::uniform_int_distribution<T>(0, std::numeric_limits<T>::max())(rng))...); }  int moo(int, int, int){ return 0; }  int main () {   test_func(moo); } 

To support functors one needs a bit longer implementation, still not too complicated:

// separate arguments type from function/functor type template <typename F, typename ... T>  void test_func_impl (F f) {   std::mt19937 rng(std::time(0));   f((std::uniform_int_distribution<T>(0, std::numeric_limits<T>::max())(rng))...); }  // overload for a straight function template <typename Ret, typename ... T> void test_func (Ret f (T...)) {   test_func_impl<decltype(f), T...>(f); }  // forwarder for a functor with a normal operator() template <typename F, typename Ret, typename... T> void test_func_for_functor (F f, Ret (F::*)(T...)) {   test_func_impl<F, T...>(f); }  // forwarder for a functor with a const operator() template <typename F, typename Ret, typename... T> void test_func_for_functor (F f, Ret (F::*)(T...)const) {   test_func_impl<F, T...>(f); }  // overload for anything that has operator() template <typename F> void test_func (F f) {   test_func_for_functor(f, &F::operator()); } 
like image 40
3 revs Avatar answered Oct 09 '22 10:10

3 revs