简体   繁体   English

如何在C ++中将运行时类型鉴别器映射到模板实例(无需手动枚举它们)?

[英]How in C++ to map run-time type discriminators to template instances (without manually enumerating them all)?

Given an enum of type discriminators representing various small types: 鉴于代表各种小类型的类型鉴别器的enum

enum TypesEnum {
    IntT, 
    DoubleT, 
    ShortStringT
};

Suppose I have a template SomeType<typename A, typename B, typename C> . 假设我有一个template SomeType<typename A, typename B, typename C> This is a type that I read and write to sets of memory-mapped files, whose layout/stride is determined by the types; 这是我读取和写入内存映射文件集的类型,其布局/跨度由类型决定; the runtime types are stored as a triplet of the enum discriminators above. 运行时类型存储为上面的枚举鉴别符的三元组。

I need to write various tools which can load these files and perform operations on them, like SomeType<A,B,C> => SomeType<B,A,C> and so forth. 我需要编写各种工具来加载这些文件并对它们执行操作,例如SomeType<A,B,C> => SomeType<B,A,C>等等。 In these tools, I therefore have a rather awkward layer which translates the type discriminators on-disk into generic lambdas wrapping operations implemented with template instances for the correct types. 因此,在这些工具中,我有一个相当尴尬的层,它将磁盘上的类型鉴别器转换为使用模板实例为正确类型实现的通用lambdas包装操作。

This looks like: 这看起来像:

static std::map< std::tuple<Discrim, Discrim, Discrim>, some_op_fn_t > = 
    {
        {std::make_tuple(IntT, DoubleT, ShortStringT), SomeOperation<int,double,char[16]>() },
         std::make_tuple(IntT, IntT, ShortStringT), SomeOperation<int,int,char[16]>() },
        ...
    };
... look up the correct function pointer and call it with the path to the files ...

where typedef std::function<bool(void)> some_op_fn_t , whose implementations in template<A,B,C> class SomeOperation do a bunch of work with side-effects to disk. 其中typedef std::function<bool(void)> some_op_fn_t ,其在template<A,B,C> class SomeOperation对磁盘的副作用typedef std::function<bool(void)> some_op_fn_t工作。

Now, this quickly gets pretty tedious as the list of types and number of different operations grow. 现在,随着类型列表和不同操作数量的增长,这很快变得相当繁琐。 The trick is I cannot use virtual inheritance to simply have a type-erased SomeType which operates on abstract/virtual value types; 诀窍是我不能使用虚拟继承来简单地使用类型擦除的SomeType来操作抽象/虚拟值类型; it's much too slow to do the indirection and pointer chasing. 做间接和指针追逐太慢了。 I need to have dead reckoning and contiguous, packed value data, suitable (in the case of floats and doubles) for passing directly to BLAS. 我需要有航位推算和连续的,打包的值数据,适合(在浮点数和双打的情况下)直接传递给BLAS。

Are there any techniques for automating the creation of interfaces/layers such as this? 有没有任何技术可以自动创建这样的接口/层? Something like a type-level combinations would help, where I could connect the enum to the types just once, and then expand all the instances of the mapping. 类似于类型级别的组合会有所帮助,我可以将枚举连接到类型一次,然后展开映射的所有实例。 Is that possible? 那可能吗?

Worst case I can write a script to generate the code, but ugh... 最糟糕的情况我可以写一个脚本来生成代码,但是呃......

First, an easy part, a mapping between type and enum value: 首先,一个简单的部分,类型和枚举值之间的映射:

template <typename T> struct EnumValue;

template <> struct EnumValue<int> : std::integral_constant<TypesEnum, IntT> {};
template <> struct EnumValue<double> : std::integral_constant<TypesEnum, DoubleT> {};
template <> struct EnumValue<char[16]> : std::integral_constant<TypesEnum, ShortStringT> {};

Then an easy helper function: 然后一个简单的帮手功能:

using TupleT = std::tuple<int, double, char[16]>;

template <typename ... Ts> // Might be T1, T2, T3, but lazy to type
constexpr auto make_my_pair()
{
    return std::make_pair(std::make_tuple(EnumValue<Ts>::value...), &SomeOperation<Ts...>);
}

Now the Cartesian product using index_sequence 现在使用index_sequence的笛卡尔积

template <std::size_t I>
constexpr std::pair<TypesEnumTuple, some_op_fn_t>
make_my_pair()
{
    constexpr std::size_t N = std::tuple_size<TupleT>();
    return make_my_pair<
        std::tuple_element_t<(I / (N * N)) % N, TupleT>,
        std::tuple_element_t<(I / N) % N, TupleT>,
        std::tuple_element_t<(I / 1) % N, TupleT>
    >();
}

template <std::size_t ... Is>
std::map<TypesEnumTuple, some_op_fn_t>
make_my_map(std::index_sequence<Is...>)
{
    return {make_my_pair<Is>()...};
}

And finally: 最后:

// 27 = std::tuple_size<TupleT>() * std::tuple_size<TupleT>() * std::tuple_size<TupleT>()
// as we have T1, T2, T3
static const std::map<TypesEnumTuple, some_op_fn_t> m =
    make_my_map(std::make_index_sequence<27>());

Demo 演示

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM