通用回调

时间:2010-03-20 23:42:07

标签: c++ templates metaprogramming

Extends

Related

所以,我正在努力学习模板元编程,我认为这是一个很好的练习。

我正在尝试编写可以使用我传递给它的任意数量的参数回调函数的代码。

// First function to call
int add( int x, int y ) ;

// Second function to call
double square( double x ) ;

// Third func to call
void go() ;

回调创建代码应如下所示:

// Write a callback object that
// will be executed after 42ms for "add"
Callback<int, int, int> c1 ;
c1.func = add ;
c1.args.push_back( 2 );  // these are the 2 args
c1.args.push_back( 5 );  // to pass to the "add" function
                         // when it is called

Callback<double, double> c2 ;
c2.func = square ;
c2.args.push_back( 52.2 ) ;

我在想的是,使用模板元编程我希望能够声明回调,比如写一个这样的结构(请记住这是非常PSEUDOcode)

<TEMPLATING ACTION <<ANY NUMBER OF TYPES GO HERE>> >
struct Callback
{
    double execTime ; // when to execute
    TYPE1 (*func)( TYPE2 a, TYPE3 b ) ;

    void* argList ;   // a stored list of arguments
                      // to plug in when it is time to call __func__
} ;

所以当用

调用时
Callback<int, int, int> c1 ;

你会自动为你构建&lt; <硬化模板动作>像

这样的结构
struct Callback
{
    double execTime ; // when to execute
    int (*func)( int a, int b ) ;

    void* argList ;   // this would still be void*,
                      // but I somehow need to remember
                      // the types of the args..
} ;

任何正确方向的指针都可以开始写这个吗?

4 个答案:

答案 0 :(得分:2)

您可以使用编译器可能不支持的variadic templates执行此操作。我自己从来没有使用它们,因此可能会弄错一些细节,但我会尝试描述它们。

Variadic模板使用“...”运算符。在模板声明(或其他类型表达式)中,省略号表示形式参数可以使用任意数量的参数。

template <typename ... Args>
class Variadic {
public:
   operator()(Args&& ... args);
};

在函数调用表达式中,省略号解包左参数。

Variadic<Args>::operator(Args&& ... args) {
    func(args...);
}

要转发,您可能需要使用std::forward;这是我的知识变得模糊的一个领域。把它放在一起,我们得到:

template <typename ReturnValue, typename ... Args>
class Callback {
    typedef ReturnValue (*Func)(Args ... args);

    double execTime;
    Func func;
    Args... args;

public:
    Callback(double et, Func f) : execTime(et), func(f) {}
    ReturnValue operator()(Args&& ... a);
    ReturnValue operator()();
};

template <typename ReturnValue, typename ... Args>
ReturnValue Callback<ReturnValue, Args>::operator()(Args&& ... a) {
    return (*func)(std::forward(a)...);
}
template <typename ReturnValue, typename ... Args>
ReturnValue Callback<ReturnValue, Args>::operator()() {
    return operator(*func)(args...);
}

答案 1 :(得分:1)

看看boost::bind。我还有更多的话要说......如果你真的想了解内部,那么时间可能最好花在仔细研究它们的来源并试图重新实现它。但考虑到它们如何完善它,重新实现只是学术上的追求。

答案 2 :(得分:0)

C ++ 0x添加了可变参数模板,它直接支持采用任意数量参数的模板。如果没有它,您可以使用部分特化来模拟它,尽管它需要为每个参数数量单独进行专门化。例如,您可以支持1到3个参数,如下所示:

class null_class {};

template <class func, class arg1, class arg2, class arg3>
class callback_t { 
    func f;
    arg1 a;
    arg2 b;
    arg3 c;
public:
    callback_t(func f, arg1 a, arg2 b, arg3 c) : f(f), a(a), b(b), c(c) {}
    double operator()() const { return f(a, b, c); }
};

template <class func, class arg1, class arg2> 
class callback_t<func, arg1, arg2, null_class> { 
    func f;
    arg1 a;
    arg2 b;
public:
    callback_t(func f, arg1 a, arg2 b) : f(f), a(a), b(b) {}
    double operator()() const { return f(a, b); }
};

template <class func, class arg1> 
class callback_t<func, arg1, null_class, null_class> {
    func f;
    arg1 a;
public:
    callback_t(func f, arg1 a) : f(f), a(a) {}
    double operator()() const { return f(a); }
};

template <class func, class arg1, class arg2, class arg3>
callback_t<func, arg1, arg2, arg3> 
callback(func f, arg1 a, arg2 b, arg3 c) { 
    return callback_t<func, arg1, arg2, arg3>(f, a, b, c);
}

template <class func, class arg1, class arg2>
callback_t<func, arg1, arg2, null_class> 
callback(func f, arg1 a, arg2 b) {
    return callback_t<func, arg1, arg2, null_class>(f, a, b);
}

template <class func, class arg>
callback_t<func, arg, null_class, null_class>
callback(func f, arg a) {
    return callback_t<func, arg, null_class, null_class>(f, a);
}

#ifdef TEST
#include <iostream>

double square(double d) { 
    return d * d;
}

double add(double a, double b) { 
    return a + b;
}

double sum(double a, double b, double c) { 
    return a + b + c;
}

int main() {
    double a = 2.0, b = 3.0, c=4.0;

    double d = callback(square, a)();
    double e = callback(add, b, c)();
    double f = callback(sum, a, b, c)();

    std::cout << "2.0 squared = " << d << "\n";
    std::cout << "3.0 + 4.0 = " << e << "\n";
    std::cout << "Sum = " << f << "\n";
    return 0;
}

#endif

返回类型也可以模板化,但为了简单起见,我将其遗漏了(或者至少降低了复杂性)。

答案 3 :(得分:0)

首先,你应该看看Boost.Function,因为它是关于自动包装功能,它会给你我想的想法;)

其次,你的语法有点尴尬。你可以完美地使用函数签名作为模板参数的一部分,它可以很好地处理可变参数模板的问题,因为它允许你传递任意数量的类型;)

Callback< int(int,int) > callback;

表示您的回调将指向一个签名类似于addint add(int, int)的函数。我实际上更喜欢这种语法,因为它使我们传递的内容更加清晰。

在我们开始之前,我有一个问题:你想对返回类型做些什么?

<强> 1。参考

然后有像Boost.Fusion库这样的东西可以帮到你很多(基本上是元组)。

另外,请查看Boost.FunctionTypes,它提供了分析功能签名的工具。

<强> 2。再次上路

// It is nice to have a base class
// Generally callbacks do not return anything though...
struct CallbackBase
{ 
  virtual ~CallbackBase();
  virtual void execute() const = 0;
};

namespace func_ = boost::function_types;

template <
  class Parameters,
  class N = typename mpl_::minus<
    typename mpl_::size< Parameters >::type,
    mpl_::size_t<1>
  >::type
>
class Streamer
{
public:
  typedef Streamer< Parameters, typename mpl_::minus<N,1>::type > next_type;
  typedef typename mpl_::size< Parameters >::type size_type;
  typedef typename mpl_::minus< size_type, N >::type index_type;
  typedef typename mpl_::at<Parameters, index_type>::type arg_type;

  Streamer(Parameters& p): mParameters(p) {}

  next_type operator<<(arg_type arg)
  {
    boost::fusion::at_c<index_type>(mParameters) = arg;
    return next_type(mParameters);
  }

private:
  Parameters& mParameters;
};

template <class Parameters>
struct Streamer<Paramaters,0>
{
  Streamer(Parameters&) {}
};


template <class Function>
class Callback: public CallbackBase
{
public:
  typedef typename func_::result_type<Function>::type result_type;
  typedef typename func_::parameters_type<Function>::type parameters_type;
  typedef typename func_::function_pointer<
    typename func_::components<Function>::type
  >::type function_pointer;

  Callback(function_pointer f): mFunction(f) {}

  virtual void execute() const
  {
    mReturn = Invoke<function_pointer>::Do(f,mParameters);
  }

  Streamer<parameters_type> operator<<(typename mpl_::at<parameters_type, 0>::type arg)
  {
    boost::fusion::at_c<0>(mParameters) = arg;
    return Streamer<parameters_type>(mParameters);
  }

private:
  function_pointer f;
  result_type mResult;
  parameters_type mParameters;
};
嗯,那是我走了多远。我没有处理需要解压缩元组以将参数传递给函数的实际调用。

到目前为止,使用方法是:

int add(int,int);

void pass(const CallbackBase& b);

int main(int argc, char* argv[])
{
  Callback<int(int,int)> c(&add);
  c << 2 << 4;
  pass(c);
}

如果您希望在这个领域继续学习,我强烈建议您深入研究Boost.Fusion,因为如果您无法将结果带入运行时世界,纯模板元编程往往毫无结果:)