按名称调用不同签名的方法

时间:2015-03-09 23:02:20

标签: c++ c++11 c++14

我有一组使用std::function的委托,指向具有不同签名的函数。我希望能够在运行时使用字符串键检索这些委托。我似乎无法使用地图,因为它们指向具有不同签名的函数。没有switch语句我可以拥有这样的功能吗?

例如,真实世界的用例是RPC系统。他们真的只是让方法具有相同的签名或使用代码生成吗?

E :与所选答案相关的有用链接需要相当长的时间才能掌握

http://en.cppreference.com/w/cpp/utility/functional/function

http://en.cppreference.com/w/cpp/utility/forward

http://en.cppreference.com/w/cpp/utility/integer_sequence

http://en.cppreference.com/w/cpp/types/result_of

http://en.cppreference.com/w/cpp/types/remove_reference

http://www.boost.org/doc/libs/1_57_0/doc/html/any.html

2 个答案:

答案 0 :(得分:4)

从类型包开始:

template<class...Ts>struct types{
  using type=types;
  enum{count = sizeof...(Ts)};
};
template<class T> struct tag{using type=T;};

现在,我们在一个全局types捆绑包中定义所有受支持的类型。

该全局types包的索引通过网络发送,用于查找反序列化代码。

在协议命名空间(对于基本类型和boost::any read( wire_data, T* unused )类型)和std(对于其他类型)的命名空间中应该定义一个函数T,它将有线数据读入一个boost::anywire_data只是一个占位符,用于您从T转变为电汇的任何内容。

我们通过魔术开关技术将类型索引转换为对read的调用:

template<size_t n> using index=std::integral_constant<size_t, n>;

template<class types, class T>
struct index_in;
template<class...Ts, class T>
struct index_in<types<T, Ts...>, T>:index<0> {};
template<class T0, class...Ts, class T1>
struct index_in<types<T0, Ts...>, T1>:index<
  index_in<types<Ts...>, T1>::value+1
> {};

为我们提供了Ttypes<Ts...>类型的偏移量。在发送方使用它将您的类型映射到列表中的索引。

另一方面,我们有:

template<class types, size_t n>
struct type_at;
template<class types, size_t n>
using type_at_t=typename type_at<types,n>::type;
template<class T0, class...Ts>
struct type_at<types<T0, Ts...>,0>: tag<T0> {};
template<class T0, class...Ts, size_t n>
struct type_at<types<T0, Ts...>,n>:
  type_at<types<Ts...>, n-1>
{};

采用types<Ts...>和索引并返回类型。

template<class types>
struct to_any {
  template<size_t n>
  struct worker {
    boost::any operator()( wire_data w )const{
      using protocol_ns::read;
      return read( w, (type_at_t<types,n>*)nullptr );
    }
  };
};

使用ADL调度到read

现在我们编写快速魔术开关:

namespace details {
  template<template<size_t>class action, class indexes>
  struct magic_switch;
  template<template<size_t>class action, size_t... Is>
  struct magic_switch<action, std::index_sequences<Is...>>
  {
    template<class...Ts, class R=std::result_of_t< action<max>(Ts...) >>
    R operator()(size_t i, Ts&&... ts)const {
      using entry = R(*)(std::remove_reference<Ts>*...);
      entry table[] = {
        [](std::remove_reference<Ts>*...args)->R{
          return action<Is>{}( std::forward<Ts>(*args)... );
        }...
      };
      if (i > sizeof(table)/sizeof(entry))
        throw std::out_of_range("i");
      return table[i]( (&ts)... );
    }
  };
}
template<template<size_t>class action, size_t max>
struct magic_switch:
  details::magic_switch<action,std::make_index_sequence<max>>
{};

然后

magic_switch<
  to_any<all_types_supported>::template worker, 
  all_types_supported::count
>

是无状态函数对象的类型,当传递nwire_data时,将调用该类型的相应read函数并返回boost::any。< / p>

好的,现在我们已到了一半。

下半部分涉及我们的签名Z(Args...)功能,并编写一个类型橡皮擦,其中std::vector<boost::any>存储Args...并返回boost::any存储{{1} }}

Z

一旦我们写完了,我们就可以为我们的函数表存储从字符串到std::function<boost::any(std::vector<boost::any>)> erased_func_t; template<class... Args, class F> erased_func_t erase_func(F&& f) { // TODO } 的映射。

我们查找erased_func_t。我们使用上述反序列化基础结构从传入的参数生成erased_func_t。我们调用它,如果失败则抛出异常。

鲍勃是你的叔叔。

如果您想要发送回复,则需要键入 - 删除回到有线格式,然后更改std::vector<boost::any>以返回通过线路将其发送回来所需的erased_func_t而不是wire_data。那可能是最好的。

以上代码均未经过测试。其中一些需要C ++ 14(不是那么多,大多数是boost::any别名),而一些声称支持C ++ 11的编译器不支持我写的_t实现(它几乎是纯粹的C ++ 11,除了magic_switch别名,我相信。但是,如果更详细,可以写出一个等效的。

最后,像许多事情一样,从头开始编写RPC协议通常不是一个好主意。赔率是我错过了重要的一步。

答案 1 :(得分:2)

查找表和std::map的问题是所有函数指针必须具有相同的签名。

然而,有一个复杂的解决方案。它涉及函数对象(仿函数)和基类。

让我们定义基础仿函数类:

struct Base_Parameters;

struct Base_Functor
{
  virtual void execute(Base_Parameter * p_parameters) = 0;
};

这解决了如何声明std::map

的问题
typedef std::map<std::string, Base_Functor *> Function_Key_Container;

任何函数都将从基函数结构派生: struct My_Functor:Base_Functor {   void execute(Base_Parameters * p_parameters)   {     // ...   } };

您可以将该功能添加到地图中,如下所示:

Function_Key_Container function_map;
function_map["Apple"] = new My_Functor;

现在的问题是将参数传递给函数 我还没有完全解决这个问题,但这里有一些建议:

  • 使用boost :: variant - 变体记录。
  • Base_Parameters创建一个子项,并在函数对象中使用 dynamic_castBase_Parameters指针转换为指针 给孩子参数。