C++ 模拟C#/Java中的检举(enum)类型

    在C++中enum类型仅仅相当于一系列的整数常量,如果要在日志中显示或标准输出打印时需要把整数与一个字符串对应起来,从配置文件中读取时为了配置文件的可读性最好配置的是字符串名称而不是数字,使用enum类型很不方便,而在C#和Java中都可以方便地使用enum类型的toString方法转换成字符串,或使用parse方法来从字符串转换为对应的enum类型。但我们可以使用类来实现类似C#的enum类型,使用其静态成员变量来代替各enum的各值,再增加相应的方法即可,例如实现一个表示一周各天的类:

class Weekday{
private:
    typedef enum{ _Sunday, _Monday, _Tuesday, _Wednesday, _Thursday,_Friday, _Saturday , _END} WeekdayValue;
    static const char* _names[];

    const WeekdayValue _value;

    Weekday(const WeekdayValue value)
        :_value(value)
    {
    }
public:
    int Value( void ) const { return _value; }
    const char* Name( void ) const { return _names[_value]; }
    operator int( void ) const { return (int)_value; }
public:
    static Weekday Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday;

    static Weekday Parse(const char* str);
    static const char** GetNames( void ) { return _names; }
};
const char* Weekday::_names[]  = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday","Friday", "Saturday"  };

Weekday Weekday::Parse( const char* str )
{
    for(int i=0; i         if( stricmp(str, _names[i]) == 0){
            return Weekday((WeekdayValue)i);
        }
    }
    throw std::exception("Not a valid Weekday name");
}

使用示例:

int main( void )
{
    Weekday d0 = Weekday::FromValue(0);
    cout<<"d0 = "< <

    Weekday d6 = Weekday::FromValue(6);
    cout<<"d6 = "< <

    cout<<"Thursday = {"< <<":"< <<"}"<

    Weekday d5 = Weekday::Parse("FRIDAY");
    cout<<"d5 = "< <
    return 0;
}

但是如果每次我们定义一下这样的类都写这么多代码显然有点儿麻烦,而且很多东西是重复的,可以通过使用宏来简化编码,分别定义两个宏,DeclareEnumN和ImplementEnumN,前者用于生成声明,后者用于声明定义,其中的N表示包含的成员的个数,由于宏不支持宏名的重载,只能通过不同宏名称来区分。这样我们在定义枚举类时需要在.hpp的头文件中来使用DeclareEnumN进行声明,在.cpp的源程序文件中使用ImplementEnumN来定义。宏的实现代码如文件enum_class.hpp:

// enum_class.hpp

#ifndef ENUM_CLASS_HPP_H_
#define ENUM_CLASS_HPP_H_

#include

/** common */
#define DECLARE_ENUM_COMMON_METHODS( C) /
private: /
             static const char* _names[]; /
             const  EnumValue _value; /
             explicit C(const EnumValue value) /
             :_value(value)     /
            { /
                if(_value > _END) /
                    throw std::exception("Invalid value of "#C);    /
            } /
public: /
        int Value( void ) const { return _value; } /
        const char* Name( void ) const { return _names[_value]; } /
        operator int( void ) const { return (int)_value; } /
public: /
        inline static const char** GetNames( void ) { return _names; } /
        static C FromValue(const int value ); /
        static C Parse(const char* str)

#define IMPLEMENT_ENUM_COMMON_METHODS( C ) /
    C C::Parse( const char* str ) /
    { /
    for(int i=0; i
    if( stricmp(str, _names[i]) == 0){ /
    return C((EnumValue)i); /
    } /
    } /
    throw std::exception("Not a valid "#C " name"); /
}

/** 2 enum values */
#if 1
#define DeclareEnum2(C, E1, E2)    /
class C{ /
    private: typedef enum{ _##E1, _##E2, _END} EnumValue; /
    public: static C E1, E2; /
    DECLARE_ENUM_COMMON_METHODS( C ); /
};

#define ImplementEnum2(C, E1, E2) /
    const char* C::_names[]  = {""#E1, ""#E2}; /
    C C::E1(C::_##E1); /
    C C::E2(C::_##E2); /
    IMPLEMENT_ENUM_COMMON_METHODS( C)
#endif

/** 3 enum values */
#if 1
#define DeclareEnum3(C, E1, E2, E3)    /
class C{ /
private: typedef enum{ _##E1, _##E2, _##E3, _END} EnumValue; /
    DECLARE_ENUM_COMMON_METHODS( C ); /
public: static C E1, E2, E3; /
};

#define ImplementEnum3(C, E1, E2, E3) /
    const char* C::_names[]  = {""#E1, ""#E2, ""#E3}; /
    C C::E1(C::_##E1); /
    C C::E2(C::_##E2); /
    C C::E3(C::_##E3); /
    IMPLEMENT_ENUM_COMMON_METHODS( C)
#endif

/** 4 enum values */
#if 1
#define DeclareEnum4(C, E1, E2, E3, E4)    /
class C{ /
private: typedef enum{ _##E1, _##E2, _##E3, _##E4, _END} EnumValue; /
    DECLARE_ENUM_COMMON_METHODS( C ); /
public: static C E1, E2, E3, E4; /
};

#define ImplementEnum4(C, E1, E2, E3, E4) /
    const char* C::_names[]  = {""#E1, ""#E2, ""#E3, ""#E4}; /
    C C::E1(C::_##E1); /
    C C::E2(C::_##E2); /
    C C::E3(C::_##E3); /
    C C::E4(C::_##E4); /
    IMPLEMENT_ENUM_COMMON_METHODS( C)
#endif

/** 5 enum values */
#if 1
#define DeclareEnum5(C, E1, E2, E3, E4, E5)    /
class C{ /
private: typedef enum{ _##E1, _##E2, _##E3, _##E4, _##E5, _END} EnumValue; /
    DECLARE_ENUM_COMMON_METHODS( C ); /
public: static C E1, E2, E3, E4, E5; /
};

#define ImplementEnum5(C, E1, E2, E3, E4, E5) /
    const char* C::_names[]  = {""#E1, ""#E2, ""#E3, ""#E4, ""#E5}; /
    C C::E1(C::_##E1); /
    C C::E2(C::_##E2); /
    C C::E3(C::_##E3); /
    C C::E4(C::_##E4); /
    C C::E5(C::_##E5); /
    IMPLEMENT_ENUM_COMMON_METHODS( C)
#endif

//** 6 enum values */
#if 1
#define DeclareEnum6(C, E1, E2, E3, E4, E5, E6)    /
class C{ /
private: typedef enum{ _##E1, _##E2, _##E3, _##E4, _##E5, _##E6, _END} EnumValue; /
    DECLARE_ENUM_COMMON_METHODS( C ); /
public: static C E1, E2, E3, E4, E5, E6; /
};

#define ImplementEnum6(C, E1, E2, E3, E4, E5, E6) /
    const char* C::_names[]  = {""#E1, ""#E2, ""#E3, ""#E4, ""#E5, ""#E6}; /
    C C::E1(C::_##E1); /
    C C::E2(C::_##E2); /
    C C::E3(C::_##E3); /
    C C::E4(C::_##E4); /
    C C::E5(C::_##E5); /
    C C::E6(C::_##E6); /
    IMPLEMENT_ENUM_COMMON_METHODS( C)
#endif

/** 7 enum values */
#if 1
#define DeclareEnum7(C, E1, E2, E3, E4, E5, E6, E7)    /
class C{ /
private: typedef enum{ _##E1, _##E2, _##E3, _##E4, _##E5, _##E6, _##E7, _END} EnumValue; /
    DECLARE_ENUM_COMMON_METHODS( C ); /
public: static C E1, E2, E3, E4, E5, E6, E7; /
};

#define ImplementEnum7(C, E1, E2, E3, E4, E5, E6, E7) /
    const char* C::_names[]  = {""#E1, ""#E2, ""#E3, ""#E4, ""#E5, ""#E6, ""#E7}; /
    C C::E1(C::_##E1); /
    C C::E2(C::_##E2); /
    C C::E3(C::_##E3); /
    C C::E4(C::_##E4); /
    C C::E5(C::_##E5); /
    C C::E6(C::_##E6); /
    C C::E7(C::_##E7); /
    IMPLEMENT_ENUM_COMMON_METHODS( C)
#endif

/** 8 enum values */
#if 1
#define DeclareEnum8(C, E1, E2, E3, E4, E5, E6, E7, E8)    /
class C{ /
private: typedef enum{ _##E1, _##E2, _##E3, _##E4, _##E5, _##E6, _##E7, _##E8, _END} EnumValue; /
    DECLARE_ENUM_COMMON_METHODS( C ); /
public: static C E1, E2, E3, E4, E5, E6, E7, E8; /
};

#define ImplementEnum8(C, E1, E2, E3, E4, E5, E6, E7, E8) /
    const char* C::_names[]  = {""#E1, ""#E2, ""#E3, ""#E4, ""#E5, ""#E6, ""#E7, ""#E8}; /
    C C::E1(C::_##E1); /
    C C::E2(C::_##E2); /
    C C::E3(C::_##E3); /
    C C::E4(C::_##E4); /
    C C::E5(C::_##E5); /
    C C::E6(C::_##E6); /
    C C::E8(C::_##E7); /
    C C::E7(C::_##E8); /
    IMPLEMENT_ENUM_COMMON_METHODS( C)
#endif

/** 9 enum values */
#if 1
#define DeclareEnum9(C, E1, E2, E3, E4, E5, E6, E7, E8, E9)    /
class C{ /
private: typedef enum{ _##E1, _##E2, _##E3, _##E4, _##E5, _##E6, _##E7, _##E8, _##E9, _END} EnumValue; /
    DECLARE_ENUM_COMMON_METHODS( C ); /
public: static C E1, E2, E3, E4, E5, E6, E7, E8, E9; /
};

#define ImplementEnum9(C, E1, E2, E3, E4, E5, E6, E7, E8, E9) /
    const char* C::_names[]  = {""#E1, ""#E2, ""#E3, ""#E4, ""#E5, ""#E6, ""#E7, ""#E8, ""#E9}; /
    C C::E1(C::_##E1); /
    C C::E2(C::_##E2); /
    C C::E3(C::_##E3); /
    C C::E4(C::_##E4); /
    C C::E5(C::_##E5); /
    C C::E6(C::_##E6); /
    C C::E7(C::_##E7); /
    C C::E8(C::_##E8); /
    C C::E9(C::_##E9); /
    IMPLEMENT_ENUM_COMMON_METHODS( C)
#endif

/** 10 enum values */
#if 1
#define DeclareEnum10(C, E1, E2, E3, E4, E5, E6, E7, E8, E9, E10)    /
class C{ /
private: typedef enum{ _##E1, _##E2, _##E3, _##E4, _##E5, _##E6, _##E7, _##E8, _##E9, _##E10, _END} EnumValue; /
    DECLARE_ENUM_COMMON_METHODS( C ); /
public: static C E1, E2, E3, E4, E5, E6, E7, E8, E9, E10; /
};

#define ImplementEnum10(C, E1, E2, E3, E4, E5, E6, E7, E8, E9, E10) /
    const char* C::_names[]  = {""#E1, ""#E2, ""#E3, ""#E4, ""#E5, ""#E6, ""#E7, ""#E8, ""#E9, ""#E10}; /
    C C::E1(C::_##E1); /
    C C::E2(C::_##E2); /
    C C::E3(C::_##E3); /
    C C::E4(C::_##E4); /
    C C::E5(C::_##E5); /
    C C::E6(C::_##E6); /
    C C::E7(C::_##E7); /
    C C::E8(C::_##E8); /
    C C::E9(C::_##E9); /
    C C::E10(C::_##E10); /
    IMPLEMENT_ENUM_COMMON_METHODS( C)
#endif

//------------------
#endif

该文件定义了2-10个参数的宏,如果需要更多的参数可以自行添加,或者不使用宏,而直接利用定义好的DECLARE_ENUM_COMMON_METHODS、IMPLEMENT_ENUM_COMMON_METHODS两个宏,来定义自己的类,也可以省去很多通用编码。使用时,例如在头文件中进行声明:
// enum_class_demo.hpp

#ifndef ENUM_CLASS_DEMO_HPP
#define ENUM_CLASS_DEMO_HPP

#include "enum_class.hpp"

DeclareEnum2(Result, OK, Error);

DeclareEnum3(E3, E3_1, E3_2, E3_3);
DeclareEnum4(E4, E4_1, E4_2, E4_3, E4_4);
DeclareEnum5(E5, E5_1, E5_2, E5_3, E5_4, E5_5);
DeclareEnum6(E6, E6_1, E6_2, E6_3, E6_4, E6_5, E6_6);
DeclareEnum7(E7, E7_1, E7_2, E7_3, E7_4, E7_5, E7_6, E7_7);
DeclareEnum8(E8, E8_1, E8_2, E8_3, E8_4, E8_5, E8_6, E8_7, E8_8);
DeclareEnum9(E9, E9_1, E9_2, E9_3, E9_4, E9_5, E9_6, E9_7, E9_8, E9_9);
DeclareEnum10(E10, E10_1, E10_2, E10_3, E10_4, E10_5, E10_6, E10_7, E10_8, E10_9,E10_10);

DeclareEnum4(Season, Spring, Summer, Autumn, Winter);

#endif

而在源程序enum_class_demo.cpp文件中进行定义,例如:

#include
#include
using namespace std;
#include "enum_class_demo.hpp"
ImplementEnum2(Result, OK, Error);

ImplementEnum3(E3, E3_1, E3_2, E3_3);
ImplementEnum4(E4, E4_1, E4_2, E4_3, E4_4);
ImplementEnum5(E5, E5_1, E5_2, E5_3, E5_4, E5_5);
ImplementEnum6(E6, E6_1, E6_2, E6_3, E6_4, E6_5, E6_6);
ImplementEnum7(E7, E7_1, E7_2, E7_3, E7_4, E7_5, E7_6, E7_7);
ImplementEnum8(E8, E8_1, E8_2, E8_3, E8_4, E8_5, E8_6, E8_7, E8_8);
ImplementEnum9(E9, E9_1, E9_2, E9_3, E9_4, E9_5, E9_6, E9_7, E9_8, E9_9);
ImplementEnum10(E10, E10_1, E10_2, E10_3, E10_4, E10_5, E10_6, E10_7, E10_8, E10_9,E10_10);

ImplementEnum4(Season, Spring, Summer, Autumn, Winter);

int main( void )
{
    cout<<"ok = {"< <<":"< <<"}"<
    cout<<"error = {"< <<":"< <<"}"<

    cout<<"E3_3 = "< <
    cout<<"E4_4 = "< <
    cout<<"E5_5 = "< <
    cout<<"E6_6 = "< <
    cout<<"E7_7 = "< <
    cout<<"E8_8 = "< <
    cout<<"E9_9 = "< <
    cout<<"E10_10 = "< <

    Season ssn = Season::Parse("winter");
    cout<<"ssn = "< <
    return 0;
}

这样以后再需要定义类似C#或Java中的enum类时只需要两行就可以了。

但是这各模拟方式还有一个缺点,那就是这里用的值都是连续的,如果要定义不连续的值则需要自行实现。使用连续值的一个最重要的原因就是性能,与符串的映射表很自然地实现为一个指针数组,性能非常高效,再加上inline,可以说从数值到字符串的转换几乎达到了性能的0消耗。

在实际使用中使用enum的绝大多数情况下使用连续值即可,枚举值作为mask使用的情况就更复杂,这样还需要重载实现位操作运算符,但技术上实现是没有问题的,而且性能并不会有太大的损耗,对于硬件比大白菜还便宜的时代,这点儿性能又算得了什么呢?所换来的可读性节约的维护成本是完全可以弥补这点儿损失。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值