任务很简单:我们有一个类型列表(即using list = std::tuple<A, B, C, ...>;),我们希望根据运行时已知的索引调度其内容.通过"dispatch"我的意思是调用一个模板化的处理程序,由该列表中的类型参数化.switch手动编写很容易:
template <class... Args>
auto dispatch(size_t i, Args&& ...args)
{
switch (i) {
case 0: return handler<typename std::tuple_element<0, list>::type>(std::forward<Args>(args)...);
...
}
}
Run Code Online (Sandbox Code Playgroud)
但这很乏味,容易出错,而且很无聊.我正在寻找一种方法来强制编译器从更紧凑和简洁的定义中做到这一点.
我想要获得与手动开关相同或非常接近的结果.因此,生成函数指针表(如此处)并不是一个理想的解决方案(我不想为处理程序完全无法使用的情况引入函数调用).
到目前为止,我发现了两种方法:
无法递归(感谢Horstling)
template <size_t N>
struct dispatch_helper
{
template <class... Args>
static R dispatch(size_t i, Args&& ...args)
{
if (N == i)
return handler<typename std::tuple_element<N, list>::type>(std::forward<Args>(args)...);
return dispatch_helper<N+1>::dispatch(i, std::forward<Args>(args)...);
}
};
template <>
struct dispatch_helper<200>
{
template <class... Args>
static R dispatch(size_t …Run Code Online (Sandbox Code Playgroud)我正在研究在现代C++(C++ 11/C++ 14)中动态调度不相关类型的可能实现.
通过"动态分派类型",我的意思是在运行时我们需要通过其整数索引从列表中选择一个类型并对其执行某些操作(调用静态方法,使用类型特征等).
例如,考虑序列化数据流:有几种数据值,它们以不同方式序列化/反序列化; 有几个编解码器,它们进行序列化/反序列化; 我们的代码从流中读取类型标记,然后决定它应该调用哪个编解码器来读取完整值.
我对许多操作感兴趣,这些操作可以在类型上调用(几个静态方法,类型特征......),并且可以是从逻辑类型到C++类的不同映射,而不仅仅是1:1(在使用序列化的例子意味着可能存在由同一编解码器序列化的多种数据类型.
我还希望避免手动代码重复,并使代码更易于维护,并且不易出错.表现也很重要.
目前我正在看到那些可能的实现,我错过了什么?这可以做得更好吗?
使用switch-case手动编写尽可能多的函数,因为类型上可能有操作调用.
size_t serialize(const Any & any, char * data)
{
switch (any.type) {
case Any::Type::INTEGER:
return IntegerCodec::serialize(any.value, data);
...
}
}
Any deserialize(const char * data, size_t size)
{
Any::Type type = deserialize_type(data, size);
switch (type) {
case Any::Type::INTEGER:
return IntegerCodec::deserialize(data, size);
...
}
}
bool is_trivially_serializable(const Any & any)
{
switch (any.type) {
case Any::Type::INTEGER:
return traits::is_trivially_serializable<IntegerCodec>::value;
...
}
}
Run Code Online (Sandbox Code Playgroud)优点:简单易懂; 编译器可以内联调度方法.
缺点:它需要大量的手动重复(或通过外部工具生成代码).
像这样创建调度表
class AnyDispatcher
{ …Run Code Online (Sandbox Code Playgroud)我想将一个可变参数类型列表"实现"到相关值的initializer_list中.例如,有std::tuple几个std::integral_constant<T, x>得到一个std::initializer_list<T>{...}.一般情况下,我想得到一些复杂类型的initializer_list,比如std::string.
但是下面这个简单的例子让我在Clang编译时崩溃了(虽然它适用于GCC,至少在Coliru上),所以我怀疑UB(或Clang中的bug):
template <class... Ts>
std::initializer_list<const std::string> materialize()
{
return {
std::to_string(Ts::value)...
};
}
void print_out()
{
for (const auto & x : materialize<std::true_type, std::false_type>()) {
std::cout << x << "\n";
}
}
Run Code Online (Sandbox Code Playgroud)
那么,这样的代码合法吗?在C++ 11/14/17中?