在编写一些库绑定代码时,我尝试定义基于数组的类型,遇到了一个奇怪的挑战。
考虑如下的枚举:
enum class MyTypes
{
UInt,
Int,
Float
}
利用某些类型特征,我可以将枚举值转换为类型:
template <MyTypes Ty> struct typeify;
template <> struct typeify<MyTypes::UInt> { using type = unsigned int; };
template <> struct typeify<MyTypes::Int> { using type = int; };
template <> struct typeify<MyTypes::Float> { using type = float; };
template <MyTypes Ty> using typeify_t = typename typeify<Ty>::type;
为了帮助绑定,我还有这个(因为 C++ 没有反射):
inline static constexpr std::array KnownTypes
{
std::pair{"UInt", MyTypes::UInt},
std::pair{"Int", MyTypes::Int},
std::pair{"Float", MyTypes::Float}
};
从这里,我想std::variant
根据已知的类型定义一个。
有了 3 个值,显然只需像这样写出变量:
std::variant<unsigned int, int, float>
std::variant
但在我的情况中,我有 50 多种类型。因此,我希望有一种方法可以从数组KnownTypes
和类型特征中自动生成类型定义typeify_t
。
我认为我已经接近以下内容,但由于无法为可变模板参数提供默认值,我受到了困扰:
template <template <typename T, T ... Idx> class temp = std::make_index_sequence<KnownTypes.size()>>
using AnyKnownType = std::variant<typeify_t<KnownTypes[Idx].second>, ...>;
编程中的任何问题都可以通过另一个间接级别来解决(间接级别过多除外)。
不要直接创建
using
表达式模板,而是创建函数模板声明并在using
表达式中使用其返回类型:演示
使用部分专门化我建议:
据我所知,这是您可以轻松想到的选项。由于 C++ 关于别名模板的规则,您无法直接部分特化别名模板。别名模板不允许部分特化,这意味着您无法编写不使用辅助结构即可“解开”索引序列的别名。因此,无法从上述方法转换为您的方法。
((查看现场演示))
实际上,您为框架定义了 3 个不同的序列:
枚举
模板特化列表
将枚举值链接到字符串的数组
您至少可以减少到 2 个序列,如下所示:
保存枚举值的数组
模板特化列表。由于它们是相关的,因此最好将类型和相关字符串都放在此处
然后,您可以按照将 constexpr 数组的内容转换为参数包的可能性来构建变体(请参阅此处的通用方法),这导致:
#define
请注意,为了避免定义枚举和定义保存所有枚举值的数组的冗余,使用的方法并不那么漂亮(这是另一个故事,解决方案似乎存在)。c++20
还要注意,它将从(即在定义中使用类类型的非类型模板参数)开始运行Generator
。