65.9K
CodeProject 正在变化。 阅读更多。
Home

改进 C++ 枚举:添加序列化、继承和迭代

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.05/5 (13投票s)

2008 年 12 月 24 日

CPOL

5分钟阅读

viewsIcon

80505

downloadIcon

309

一种不同的 C++ 枚举方法:枚举到字符串、枚举扩展和枚举迭代。

引言

我一直在寻找一种易于序列化的枚举类型(例如,写入和读取文件)。我不想关心与 enum 关联的内部整数编号,也不想关心这些编号的重叠问题。我只想关心枚举项的名称。我还希望有一种简单的方法来扩展 enum 定义,以处理多态类。我希望能够列出错误而不使用数字,我还希望能够列出 string ID 而不使用数字。这就是为什么一开始我并不关心迭代或为每个项分配特定值的原因。最后,我也决定实现这些可能性,因为也许有人会觉得它们有用。

背景

在我决定实现自己的解决方案之前,我读过几篇关于 enums 的有趣文章。

Using the Code

我的方法关键在于将枚举项列表与枚举类型定义本身分开。所以,您需要做的第一件事是创建一个包含您想要枚举的项的列表文件。例如,声明 "DayEnum.h"

// In DayEnum.h
ENUMITEM(Sunday)
ENUMITEM(Monday)
ENUMITEM(Tuesday)
ENUMITEM(Wednesday)
ENUMITEM(Thursday)
ENUMITEM(Friday)
ENUMITEM(Saturday)

ENUMITEM() 以几种不同的方式实现,以完成繁重的工作。也可以使用 ENUMITEM_VALUE(,) 为您想要的项分配特定值,但不推荐这样做,因为这可能会与 enum 扩展/继承产生冲突。我建议尽可能只使用 ENUMITEM()

以下示例代码将声明 enum 类型以及将 enum 转换为 string,将 string 转换为 enum,进行迭代和计数项的函数。所有这些都将在一个命名空间(如第一个背景文章中推荐的)或一个 static 类(从本代码的 5.0 版本开始,它允许子类定义)中定义(并封装),这样我们就避免了与其他 enum 定义的所有冲突。

// Declaration of the enumeration:
/////////////////////////////////////////////////////
// Input parameters:
// IMPROVED_ENUM_NAME - the name of the enumeration
// IMPROVED_ENUM_FILE - the file with the enum items
// DefineImprovedEnum:
// Batch file of preprocessing commands (uses the input parameters above)
/////////////////////////////////////////////////////
#define IMPROVED_ENUM_NAME  Day
#define IMPROVED_ENUM_FILE "DayEnum.h"
#include "DefineImprovedEnum.h"

有些作者会选择写 ??=include "DefineImprovedEnum.h" 来区分包含文件的不同用法。"??= 符号是 # 的三字符组",但并非所有编译器都识别三字符组。

作为一项新功能(从本代码的 4.0 版本开始),以上所有代码也可以在同一位置定义(无需单独的文件)。

// Inline declaration of the enumeration:
#define IMPROVED_ENUM_NAME  Day
#define IMPROVED_ENUM_LIST  ENUMITEM(Sunday)    \
                            ENUMITEM(Monday)    \
                            ENUMITEM(Tuesday)   \
                            ENUMITEM(Wednesday) \
                            ENUMITEM(Thursday)  \
                            ENUMITEM(Friday)    \
                            ENUMITEM(Saturday)
#include "DefineImprovedEnum.h"

使用枚举就像这样简单

void Test()
{
    // Conversion to string and conversion to enum:
    Day::EnumType t = Day::Monday;
    std::string text = Day::Enum2String(t);
    t = Day::String2Enum("Friday");

    // Iteration:
    t = Day::FirstEnumItem();
    t = Day::NextEnumItem(t);
    t = Day::LastEnumItem();
    t = Day::PreviousEnumItem(t);

    // Count:
    int n = Day::NumberOfValidEnumItem();
}

Test() 的末尾,t 的值为 Fridaytext 的值为 "Monday"。

如何扩展枚举定义(继承自另一个枚举)

使用此代码解决 继承 C++ 枚举类型 问题的另一种方法是执行以下操作:

// In Fruit.h
ENUMITEM(Orange)
ENUMITEM(Mango)
ENUMITEM(Banana)

// In NewFruit.h
ENUMITEM(Apple)
ENUMITEM(Pear)

然后,由于我们已将项目列表分开,因此我们可以创建一个包含所有项目的新列表。

// In MyFruit.h
#include "Fruit.h"
#include "NewFruit.h"

并声明新的 enum 类型

// Declaration of the extended enumeration:
#define IMPROVED_ENUM_NAME  MyFruit
#define IMPROVED_ENUM_FILE "MyFruit.h"
#include "DefineImprovedEnum.h"

这种方法效果很好,但我们没有简单的方法将基 enum 类型转换为扩展的 enum 类型。因此,我决定直接实现一些继承函数来扩展功能。以下示例代码将声明基 enum 和扩展的 enum,以及扩展的功能。

// Declaration of the base enum (Fruit):
#define IMPROVED_ENUM_NAME  Fruit
#define IMPROVED_ENUM_FILE "Fruit.h"
#include "DefineImprovedEnum.h"

// Declaration of the extended enum
// (MyFruit inherits from Fruit, extended with NewFruit):
#define IMPROVED_ENUM_NAME  MyFruit
#define IMPROVED_ENUM_FILE "NewFruit.h"
#define IMPROVED_ENUM_INHERITED_NAME  Fruit
#define IMPROVED_ENUM_INHERITED_FILE "Fruit.h"
#include "DefineImprovedEnum.h"

每个 enum 定义都有自己的命名空间,没有重叠问题。DefineImprovedEnum 批处理文件定义了从一个命名空间中的项转换为另一个命名空间中的项的函数。使用扩展枚举就像这样简单

// Accepts only the base type
void eat(Fruit::EnumType fruit) {};
// Accepts only the extended type
void consume(MyFruit::EnumType myfruit) {};

void ExtendedTest()
{
    // Declarations:
    Fruit::EnumType fruitAux, fruit;
    MyFruit::EnumType myfruitAux, myfruit, newfruit;

    // Direct assignments:
    fruit    = Fruit::Orange; // OK
    myfruit  = MyFruit::Orange; // OK
    newfruit = MyFruit::Apple; // OK

    // Conversions to extended enum:
    myfruitAux = MyFruit::Inherited2Enum(fruit); // OK
    myfruitAux = MyFruit::Inherited2Enum(myfruit); // OK
    myfruitAux = MyFruit::Inherited2Enum(newfruit); // OK

    // Conversions to base enum:
    fruitAux = MyFruit::Enum2Inherited(fruit); // OK
    fruitAux = MyFruit::Enum2Inherited(myfruit); // OK
    fruitAux = MyFruit::Enum2Inherited(newfruit); // returns NotValidEnumItem

    // The following compiles:
    eat(fruit); // OK
    eat(MyFruit::Enum2Inherited(myfruitAux)); // Possible NotValidEnumItem
    consume(myfruit); // OK
    consume(MyFruit::Inherited2Enum(fruit)); // OK

    // Partial iteration:
    myfruitAux = MyFruit::FirstExtendedEnumItem();
    myfruitAux = MyFruit::NextInheritedEnumItem(newfruit);
    myfruitAux = MyFruit::LastInheritedEnumItem();
    myfruitAux = MyFruit::PreviousExtendedEnumItem(newfruit);

    // Partial count:
    int n = MyFruit::NumberOfInheritedValidEnumItem();
    int m = MyFruit::NumberOfExtendedValidEnumItem();
}

实现

实现基于一个名为 "DefineImprovedEnum.h" 的预处理命令批处理文件。

// In DefineImprovedEnum.h

////////////////////////////////////////////////////////////////////////
// IMPORTANT NOTE:
// This is a "batch file of preprocessing directives"
// (because this cannot be done with a macro).
// Each time you include this file you are calling a batch file,
// it doesn't work as a macro include.
// If you want to declare several different enum types,
// you have to include this file several times.
// Do not use "#pragma once" directive, because it would have
// unexpected behaviour and results.
// Do not use directives like:
// #ifndef _IMPROVED_ENUM_H_ ; #define _IMPROVED_ENUM_H_ (same reason).
////////////////////////////////////////////////////////////////////////
// AUTHOR:      Hugo González Castro
// TITLE:       Improving C++ Enum: Adding Serialization,
//                                  Inheritance and Iteration.
// DESCRIPTION: A different approach to C++ enums: enum to string,
//              enum extension and enum iteration.
// VERSION:     v5.0 - 2009/04/13
// LICENSE:     CPOL (Code Project Open License).
//              Please, do not remove nor modify this header.
// URL:         ImprovedEnum.aspx
////////////////////////////////////////////////////////////////////////
// INPUT PARAMETERS:
// This file needs the following input parameters to be defined
// before including it:
// Input parameter: the name of the enumeration
// #define IMPROVED_ENUM_NAME [NameOfYourEnum]
// Input parameter: the file with the enum items
// #define IMPROVED_ENUM_FILE ["EnumItemFile"]
////////////////////////////////////////////////////////////////////////
// ENUMITEM FILE:
// The EnumItemFile is a list (one per line) of:
// ENUMITEM(EnumItem) or ENUMITEM_VALUE(EnumItem, Value)
////////////////////////////////////////////////////////////////////////
// ALTERNATIVE TO ENUMITEM FILE:
// IMPROVED_ENUM_LIST instead of IMPROVED_ENUM_FILE
// #define IMPROVED_ENUM_LIST  ENUMITEM(Item1) ... ENUMITEM(LastItem)
// #define IMPROVED_ENUM_LIST  ENUMITEM(Item1) \
//                             ENUMITEM(Item2) \
//                             ...
//                             ENUMITEM(LastItem)
////////////////////////////////////////////////////////////////////////
// OPTIONAL INPUT PARAMETERS:
// If you want to define a subclass instead of a namespace, you can
// #define IMPROVED_ENUM_SUBCLASS, or
// #define IMPROVED_ENUM_SUBCLASS_PARENT [ParentClass]
// to make subclass inherit from a ParentClass.
// If you want to extend an already defined ImprovedEnum, you have to
// define which type do you want to extend with
// IMPROVED_ENUM_INHERITED_NAME and IMPROVED_ENUM_INHERITED_FILE
// input parameters.
////////////////////////////////////////////////////////////////////////

// Checking ENUMITEM and ENUMITEM_VALUE macros are not already defined
#if defined(ENUMITEM)
#error ENUMITEM macro cannot be already defined
#elif defined(ENUMITEM_VALUE)
#error ENUMITEM_VALUE macro cannot be already defined
#endif

// Standard string class
#include <string>


#if defined(IMPROVED_ENUM_SUBCLASS_PARENT)

//! We define the IMPROVED_ENUM_NAME subclass (that
//! inherits from the specified parent class) which contains
//! the enum type and the static conversion methods from the
//! enum type to the string type and vice versa.
///////////////////////////////////////////////////////////
#define STATIC_METHOD static
class IMPROVED_ENUM_NAME : public IMPROVED_ENUM_SUBCLASS_PARENT
{
public:

#elif defined(IMPROVED_ENUM_SUBCLASS)

//! We define the IMPROVED_ENUM_NAME subclass, which contains
//! the enum type and the static conversion methods from the
//! enum type to the string type and vice versa.
///////////////////////////////////////////////////////////
#define STATIC_METHOD static
class IMPROVED_ENUM_NAME
{
public:

#else // IMPROVED_ENUM_SUBCLASS || IMPROVED_ENUM_SUBCLASS_PARENT

//! We define the IMPROVED_ENUM_NAME namespace, which contains
//! the enum type and the conversion functions from the
//! enum type to the string type and vice versa.
///////////////////////////////////////////////////////////
#define STATIC_METHOD
namespace IMPROVED_ENUM_NAME
{

#endif // IMPROVED_ENUM_SUBCLASS || IMPROVED_ENUM_SUBCLASS_PARENT

    //! Some stuff to get the string of the IMPROVED_ENUM_NAME
    ///////////////////////////////////////////////////////////
    #define GET_MACRO_STRING_EXPANDED(Macro)  #Macro
    #define GET_MACRO_STRING(Macro)  GET_MACRO_STRING_EXPANDED(Macro)
    #define ENUM_SEPARATOR  "::"
    #define ENUM_TYPE_NAME  GET_MACRO_STRING(IMPROVED_ENUM_NAME)
    STATIC_METHOD inline const std::string EnumSeparator() { return ENUM_SEPARATOR; }
    STATIC_METHOD inline const std::string EnumTypeName() { return ENUM_TYPE_NAME; }
    #ifdef  IMPROVED_ENUM_INHERITED_NAME
    #define PARENT_ENUM_TYPE_NAME  GET_MACRO_STRING(IMPROVED_ENUM_INHERITED_NAME)
    #define FULL_ENUM_TYPE_NAME    PARENT_ENUM_TYPE_NAME  ENUM_SEPARATOR  ENUM_TYPE_NAME
    #else //IMPROVED_ENUM_INHERITED_NAME
    #define PARENT_ENUM_TYPE_NAME  ""
    #define FULL_ENUM_TYPE_NAME    ENUM_TYPE_NAME
    #endif//IMPROVED_ENUM_INHERITED_NAME
    STATIC_METHOD inline const std::string ParentEnumTypeName()
                                            { return PARENT_ENUM_TYPE_NAME; }
    STATIC_METHOD inline const std::string FullEnumTypeName()
                                            { return FULL_ENUM_TYPE_NAME; }


    //! This defines the enumerated type:
    //////////////////////////////////////////
    typedef enum EnumTypeTag
    {
        //////////////////////////////////////////
        // With this mini-macro we make ENUMITEM file/s
        // a list of items separated by commas:
        #define  ENUMITEM(EnumItem) EnumItem,
        #define  ENUMITEM_VALUE(EnumItem, Value) EnumItem = Value,
        #ifdef   IMPROVED_ENUM_INHERITED_FILE
        #include IMPROVED_ENUM_INHERITED_FILE
        #endif// IMPROVED_ENUM_INHERITED_FILE
        #ifdef   IMPROVED_ENUM_FILE
        #include IMPROVED_ENUM_FILE
        #else // IMPROVED_ENUM_LIST
                 IMPROVED_ENUM_LIST
        #endif// IMPROVED_ENUM_FILE
        #undef   ENUMITEM_VALUE
        #undef   ENUMITEM
        //////////////////////////////////////////
        NotValidEnumItem // We add this item to all enums
    } EnumType, Type;

    //! Conversion from enum to string:
    //////////////////////////////////////////
    STATIC_METHOD inline const std::string Enum2String(const EnumType& t)
    {
        switch (t)
        {
        //////////////////////////////////////////
        // With this mini-macro we make ENUMITEM file/s
        // a CASE list which returns the stringized value:
        #define  ENUMITEM(EnumItem) case EnumItem : return #EnumItem;
        #define  ENUMITEM_VALUE(EnumItem, Value) ENUMITEM(EnumItem)
        #ifdef   IMPROVED_ENUM_INHERITED_FILE
        #include IMPROVED_ENUM_INHERITED_FILE
        #endif// IMPROVED_ENUM_INHERITED_FILE
        #ifdef   IMPROVED_ENUM_FILE
        #include IMPROVED_ENUM_FILE
        #else // IMPROVED_ENUM_LIST
                 IMPROVED_ENUM_LIST
        #endif// IMPROVED_ENUM_FILE
        #undef   ENUMITEM_VALUE
        #undef   ENUMITEM
        //////////////////////////////////////////
        }
        return ""; // NotValidEnumItem
    }

    //! Conversion from enum to full string (namespace::string):
    /////////////////////////////////////////////////////////////
    STATIC_METHOD inline const std::string Enum2FullString(const EnumType& t)
    {
        switch (t)
        {
        //////////////////////////////////////////
        // With this mini-macro we make ENUMITEM file/s
        // a CASE list which returns the stringized value:
        #define  ENUMITEM(EnumItem) \
        case EnumItem : return  FULL_ENUM_TYPE_NAME  ENUM_SEPARATOR  #EnumItem;
        #define  ENUMITEM_VALUE(EnumItem, Value) ENUMITEM(EnumItem)
        #ifdef   IMPROVED_ENUM_INHERITED_FILE
        #include IMPROVED_ENUM_INHERITED_FILE
        #endif// IMPROVED_ENUM_INHERITED_FILE
        #ifdef   IMPROVED_ENUM_FILE
        #include IMPROVED_ENUM_FILE
        #else // IMPROVED_ENUM_LIST
                 IMPROVED_ENUM_LIST
        #endif// IMPROVED_ENUM_FILE
        #undef   ENUMITEM_VALUE
        #undef   ENUMITEM
        //////////////////////////////////////////
        }
        return ""; // NotValidEnumItem
    }

    //! Conversion from string to enum:
    //////////////////////////////////////////
    STATIC_METHOD inline const EnumType String2Enum(const std::string& s)
    {
        if (s == "") return NotValidEnumItem;
        //////////////////////////////////////////
        // With this mini-macro we make ENUMITEM file/s
        // an IF list which returns the enum item:
        #define  ENUMITEM(EnumItem) if (s == #EnumItem) return EnumItem;
        #define  ENUMITEM_VALUE(EnumItem, Value) ENUMITEM(EnumItem)
        #ifdef   IMPROVED_ENUM_INHERITED_FILE
        #include IMPROVED_ENUM_INHERITED_FILE
        #endif// IMPROVED_ENUM_INHERITED_FILE
        #ifdef   IMPROVED_ENUM_FILE
        #include IMPROVED_ENUM_FILE
        #else // IMPROVED_ENUM_LIST
                 IMPROVED_ENUM_LIST
        #endif// IMPROVED_ENUM_FILE
        #undef   ENUMITEM_VALUE
        #undef   ENUMITEM
        //////////////////////////////////////////
        return NotValidEnumItem;
    }

    //! Conversion from full string (namespace::string) to enum:
    /////////////////////////////////////////////////////////////
    STATIC_METHOD inline const EnumType FullString2Enum(const std::string& s)
    {
        if (s == "") return NotValidEnumItem;
        //////////////////////////////////////////
        // With this mini-macro we make ENUMITEM file/s
        // an IF list which returns the enum item:
        #define  ENUMITEM(EnumItem) \
        if (s ==  FULL_ENUM_TYPE_NAME  ENUM_SEPARATOR  #EnumItem) return EnumItem;
        #define  ENUMITEM_VALUE(EnumItem, Value) ENUMITEM(EnumItem)
        #ifdef   IMPROVED_ENUM_INHERITED_FILE
        #include IMPROVED_ENUM_INHERITED_FILE
        #endif// IMPROVED_ENUM_INHERITED_FILE
        #ifdef   IMPROVED_ENUM_FILE
        #include IMPROVED_ENUM_FILE
        #else // IMPROVED_ENUM_LIST
                 IMPROVED_ENUM_LIST
        #endif// IMPROVED_ENUM_FILE
        #undef   ENUMITEM_VALUE
        #undef   ENUMITEM
        //////////////////////////////////////////
        return NotValidEnumItem;
    }

    //! Enum iteration to next:
    //////////////////////////////////////////
    STATIC_METHOD inline const EnumType NextEnumItem(const EnumType& t)
    {
        switch (t)
        {
        case NotValidEnumItem : 
        //////////////////////////////////////////
        // With this mini-macro we make ENUMITEM file/s
        // a CASE list which returns the next item:
        #define  ENUMITEM(EnumItem) return EnumItem; case EnumItem : 
        #define  ENUMITEM_VALUE(EnumItem, Value) ENUMITEM(EnumItem)
        #ifdef   IMPROVED_ENUM_INHERITED_FILE
        #include IMPROVED_ENUM_INHERITED_FILE
        #endif// IMPROVED_ENUM_INHERITED_FILE
        #ifdef   IMPROVED_ENUM_FILE
        #include IMPROVED_ENUM_FILE
        #else // IMPROVED_ENUM_LIST
                 IMPROVED_ENUM_LIST
        #endif// IMPROVED_ENUM_FILE
        #undef   ENUMITEM_VALUE
        #undef   ENUMITEM
        //////////////////////////////////////////
                    return NotValidEnumItem; // (This indentation is intentional)
        }
        return NotValidEnumItem; // (This line is intentional too, do not remove)
    }

    //! Enum iteration to previous:
    //////////////////////////////////////////
    STATIC_METHOD inline const EnumType PreviousEnumItem(const EnumType& t)
    {
        EnumType tprev = NotValidEnumItem;
        //////////////////////////////////////////
        // With this mini-macro we make ENUMITEM file/s
        // an IF list which returns the previous item:
        #define  ENUMITEM(EnumItem) \
        if (t == EnumItem) return tprev; else tprev = EnumItem;
        #define  ENUMITEM_VALUE(EnumItem, Value) ENUMITEM(EnumItem)
        #ifdef   IMPROVED_ENUM_INHERITED_FILE
        #include IMPROVED_ENUM_INHERITED_FILE
        #endif// IMPROVED_ENUM_INHERITED_FILE
        #ifdef   IMPROVED_ENUM_FILE
        #include IMPROVED_ENUM_FILE
        #else // IMPROVED_ENUM_LIST
                 IMPROVED_ENUM_LIST
        #endif// IMPROVED_ENUM_FILE
        #undef   ENUMITEM_VALUE
        #undef   ENUMITEM
        //////////////////////////////////////////
        return tprev;
    }

    //! The first and the last Enums:
    //////////////////////////////////////////
    STATIC_METHOD inline const EnumType FirstEnumItem()
                                        { return NextEnumItem(NotValidEnumItem); }
    STATIC_METHOD inline const EnumType LastEnumItem()
                                        { return PreviousEnumItem(NotValidEnumItem); }

    //! Number of enum items:
    //////////////////////////////////////////
    STATIC_METHOD inline const int NumberOfValidEnumItem()
    {
        return 0
        //////////////////////////////////////////
        // With this mini-macro we make ENUMITEM file/s
        // a counter list:
        #define  ENUMITEM(EnumItem) +1
        #define  ENUMITEM_VALUE(EnumItem, Value) ENUMITEM(EnumItem)
        #ifdef   IMPROVED_ENUM_INHERITED_FILE
        #include IMPROVED_ENUM_INHERITED_FILE
        #endif// IMPROVED_ENUM_INHERITED_FILE
        #ifdef   IMPROVED_ENUM_FILE
        #include IMPROVED_ENUM_FILE
        #else // IMPROVED_ENUM_LIST
                 IMPROVED_ENUM_LIST
        #endif// IMPROVED_ENUM_FILE
        #undef   ENUMITEM_VALUE
        #undef   ENUMITEM
        //////////////////////////////////////////
        ;
    }

    // This is only needed when working with inherited/extended enums:
    ////////////////////////////////////////////////////////////////////
    #ifdef IMPROVED_ENUM_INHERITED_NAME
        //! Conversion from inherited enums:
        //! The same class items are returned without change, but
        //! other items are converted from one namespace to the other:
        //////////////////////////////////////////
        STATIC_METHOD inline const EnumType Inherited2Enum(const EnumType& t)
                                                        { return t; }
        STATIC_METHOD inline const EnumType Inherited2Enum(
                                const IMPROVED_ENUM_INHERITED_NAME::EnumType& t)
        {
            switch (t)
            {
            //////////////////////////////////////////
            // With this mini-macro we make ENUMITEM file
            // a CASE list which returns the converted value
            // from one namespace to the other:
            #define  ENUMITEM(EnumItem) \
            case IMPROVED_ENUM_INHERITED_NAME::EnumItem : return EnumItem;
            #define  ENUMITEM_VALUE(EnumItem, Value) ENUMITEM(EnumItem)
            #ifdef   IMPROVED_ENUM_INHERITED_FILE
            #include IMPROVED_ENUM_INHERITED_FILE
            #endif// IMPROVED_ENUM_INHERITED_FILE
            #undef   ENUMITEM_VALUE
            #undef   ENUMITEM
            //////////////////////////////////////////
            }
            return NotValidEnumItem;
        }

        //! Conversion to inherited enums:
        //! The same class items are returned without change, but
        //! other items are converted from one namespace to the other:
        //////////////////////////////////////////
        STATIC_METHOD inline const IMPROVED_ENUM_INHERITED_NAME::EnumType Enum2Inherited(
                                    const IMPROVED_ENUM_INHERITED_NAME::EnumType& t)
                                                                        { return t; }
        STATIC_METHOD inline const IMPROVED_ENUM_INHERITED_NAME::EnumType Enum2Inherited(
                                                                    const EnumType& t)
        {
            switch (t)
            {
            //////////////////////////////////////////
            // With this mini-macro we make ENUMITEM file
            // a CASE list which returns the converted value
            // from one namespace to the other:
            #define  ENUMITEM(EnumItem) \
            case EnumItem : return IMPROVED_ENUM_INHERITED_NAME::EnumItem;
            #define  ENUMITEM_VALUE(EnumItem, Value) ENUMITEM(EnumItem)
            #ifdef   IMPROVED_ENUM_INHERITED_FILE
            #include IMPROVED_ENUM_INHERITED_FILE
            #endif// IMPROVED_ENUM_INHERITED_FILE
            #undef   ENUMITEM_VALUE
            #undef   ENUMITEM
            //////////////////////////////////////////
            }
            return IMPROVED_ENUM_INHERITED_NAME::NotValidEnumItem;
        }

        //! Enum iteration to next extended (not inherited):
        //////////////////////////////////////////
        STATIC_METHOD inline const EnumType NextExtendedEnumItem(
                                                const EnumType& t)
        {
            switch (t)
            {
            case NotValidEnumItem : 
            //////////////////////////////////////////
            // With this mini-macro we make ENUMITEM file/s
            // a CASE list which returns the next item:
            #define  ENUMITEM(EnumItem) return EnumItem; case EnumItem : 
            #define  ENUMITEM_VALUE(EnumItem, Value) ENUMITEM(EnumItem)
            #ifdef   IMPROVED_ENUM_FILE
            #include IMPROVED_ENUM_FILE
            #else // IMPROVED_ENUM_LIST
                     IMPROVED_ENUM_LIST
            #endif// IMPROVED_ENUM_FILE
            #undef   ENUMITEM_VALUE
            #undef   ENUMITEM
            //////////////////////////////////////////
                                    return NotValidEnumItem;
            }
            return NotValidEnumItem;
        }

        //! Enum iteration to previous extended (not inherited):
        //////////////////////////////////////////
        STATIC_METHOD inline const EnumType PreviousExtendedEnumItem(
                                                const EnumType& t)
        {
            EnumType tprev = NotValidEnumItem;
            //////////////////////////////////////////
            // With this mini-macro we make ENUMITEM file/s
            // an IF list which returns the previous item:
            #define  ENUMITEM(EnumItem) \
            if (t == EnumItem) return tprev; else tprev = EnumItem;
            #define  ENUMITEM_VALUE(EnumItem, Value) ENUMITEM(EnumItem)
            #ifdef   IMPROVED_ENUM_FILE
            #include IMPROVED_ENUM_FILE
            #else // IMPROVED_ENUM_LIST
                     IMPROVED_ENUM_LIST
            #endif// IMPROVED_ENUM_FILE
            #undef   ENUMITEM_VALUE
            #undef   ENUMITEM
            //////////////////////////////////////////
            return tprev;
        }

        //! The first and the last extended Enums:
        //////////////////////////////////////////
        STATIC_METHOD inline const EnumType FirstExtendedEnumItem()
                            { return NextExtendedEnumItem(NotValidEnumItem); }
        STATIC_METHOD inline const EnumType LastExtendedEnumItem()
                            { return PreviousExtendedEnumItem(NotValidEnumItem); }

        //! Number of extended enum items:
        //////////////////////////////////////////
        STATIC_METHOD inline const int NumberOfExtendedValidEnumItem()
        {
            return 0
            //////////////////////////////////////////
            // With this mini-macro we make ENUMITEM file
            // a counter list:
            #define  ENUMITEM(EnumItem) +1
            #define  ENUMITEM_VALUE(EnumItem, Value) ENUMITEM(EnumItem)
            #ifdef   IMPROVED_ENUM_FILE
            #include IMPROVED_ENUM_FILE
            #else // IMPROVED_ENUM_LIST
                     IMPROVED_ENUM_LIST
            #endif// IMPROVED_ENUM_FILE
            #undef   ENUMITEM_VALUE
            #undef   ENUMITEM
            //////////////////////////////////////////
            ;
        }

        //! Enum iteration to next inherited:
        //////////////////////////////////////////
        STATIC_METHOD inline const EnumType NextInheritedEnumItem(
                                                const EnumType& t)
        {
            switch (t)
            {
            case NotValidEnumItem : 
            //////////////////////////////////////////
            // With this mini-macro we make ENUMITEM file/s
            // a CASE list which returns the next item:
            #define  ENUMITEM(EnumItem) return EnumItem; case EnumItem : 
            #define  ENUMITEM_VALUE(EnumItem, Value) ENUMITEM(EnumItem)
            #include IMPROVED_ENUM_INHERITED_FILE
            #undef   ENUMITEM_VALUE
            #undef   ENUMITEM
            //////////////////////////////////////////
                                    return NotValidEnumItem;
            }
            return NotValidEnumItem;
        }

        //! Enum iteration to previous inherited:
        //////////////////////////////////////////
        STATIC_METHOD inline const EnumType PreviousInheritedEnumItem(
                                                    const EnumType& t)
        {
            EnumType tprev = NotValidEnumItem;
            //////////////////////////////////////////
            // With this mini-macro we make ENUMITEM file/s
            // an IF list which returns the previous item:
            #define  ENUMITEM(EnumItem) \
            if (t == EnumItem) return tprev; else tprev = EnumItem;
            #define  ENUMITEM_VALUE(EnumItem, Value) ENUMITEM(EnumItem)
            #include IMPROVED_ENUM_INHERITED_FILE
            #undef   ENUMITEM_VALUE
            #undef   ENUMITEM
            //////////////////////////////////////////
            return tprev;
        }

        //! The first and the last inherited Enums:
        //////////////////////////////////////////
        STATIC_METHOD inline const EnumType FirstInheritedEnumItem()
                                { return NextInheritedEnumItem(NotValidEnumItem); }
        STATIC_METHOD inline const EnumType LastInheritedEnumItem()
                                { return PreviousInheritedEnumItem(NotValidEnumItem); }

        //! Number of inherited enum items:
        //////////////////////////////////////////
        STATIC_METHOD inline const int NumberOfInheritedValidEnumItem()
        {
            return 0
            //////////////////////////////////////////
            // With this mini-macro we make ENUMITEM file
            // a counter list:
            #define  ENUMITEM(EnumItem) +1
            #define  ENUMITEM_VALUE(EnumItem, Value) ENUMITEM(EnumItem)
            #include IMPROVED_ENUM_INHERITED_FILE
            #undef   ENUMITEM_VALUE
            #undef   ENUMITEM
            //////////////////////////////////////////
            ;
        }

    #endif // IMPROVED_ENUM_INHERITED_NAME

    // Free temporary macros:
    ///////////////////////////
    #undef STATIC_METHOD
    #undef ENUM_SEPARATOR
    #undef ENUM_TYPE_NAME
    #undef PARENT_ENUM_TYPE_NAME
    #undef FULL_ENUM_TYPE_NAME
    #undef GET_MACRO_STRING
    #undef GET_MACRO_STRING_EXPANDED
}
#if defined(IMPROVED_ENUM_SUBCLASS) || defined(IMPROVED_ENUM_SUBCLASS_PARENT)
;
#endif

// Free this file's parameters:
////////////////////////////////
#undef IMPROVED_ENUM_NAME
#undef IMPROVED_ENUM_FILE
#undef IMPROVED_ENUM_LIST
#undef IMPROVED_ENUM_SUBCLASS
#undef IMPROVED_ENUM_SUBCLASS_PARENT
#undef IMPROVED_ENUM_INHERITED_NAME
#undef IMPROVED_ENUM_INHERITED_FILE
// Do not use directives like: #endif (reason above)

关注点

我找不到一种方法可以在宏中包含 #include#define 指令,所以我认为这无法通过标准的宏定义来完成。我决定创建一个包含我所需的所有预处理指令的文件,然后在我的代码中需要它的任何地方包含它。这种方法唯一的问题在于我必须如何将参数/参数传递给这个文件,因为定义 enum 类型的代码不像其他解决方案那样清晰。无论如何,对于我遇到的特定问题,这是我找到的最佳解决方案。欢迎任何建设性的评论和想法。

除此之外,我还探索了枚举中的继承概念,并将其与类继承进行了比较。派生类向基类添加变量和方法,而派生/扩展 enum 向基 enum 添加项。我想将派生类与相应的派生 enums(以多态模式)混合,因为我认为这是一个好主意,但这可能是无稽之谈(因为类继承是为了专业化,而 enum 继承是为了扩展)。正如你可以有一个接受基类指针但可以使用派生类(使用多态)调用的方法一样,我想有一个接受 "基 enum" 但可以使用 "派生 enums" 调用的方法。这可以通过将每个 enum 封装在一个类中来实现,这就是 IMPROVED_ENUM_SUBCLASSIMPROVED_ENUM_SUBCLASS_PARENT 输入参数的新方法。欢迎提出想法……

历史

  • v1.0 - 2008/12/16
    • 第一个版本,包含 Enum2StringString2Enum
  • v2.0 - 2008/12/22
    • 添加了 Enum2InheritedInherited2Enum
  • v3.0 - 2008/12/23
    • 添加了 CountIteration,并在 The Code Project 上发布。
  • v4.0 - 2009/04/02
    • 添加了 IMPROVED_ENUM_LIST 用于内联 enum 声明,Enum2FullStringFullString2Enum 用于在处理多个 enum 时生成无歧义的 string,所有函数都声明为 inline 以便在头文件中无问题地使用,以及一个 "vcproj" 示例。
  • v5.0 - 2009/04/13
    • 添加了 IMPROVED_ENUM_SUBCLASSIMPROVED_ENUM_SUBCLASS_PARENT,以将 enum 封装在 static 类或 static 派生类中,而不是命名空间。调用语法与命名空间中的语法完全相同。您不能在类中声明命名空间,但通过此选项,您现在可以在类中声明 ImprovedEnums。示例也已更新。
© . All rights reserved.