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

FPG*_*PGA 5 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

Yak*_*ont 4

从类型包开始:

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

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

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

应该boost::any read( wire_data, T* unused )在您的协议命名空间(对于基本类型和std类型)和(对于其他类型)的命名空间中定义一个函数T,该函数将线数据读取到boost::any. wire_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
> {};
Run Code Online (Sandbox Code Playgroud)

给我们一个类型的偏移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>
{};
Run Code Online (Sandbox Code Playgroud)

它接受 atypes<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 );
    }
  };
};
Run Code Online (Sandbox Code Playgroud)

read使用 ADL 进行调度。

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

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>>
{};
Run Code Online (Sandbox Code Playgroud)

然后

magic_switch<
  to_any<all_types_supported>::template worker, 
  all_types_supported::count
>
Run Code Online (Sandbox Code Playgroud)

是无状态函数对象的类型,当传递n和 时wire_data,将调用该类型的适当read函数并返回boost::any.

好的,现在我们已经成功了一半。

后半部分涉及采用我们的签名函数Z(Args...),并编写一个类型擦除器,该擦除器接受std::vector<boost::any>存储Args...并返回boost::any存储 a 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
}
Run Code Online (Sandbox Code Playgroud)

一旦我们写好了,我们就可以存储一个从字符串到erased_func_t函数表的映射。

我们查找erased_func_t. std::vector<boost::any>我们使用上面的反序列化基础设施从传入的参数生成 a 。我们调用它,如果失败则抛出异常。

鲍勃是你叔叔。

如果您想将答案发回,则需要通过键入擦除返回到有线格式,并更改erased_func_twire_data返回通过有线方式发回所需的格式,而不是boost::any. 这可能是最好的。

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

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