在编译时用模板填充运行时数据
Fill runtime data at compile time with templates
我有一个特殊的情况,我想在编译时准备一些运行时结构,而不需要复制代码。
我有两个结构体,我用它们在编译时为我编写的编译器注册一些类型:
using TypeID = u8;
template<typename T, typename TYPE_ID, TYPE_ID I>
struct TypeHelper
{
static constexpr TYPE_ID value = std::integral_constant<TYPE_ID, I>::value;
};
template<typename T> struct Type : TypeHelper<T, u8, __COUNTER__> { static_assert(!std::is_same<T,T>::value, "Must specialize for type!"); };
这些是在配置头中与一个宏一起使用的,该宏针对我需要的多种类型特殊化Type<T>
:
using type_size = unsigned char;
#define GET_NTH_MACRO(_1,_2,_3, NAME,...) NAME
#define REGISTER_TYPE(...) GET_NTH_MACRO(__VA_ARGS__, REGISTER_TYPE3, REGISTER_TYPE2, REGISTER_TYPE1)(__VA_ARGS__)
#define REGISTER_TYPE1(_TYPE_) REGISTER_TYPE2(_TYPE_, _TYPE_)
#define REGISTER_TYPE2(_TYPE_,_NAME_)
constexpr TypeID TYPE_##_NAME_ = __COUNTER__;
template<> struct Type<_TYPE_> : TypeHelper<_TYPE_, type_size, TYPE_##_NAME_> {
static constexpr const char* name = #_NAME_;
};
REGISTER_TYPE(void)
REGISTER_TYPE(s64)
REGISTER_TYPE(s32)
让这些展开为
constexpr TypeID TYPE_void = 2;
template<> struct Type<void> : TypeHelper<void, type_size, TYPE_void> { static constexpr const char* name = "void"; };
constexpr TypeID TYPE_s64 = 3;
template<> struct Type<s64> : TypeHelper<s64, type_size, TYPE_s64> { static constexpr const char* name = "s64"; };
constexpr TypeID TYPE_s32 = 4;
template<> struct Type<s32> : TypeHelper<s32, type_size, TYPE_s32> { static constexpr const char* name = "s32"; };
这很好,但是编译器也需要一些关于这些类型的运行时信息,所以除了这些,我还必须定义一些辅助函数,比如
static TypeID typeForIdent(const std::string& name);
static const char* nameForType(TypeID type);
static void mapTypeName(TypeID type, const std::string& name);
inline bool isSigned(TypeID type)
{
return type == Type<s8>::value || type == Type<s16>::value ||
type == Type<s32>::value || type == Type<s64>::value;
}
和类似的函数
这些函数必须在没有模板参数的情况下工作,因此TypeID
必须是一个普通参数。但是我需要在代码的单独部分初始化这些数据,例如:
mapTypeName(Type<s32>::value, "s32");
使用静态std::unordered_map<TypeID, std::string>
。当然,这也意味着,当大多数信息在编译时通过类型定义已经可用时,我必须维护两倍的代码。
我想知道是否有一些晦涩的技巧,我错过了可以合并这些,以便REGISTER_TYPE
宏也注册运行时信息。我还没有准备好,但是也许有一个聪明的方法来处理这个
如果您不是特别关心将运行时数据注册到映射中的性能,您可以简单地使用inline
函数返回对static
映射实例的引用,并在REGISTER_TYPE
宏中生成"dummy" registrar
实例,这些实例在其构造函数中填充映射。
inline auto& registration_map()
{
static std::unordered_map<int, std::string> m;
return m;
}
struct registrar
{
registrar(int id, std::string s)
{
registration_map()[id] = std::move(s);
}
};
template <typename T>
struct TypeHelper { };
#define CAT3_IMPL(a, b, c) a ## b ## c
#define CAT3(a, b, c) CAT3_IMPL(a, b, c)
#define REGISTER_TYPE(id, type)
template<> struct TypeHelper<type> { };
[[maybe_unused]] registrar CAT3(unused_registrar_, __LINE__, type) {id, #type};
REGISTER_TYPE(0, int)
REGISTER_TYPE(1, float)
REGISTER_TYPE(2, double)
int main()
{
assert(registration_map()[0] == "int");
assert(registration_map()[1] == "float");
assert(registration_map()[2] == "double");
}
关于wandbox的完整示例。
指出:
如果相同的
REGISTER_TYPE
包含在多个翻译单元中,您可能会得到重复注册。CAT3(unused_registrar_, __LINE__, type)
用于生成一个唯一的名称,不会与其他REGISTER_TYPE
扩展冲突。
我看到你使用c++扩展__COUNTER__
,因此你可能会发现有趣的字符串文字gcc和clang扩展,这将允许你绑定字符串文字表示注册类型直接到你的Type
(不需要额外的标识号):
#include <iostream>
template <class Char, Char... Cs>
struct string_literal {
static Char str[sizeof...(Cs) + 1];
};
template <class Char, Char... Cs>
Char string_literal<Char, Cs...>::str[sizeof...(Cs) + 1] = { Cs..., ' ' };
template <class Char, Char... Cs>
constexpr string_literal<Char, Cs...> operator ""_sl() {
return {};
}
template <class T, class SL>
struct TypeHelper { };
template <class T>
struct Type;
template <class A, class B>
auto getType(TypeHelper<A, B>) {
return B{};
}
#define REGISTER(TYPE) template <>
struct Type<TYPE>: TypeHelper<TYPE, decltype(#TYPE##_sl)> { };
struct X{};
REGISTER(void)
REGISTER(int)
REGISTER(X)
int main(){
std::cout << decltype(getType(Type<void>{}))::str << std::endl;
}
输出:(现场演示)空白
- CMake-按正确顺序将项目与C运行时对象文件链接
- 我在c++代码中生成了一个运行时#3异常
- 为什么在运行时没有向我们提供有关分段错误的更多信息?
- 删除指向指针的指针是运行时错误吗
- 如何用参数值调用函数(仅在运行时已知)
- 为什么即使使用-cudart-static进行编译,库用户仍然需要链接到cuda运行时
- 是否可以在编译时初始化数组,以便在运行时不会花费时间?
- c++中的指针和运行时错误
- 在运行时处理类型擦除的数据-如何不重新发明轮子
- 有没有一种方法可以测量c++程序的运行时内存使用情况
- 建议在运行时将带有类实例的列表从c++导入qml
- 无法理解此 return 语句的功能,没有它就会发生运行时错误
- 如何在GTK程序运行时禁用屏幕保护程序/电源管理/屏幕消隐
- 在同一模拟中使用静脉和静脉_ inet内容时出现运行时错误
- 在运行时 c++ 更改用类对象填充的数组的大小
- 在运行时开始时填充两个大向量
- 在运行时填充多维矢量
- 如何在运行时填充Boost :: Fusion ::向量
- 在编译时用模板填充运行时数据
- 有没有更好的方法可以通过模板用预先计算的值填充数组(用于运行时)