C++ c++;元编程:为每个枚举类型成员创建typedef作为类型

C++ c++;元编程:为每个枚举类型成员创建typedef作为类型,c++,metaprogramming,C++,Metaprogramming,我想知道是否有可能为元编程目的从enum类生成类型集。 我最初是一名C#程序员,习惯于使用大量属性进行反射和元编程。例如,对于我来说,用C#编写这样的代码片段是一种通用模式: 因此,现在我可以询问Components::getComponentUID(),它返回该组件的uid(取决于它作为模板参数的位置或该组件的constexpr值,这无关紧要)。但这是一种非常不方便的方法,我想知道是否可以在组件类中放置宏,或者使用属性和代码生成步骤或其他什么。换句话说,我的目标是以某种方式标记应该在组件集中的

我想知道是否有可能为元编程目的从enum类生成类型集。 我最初是一名C#程序员,习惯于使用大量属性进行反射和元编程。例如,对于我来说,用C#编写这样的代码片段是一种通用模式:

因此,现在我可以询问
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++模板的区别,泛型是运行时实例类型,而模板是编译时类型。因此,我不需要创建映射或处理整个代码库,我所需要的一切都将在编译时使用模板生成

解决方案本身:

  • 我想要一个枚举为我的组件生成连续的uid号。因此,定义它:
  • 我想要一个简单的接口,让我的组件请求元信息。也要定义它:

  • 运行一些测试:

        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;