C++ 按名称调用不同签名的方法
我有一组使用C++ 按名称调用不同签名的方法,c++,c++11,c++14,C++,C++11,C++14,我有一组使用std::function的委托,这些委托指向具有不同签名的函数。我希望能够在运行时使用字符串键检索这些委托。我似乎不能使用映射,因为它们指向具有不同签名的函数。如果没有switch语句,我可以拥有这样的功能吗 例如,现实世界的用例是RPC系统。他们真的只是让方法具有相同的签名还是使用代码生成 E:与所选答案相关的有用链接,需要相当长的时间才能掌握 查找表和std::map的问题是所有函数指针必须具有相同的签名 然而,围绕这一点有一个复杂的解决方案。它涉及函数对象(函子)和
std::function
的委托,这些委托指向具有不同签名的函数。我希望能够在运行时使用字符串键检索这些委托。我似乎不能使用映射,因为它们指向具有不同签名的函数。如果没有switch语句,我可以拥有这样的功能吗
例如,现实世界的用例是RPC系统。他们真的只是让方法具有相同的签名还是使用代码生成
E:与所选答案相关的有用链接,需要相当长的时间才能掌握
查找表和
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;
现在的问题是将参数传递给函数。我还没有完全解决这个问题,但这里有一些建议:
- 使用boost::variant——一个变量记录
- 从
Base\u参数创建一个子项,并在函数对象中使用
将dynamic_cast
指针转换为指针 添加到子参数Base_参数
template<class...Ts>struct types{
using type=types;
enum{count = sizeof...(Ts)};
};
template<class T> struct tag{using type=T;};
为我们提供types
中类型T
的偏移量。在发送端使用它将您的类型映射到列表中的索引
另一方面,我们有:
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>
{};
然后
一旦我们写好了,我们就可以为我们的函数表存储一个从字符串到一个erased\u func\t
的映射
我们查找erased\u func\t
。我们使用上述反序列化基础设施从传入的参数生成std::vector
。我们调用它,如果失败则抛出异常
鲍勃是你叔叔
如果你想发回答案,你需要输入erase返回到wire格式,并更改erased_func_t
以返回通过wire发回所需的wire_数据,而不是boost::any
。那可能是最好的
以上代码都没有经过测试。有些编译器需要C++14(没有那么多,大部分是\u t
别名),有些声称支持C++11的编译器不支持我编写的magic\u switch
实现(我相信除了\u t
别名之外,它几乎是纯C++11)。但如果更详细的话,也可以编写一个等效的
最后,像许多事情一样,从头开始编写RPC协议通常不是一个好主意。我错过了上面的一个重要步骤。< /p>你如何调用从地图中检索的函数?C++是静态类型的,不能调用不知道签名的函数。您可能会尝试使用boost::any
,但我认为您的RPC可能需要一个通用的签名用于所有函数。@但我的意思是:您将如何调用调用?因为您问过:一个典型的RPC实现(例如DCE/RPC或Microsoft DCOM)会让您使用一种特殊的语言描述您的外部接口(接口定义语言,IDL),然后由专用工具(IDL编译器)处理。此工具生成代理和存根函数-具有正确签名的函数,用于处理其参数的序列化和反序列化。客户端调用代理,代理神奇地调用另一端的存根,存根反过来调用服务器的实现。您不能将std::array
,而Param
作为模板。Y对于某些具体类型,您只能使用std::array
,我会寻找一种“后期绑定”的方法函数对象实例的参数,但除此之外,这正是我建议的方法。std::index_序列
到std::index_序列
回答得很好!在第五个代码片段中,什么是max
?@quen它构建了一个用于高效切换的跳转表:max
是有效的i
值(转换为编译时常数)是半开区间[0,max)
。在下一个代码段中,我们将max
设置为类型包中的类型数。
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
> {};
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>
{};
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 );
}
};
};
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
>
std::function<boost::any(std::vector<boost::any>)> erased_func_t;
template<class... Args, class F>
erased_func_t erase_func(F&& f) {
// TODO
}