【问题标题】:member function pointer with variadic templates带有可变参数模板的成员函数指针
【发布时间】:2017-02-24 22:27:33
【问题描述】:

我正在尝试编写一个在 C++ 中“管理”委托的类。我已经为我实现了委托类。我希望这个委托管理器类有两个功能:

  • 将指针指向具有给定输入参数/返回类型的特定类型的委托实例,并将其缓存。

  • 另一个函数将采用正确类型的成员函数,以便将缓存的委托实例绑定到它。

目前,我有:

template<typename... Args>
struct FunctionParamsPack { };

这是此函数采用的参数类型的容器。即foo(int i, double d) 将是intdouble。我正在听从here 的建议。

然后我有DelegateInfoPack 类:

template<typename FuncRetType,typename... FuncParams>
struct DelegateInfoPack{
    //for look-up by components in the program
    typedef typename DelegateClass<FuncRetType, FuncParams...>          _Delegate;
    //for the delegate manager
    typedef typename FuncRetType                                        _FuncRetType;
    typedef typename FunctionParamsPack<FuncParams...>                  _FuncParams;
};

这个结构体包含在程序中的组件中,它定义了三个类型名,其中两个将在 DelegateManger 类中使用:

template<typename DelegateInfoPack>
class DelegateManager
{

typedef typename    DelegateInfoPack::_Delegate         _Delegate;  

typedef typename    DelegateInfoPack::_FuncRetType      _FuncRetType;
typedef typename    DelegateInfoPack::_FuncParams       _FuncParams;


void CacheDelegate(_Delegate* del,...) {}

template<typename UserClass>
void BindDelegate(..., _FuncRetType(UserClass::*fp)( _FuncParams())) {} //Doesn't work!

}

我的问题在于BindDelegate() 函数。我无法为具有给定返回类型和输入参数类型的类型的成员函数创建正确的签名。

基本上,我需要知道如何让正确的函数指针类型具有给定的返回类型和参数类型,以便我的 BindDelegate 将其作为参数。

【问题讨论】:

  • 为什么不使用 std::function?
  • @Evgeniy 我不熟悉它们。你愿意展示一下吗?

标签: c++ templates delegates function-pointers variadic-templates


【解决方案1】:

一种方法是使用部分特化:

template<typename> class DelegateManager;

template<typename FuncRetType,typename... FuncParams>
class DelegateManager<DelegateInfoPack<FuncRetType,FuncParams...>>
{
    template<typename UserClass>
    void BindDelegate(_FuncRetType(UserClass::*fp)(FuncParams...))
    {
    }
};

另一种方法是拥有一个生成适当函数类型的类

template <typename FuncRetType,typename FuncParams>
struct FunctionPointer;

template <typename FuncRetType,typename...ARGS>
struct FunctionPointer<FuncRetType,FunctionParamsPack<ARGS...>> {
    typedef FuncRetType (Type)(ARGS...);
};

然后在你的 BindDelegate 成员函数中使用它:

template<typename UserClass>
void
  BindDelegate(
    typename FunctionPointer<_FuncRetType,_FuncParams>::Type UserClass::*fp
  )
{ ... }

或者甚至可以将其放入您的 DelegateInfoPack 类中:

template<typename FuncRetType,typename... FuncParams>
struct DelegateInfoPack {
    .
    .
    .
    typedef FuncRetType (_FuncType)(FuncParams...);
};

并在你的 DelegateManager 中使用它

template<typename DelegateInfoPack>
struct DelegateManager
{
    .
    .
    .

    typedef typename DelegateInfoPack::_FuncType _FuncType;

    template<typename UserClass>
    void BindDelegate(_FuncType UserClass::*fp)
    {
    }
};

【讨论】:

  • 我更喜欢保留我当前的设计。真的很想知道我存储参数类型的方法以及我在成员函数指针类型的签名中“调用”它们的方式有什么问题。
  • @user4376555:为了扩展参数包,包必须在范围内,因此必须进行一些更改。有多种方法可以做到这一点。您能否更具体地说明您想要保留设计的哪些方面?
  • 哦,我明白了。目前,DelegateInfoPack 有两个用途,它对委托类型进行类型定义,以便通过包含 DelegateInfoPack 的标头,组件将能够具有 DELEGATE_TYPE del;他们以后可以将其传递给委托经理。其次,它只是一种将所有信息包装在一起并使 DelegateManager 类干净的整洁方式。听起来我不能像我希望的那样保持它“干净”。我试着和“朋友”一起玩,但也没有用。我想我将不得不让 DelegateInfoPack 变得更薄,并从中去掉 FuncParams。
  • 顺便说一句,我确实有一个容器类来存储这些委托管理器的模板化版本。所以我的 DelegateManager 实际上具有以下形式:template&lt;typename DelegateInfoPack&gt; class DelegateManager Contract : public DelegateManagerContainer{...} 你能将你的template&lt;typename&gt; class DelegateManager; 与这样的容器类结合起来吗?
  • @user4376555:是的,您可以让模板特化派生自另一个类。
【解决方案2】:

作为解决任务的另一种方法 - C++11 引入了新的语言功能,可以使您的代码更灵活地使用标准元素

#include <iostream>
#include <functional>
#include <tuple>
#include <iostream>

using std::cout;
using std::endl;
using namespace std::placeholders;

// helpers for tuple unrolling
template<int ...> struct seq {};
template<int N, int ...S> struct gens : gens<N-1, N-1, S...> {};
template<int ...S> struct gens<0, S...>{ typedef seq<S...> type; };

// simple function
double foo_fn(int x, float y, double z)
{
  return x + y + z;
}

// structure with memner function to call
struct foo_struct
{
    // member function to be used as a delegate
    double foo_fn(int x, float y, double z)
    {
        return x + y + z;
    }
    // this member function has different signature - but it can be used too
    // please note that argument order is changed too
    double foo_fn_4(int x, double z, float y, long xx)
    {
        return x + y + z + xx;
    }
};

// delegate class that holds as delegate as its params for future call
template <typename Ret, typename ...Args>
struct delayed_call
{
  // tuple can be used as FunctionParamsPack type
  typedef std::tuple<Args...> params_type;
  // std::function as delegate type
  typedef std::function<Ret(Args...)> function_type;

  // stored parameters
  params_type params;
  // stored delegate
  function_type func;

  // invocation
  Ret operator()()
  {
    return callFunc(typename gens<sizeof...(Args)>::type());
  }
  // direct invocation
  Ret operator()(Args... args)
  {
    return func(args...);
  }

  // internal invocation with tuple unrolling
  template<int ...S>
  double callFunc(seq<S...>)
  {
    return func(std::get<S>(params) ...);
  }
};

int main(void)
{
  // arguments
  std::tuple<int, float, double> t = std::make_tuple(1, 5, 10);
  // var #1 - you can use simple function as delegate
  delayed_call<double, int,float, double> saved_foo_fn{t, foo_fn};
  foo_struct fs;
  // var #2 - you can use member function as delegate
  delayed_call<double, int,float, double> saved_foo_fn_struct{t, std::bind(&foo_struct::foo_fn, fs, _1, _2, _3)};
  // var #3 - you can use member function with different signature as delegate. 
  // bind 0 to xx and change argument order
  delayed_call<double, int,float, double> saved_foo_fn_struct_4{t, std::bind(&foo_struct::foo_fn_4, fs, _1, _3, _2, 0l)};
  // var #4 - you can use lambda function as delegate
  delayed_call<double, int,float, double> saved_lambda{t, [](int x, float y, double z)
    {
        return x + y + z;
    }
  };
  cout << "saved_foo_fn: " << saved_foo_fn() << endl;
  cout << "saved_foo_fn_struct: " << saved_foo_fn_struct() << endl;
  cout << "saved_foo_fn_struct_4: " << saved_foo_fn_struct_4() << endl;
  cout << "saved_lambda: " << saved_lambda() << endl;
  cout << "direct call with (1,2,3) to a member: " << saved_foo_fn_struct(1, 2, 3) << endl;
}

输出:

saved_foo_fn: 16
saved_foo_fn_struct: 16
saved_foo_fn_struct_4: 16
saved_lambda: 16
direct call with (1,2,3) to a member: 6

Live demo

所以你不仅限于成员函数,还可以使用任何可调用类型甚至不同的签名

如果占位符::_1... 看起来很丑 - there is a solution

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-10-20
    • 2013-12-26
    • 2014-02-03
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多