如何生成与函数arity一样多的参数?

时间:2014-05-14 08:32:14

标签: c++ c++11

假设我有以下函数,它将函数作为参数。

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!
}

用法是:

int foo(int, int) { return 0; }
int bar(int, int, int, int) { return 0; }

int main()
{
    test_func(foo);
    // test_func(bar);
}

就像foobar一样,我有几个返回T的函数,并且取一些T类型的参数。我希望test_func能够生成与RNG一样多的调用,因为函数f需要参数。换句话说,我们可以假设T始终是整数类型,并且每个参数都是相同的,即对RNG的函数调用。

使用function_traits(例如the ones in Boost),我可以获取F的返回类型,这有点帮助。粗略地说,我的问题是

  

如何生成所需数量的函数调用,使其与函数F?

的arity匹配

在C ++ 11之前,我会看一下Boost.Preprocessor,或者可能依赖于模板专业化。现在有更好的方法吗?

2 个答案:

答案 0 :(得分:22)

首先定义一个名为arity的元函数来计算函数的arity(它只是一个简单的实现;可以改进以计算仿函数的arity。请参阅我的答案here。):< / p>

template<typename F> 
struct arity;

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

然后定义另一个名为genseq的元函数来生成整数值的编译时序列:

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!

然后一个函数调用者:

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] ... );
}

然后你的代码就会变成这样:

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

希望有所帮助。

答案 1 :(得分:10)

无需复杂的元计算。

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);
}

为了支持仿函数,需要更长时间的实现,仍然不会太复杂:

// 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());
}