C++ c++;元编程:为每个枚举类型成员创建typedef作为类型
我想知道是否有可能为元编程目的从enum类生成类型集。 我最初是一名C#程序员,习惯于使用大量属性进行反射和元编程。例如,对于我来说,用C#编写这样的代码片段是一种通用模式: 因此,现在我可以询问C++ c++;元编程:为每个枚举类型成员创建typedef作为类型,c++,metaprogramming,C++,Metaprogramming,我想知道是否有可能为元编程目的从enum类生成类型集。 我最初是一名C#程序员,习惯于使用大量属性进行反射和元编程。例如,对于我来说,用C#编写这样的代码片段是一种通用模式: 因此,现在我可以询问Components::getComponentUID(),它返回该组件的uid(取决于它作为模板参数的位置或该组件的constexpr值,这无关紧要)。但这是一种非常不方便的方法,我想知道是否可以在组件类中放置宏,或者使用属性和代码生成步骤或其他什么。换句话说,我的目标是以某种方式标记应该在组件集中的
Components::getComponentUID()
,它返回该组件的uid(取决于它作为模板参数的位置或该组件的constexpr值,这无关紧要)。但这是一种非常不方便的方法,我想知道是否可以在组件类中放置宏,或者使用属性和代码生成步骤或其他什么。换句话说,我的目标是以某种方式标记应该在组件集中的类,并在以后使用它。C++提供的目的是什么?
如果我可以像C#方式那样创建一个enum类,在那里列出所有组件,并在组件类(或enum类附近的某个地方)中编写一个constexpr值,那就没问题了,这两种方式都对我有好处。
我的意思是:
/* ComponentsEnum.h */
enum class ComponentsEnum { Comp1, Comp2, Comp3 };
// Here is some magic to generate Components<C1, C2, C3> metaclass.
/* another file */
#include "ComponentsEnum.h"
struct C1 { const ComponentsEnum MyValue = ComponentsEnum::Comp1; };
提前谢谢 下面是我的评论 您可以在C++17中执行类似的操作:
//在register.hpp中
int register_me();
//在register.cpp中
int寄存器_me(){
静态int id=0;
返回id++;
}
//在where.hpp中
//#包括“register.hpp”
结构组件{
内联静态int id=寄存器_me();
};
C++17之前版本要求将每个组件::id
的定义和初始化移动到.cpp
但我强烈建议不要使用这个。重新考虑一下您的设计,将类型转换为ID对我来说是一种代码味道。C++并不是真正设计来做这些事情的,它会在以后困扰你。
上面的代码依赖于程序开始时所有静态变量的动态初始化。顺序未指定,每次编译可能会导致分配不同的ID
绝对不要把它放到任何共享库中,在100%确定你知道编译、链接和加载过程是如何为你的工具链工作的,因为这些超出了C++标准的范围。
< P>谢谢@ JerryJeremiah链接和昆比的建议,我找到了解决方案。 所以,我被我的C#习惯误导了,这个想法很简单,但很棘手。 根据C泛型和C++模板的区别,泛型是运行时实例类型,而模板是编译时类型。因此,我不需要创建映射或处理整个代码库,我所需要的一切都将在编译时使用模板生成 解决方案本身:运行一些测试:
std::cout << C1::toString() << ": " << Components::getComponentUID<C1>() << std::endl;
std::cout << C2::toString() << ": " << Components::getComponentUID<C2>() << std::endl;
不幸的是,目前还没有通用的方法来获取所有已定义枚举常量的列表。反射不会早于C++23。但您试图做的并不是一个好主意-为类型分配一些项目范围的ID违背了单独的编译机制,您将很难保持所有内容的同步。有了足够的全局变量及其静态初始化,可能就可以完成了,但不能在编译时完成。@Quimby好的,那么不使用枚举,而是使用另一种机制怎么样?代码生成,属性标记,还是什么?只是外部变量初始化?我做了一个回答,因为它不适合注释。它有ID功能,但实际上,如果您想在任何严肃的项目中使用它,请先尝试找到其他功能,或者至少进行足够的研究,以真正了解代码中每个关键字的功能。以下是一个想法:
/* ComponentsEnum.h */
enum class ComponentsEnum { Comp1, Comp2, Comp3 };
// Here is some magic to generate Components<C1, C2, C3> metaclass.
/* another file */
#include "ComponentsEnum.h"
struct C1 { const ComponentsEnum MyValue = ComponentsEnum::Comp1; };
/* ComponentsEnum.h */
enum class ComponentsEnum { Comp1, Comp2, Comp3 };
// Here is all the magic
// All enum members concats into `Components<Comp1, Comp2, Comp3, ...>`
ConcatAll<ComponentsEnum>();
/* another file */
#include "ComponentsEnum.h"
struct Comp1 { };
/* ComponentsEnum.h */
enum class ComponentsEnum { Comp1, Comp2, Comp3 };
#define InitMeta(ComponentsEnumMember) /* Some Magic */
/* another file */
#include "ComponentsEnum.h"
struct Comp1 { InitMeta(ComponentsEnum::Comp1) };
enum class ComponentEnum
{
C1,
C2,
C3
};
struct Components
{
template<typename T>
static bool isComponent() { /* Some stuff here */ }
template<typename T>
static int getComponentUID() { /* Some stuff here */ }
};
template <typename T, ComponentEnum enumMember>
struct ComponentMeta
{
static constexpr bool isComponent = true;
static constexpr int uid = static_cast<int>(enumMember);
};
#define ComponentMetaMacro(type_name, enum_name) typedef ComponentMeta<type_name, ComponentEnum::enum_name> Meta; \
static const char* toString() { return #type_name; }
struct Components
{
template<typename T>
static bool isComponent() { return T::Meta::isComponent; }
template<typename T>
static int getComponentUID() { return T::Meta::uid; }
};
struct C1
{
ComponentMetaMacro(C1, C1)
};
struct C2
{
ComponentMetaMacro(C2, C2)
};
std::cout << C1::toString() << ": " << Components::getComponentUID<C1>() << std::endl;
std::cout << C2::toString() << ": " << Components::getComponentUID<C2>() << std::endl;