如何将方法指针作为模板参数传递

时间:2010-07-09 01:23:23

标签: c++ boost

我正在尝试编写一个调用作为模板参数给出的类方法的代码。为简化起见,您可以假设该方法具有单个参数(任意类型)并返回void。目标是通过不键入参数类型来避免调用站点中的样板。这是一个代码示例:

template <class Method> class WrapMethod {
  public:
   template <class Object>
   Param* getParam() { return &param_; }
   Run(Object* obj) { (object->*method_)(param_); }
  private:
    typedef typename boost::mpl::at_c<boost::function_types::parameter_types<Method>, 1>::type Param;
    Method method_;
    Param param_
};

现在,在调用站点中,我可以使用该方法而无需编写参数类型。

Foo foo;
WrapMethod<BOOST_TYPEOF(&Foo::Bar)> foo_bar;
foo_bar.GetParam()->FillWithSomething();
foo_bar.Run(foo);

所以,这段代码有效,几乎是我想要的。唯一的问题是我想摆脱调用站点中的BOOST_TYPEOF宏调用。我希望能够编写类似WrapMethod<Foo::Bar> foo_bar而不是WrapMethod<BOOST_TYPEOF(&Foo::Bar)> foo_bar的内容。

我怀疑这是不可能的,因为没有办法引用方法签名而不是使用方法签名本身(这是WrapMethod的变量,以及在调用站点键入的大小)或获取方法指针然后做typeof。

有关如何修复这些或不同方法以避免在调用站点中键入参数类型的任何提示,我们表示赞赏。

只是为了澄清我的需求:解决方案必须在调用站点中没有typename Param。此外,它无法从WrapMethod(或类似)内部调用FillWithSomething。因为该方法名称可以从Param类型更改为Param类型,所以它需要存在于调用站点中。我给出的解决方案满足这两个约束条件,但是需要调用站点中的丑陋BOOST_TYPEOF(在WrapMethod中使用它或其他间接方式会很好,因为只要它是正确的,我的api用户将不会看到代码)。 p>

响应:

据我所知,没有可能的解决办法。如果事先不知道Bar的签名,即使只有基数是必要的,这可以归结为不可能写WrapMethod<&Foo::Bar>之类的事实。更一般地说,如果类型未修复,则不能使用值(而不是类型)的模板参数。例如,在typeof_literal<0>::typeint评估typeof_literal<&Foo::Bar>::type之类的内容是不可能的,这会在我的示例中评估为void (Foo*::)(Param)。请注意,BOOST_TYPEOF或decltype都不会有帮助,因为它们需要存在于校准站点中,并且不能深埋在代码中。下面合法但无效的语法可以解决问题:

template <template<class T> T value> struct typeof_literal {
  typedef decltype(T) type; 
};

在C ++ 0x中,如所选响应(以及其他使用BOOST_AUTO)所指出的,可以使用auto关键字以不同的方式实现相同的目标:

template <class T> WrapMethod<T> GetWrapMethod(T) { return WrapMethod<T>(); }
auto foo_bar = GetWrapMethod(&Foo::Bar);

5 个答案:

答案 0 :(得分:3)

将其写为:

template <typename Object, typename Param, void (Object::*F)(Param)> 
class WrapMethod { 
public: 
   Param* getParam() { return &param_; } 
   void Run(Object* obj) { (obj->*F)(param_); } 

private: 
    Param param_;
}; 

Foo foo; 
WrapMethod<Foo, Param, &Foo::Bar> foo_bar;       
foo_bar.getParam()->FillWithSomething();
foo_bar.Run(foo);

编辑:显示模板函数,允许在没有任何特殊模板包装的情况下执行相同的操作:

template <typename Foo, typename Param>
void call(Foo& obj, void (Foo::*f)(Param))
{
    Param param;
    param.FillWithSomthing();
    obj.*f(param);
}

并将其用作:

Foo foo;
call(foo, &Foo::Bar);

2nd EDIT:修改模板功能以将初始化功能作为参数:

template <typename Foo, typename Param>
void call(Foo& obj, void (Foo::*f)(Param), void (Param::*init)())
{
    Param param;
    param.*init();
    obj.*f(param);
}

并将其用作:

Foo foo;
call(foo, &Foo::Bar, &Param::FillWithSomething);

答案 1 :(得分:2)

如果您的编译器支持decltype,请使用decltype

WrapMethod<decltype(&Foo::Bar)> foo_bar;

编辑:或者,如果你真的想保存输入并且有一个符合C ++ 0x的编译器:

template <class T> WrapMethod<T> GetWrapMethod(T) { return WrapMethod<T>(); }
auto foo_bar= GetWrapMethod(&Foo::Bar);

EDIT2:虽然,实际上,如果你希望它看起来漂亮,你要么必须让用户暴露于C ++语言的复杂性,要​​么将它自己包装在预处理器宏中:

#define WrapMethodBlah(func) WrapMethod<decltype(func)>

答案 2 :(得分:0)

您是否考虑过使用方法模板?

 template <typename T> void method(T & param)
 {
   //body
 }

现在编译器能够隐式确定参数类型

           int i;
           bool b;

           method(i);
           method(b);

或者您可以明确提供类型

           method<int>(i);

您可以为不同的数据类型提供专业化

           template <> void method<int>(int param)
           {
               //body
           }

答案 3 :(得分:0)

当您已经允许BOOST_TYEPOF()时,请考虑将BOOST_AUTO()与对象生成器函数一起使用以允许类型扣除:

template<class Method> WrapMethod<Method> makeWrapMethod(Method mfp) {
    return WrapMethod<Method>(mfp);
}

BOOST_AUTO(foo_bar, makeWrapMethod(&Foo::Bar));

答案 4 :(得分:0)

好的,让我们来看看。

首先,请注意模板参数推导在功能上可用(如几个答案中所述)。

所以,这是一个实现(有点):

// WARNING: no virtual destructor, memory leaks, etc...

struct Foo
{
  void func(int e) { std::cout << e << std::endl; }
};

template <class Object>
struct Wrapper
{
  virtual void Run(Object& o) = 0;
};

template <class Object, class Param>
struct Wrap: Wrapper<Object>
{
  typedef void (Object::*member_function)(Param);

  Wrap(member_function func, Param param): mFunction(func), mParam(param) {}

  member_function mFunction;
  Param mParam;

  virtual void Run(Object& o) { (o.*mFunction)(mParam); }
};

template <class Object, class Param>
Wrap<Object,Param>* makeWrapper(void (Object::*func)(Param), Param p = Param())
{
  return new Wrap<Object,Param>(func, p);
}


int main(int argc, char* argv[])
{
  Foo foo;
  Wrap<Foo,int>* fooW = makeWrapper(&Foo::func);
  fooW->mParam = 1;
  fooW->Run(foo);

  Wrapper<Foo>* fooW2 = makeWrapper(&Foo::func, 1);
  fooW2->Run(foo);
  return 0;
}

我认为使用基类是通过类型擦除隐藏信息的本机C ++方式。