namespaces for enum types - best practices

前端 未结 8 1836
隐瞒了意图╮
隐瞒了意图╮ 2020-12-04 06:34

Often, one needs several enumerated types together. Sometimes, one has a name clash. Two solutions to this come to mind: use a namespace, or use \'larger\' enum element na

相关标签:
8条回答
  • 2020-12-04 06:42

    Since enums are scoped to their enclosing scope, it's probably best to wrap them in something to avoid polluting the global namespace and to help avoid name collisions. I prefer a namespace to class simply because namespace feels like a bag of holding, whereas class feels like a robust object (cf. the struct vs. class debate). A possible benefit to a namespace is that it can be extended later - useful if you're dealing with third-party code that you cannot modify.

    This is all moot of course when we get enum classes with C++0x.

    0 讨论(0)
  • 2020-12-04 06:49

    Original C++03 answer:

    The benefit from a namespace (over a class) is that you can use using declarations when you want.

    The problem with using a namespace is that namespaces can be expanded elsewhere in the code. In a large project, you would not be guaranteed that two distinct enums don't both think they are called eFeelings

    For simpler-looking code, I use a struct, as you presumably want the contents to be public.

    If you're doing any of these practices, you are ahead of the curve and probably don't need to scrutinize this further.

    Newer, C++11 advice:

    If you are using C++11 or later, enum class will implicitly scope the enum values within the enum's name.

    With enum class you will lose implicit conversions and comparisons to integer types, but in practice that may help you discover ambiguous or buggy code.

    0 讨论(0)
  • 2020-12-04 06:51

    I've hybridized the preceding answers to something like this: (EDIT: This is only useful for pre- C++11. If you are using C++11, use enum class)

    I've got one big header file that contains all my project enums, because these enums are shared between worker classes and it doesn't make sense to put the enums in the worker classes themselves.

    The struct avoids the public: syntactic sugar, and the typedef lets you actually declare variables of these enums within other worker classes.

    I don't think using a namespace helps at all. Maybe this is because I'm a C# programmer, and there you have to use the enum type name when referring the values, so I'm used to it.

        struct KeySource {
            typedef enum { 
                None, 
                Efuse, 
                Bbram
            } Type;
        };
    
        struct Checksum {
            typedef enum {
                None =0,
                MD5 = 1,
                SHA1 = 2,
                SHA2 = 3
            } Type;
        };
    
        struct Encryption {
            typedef enum {
                Undetermined,
                None,
                AES
            } Type;
        };
    
        struct File {
            typedef enum {
                Unknown = 0,
                MCS,
                MEM,
                BIN,
                HEX
            } Type;
        };
    

    ...

    class Worker {
        File::Type fileType;
        void DoIt() {
           switch(fileType) {
           case File::MCS: ... ;
           case File::MEM: ... ;
           case File::HEX: ... ;
        }
    }
    
    0 讨论(0)
  • 2020-12-04 06:51

    I also tend to wrap my enums in classes.

    As signaled by Richard Corden, the benefit of a class is that it is a type in the c++ sense and so you can use it with templates.

    I have special toolbox::Enum class for my needs that I specialize for every templates which provides basic functions (mainly: mapping an enum value to a std::string so that I/O are easier to read).

    My little template also has the added benefit of really checking for the allowed values. The compiler is kind of lax on checking if the value really is in the enum:

    typedef enum { False: 0, True: 2 } boolean;
       // The classic enum you don't want to see around your code ;)
    
    int main(int argc, char* argv[])
    {
      boolean x = static_cast<boolean>(1);
      return (x == False || x == True) ? 0 : 1;
    } // main
    

    It always bothered me that the compiler will not catch this, since you are left with an enum value that has no sense (and that you won't expect).

    Similarly:

    typedef enum { Zero: 0, One: 1, Two: 2 } example;
    
    int main(int argc, char* argv[])
    {
      example y = static_cast<example>(3);
      return (y == Zero || y == One || y == Two) ? 0 : 1;
    } // main
    

    Once again main will return an error.

    The problem is that the compiler will fit the enum in the smallest representation available (here we need 2 bits) and that everything that fits in this representation is considered a valid value.

    There is also the problem that sometimes you'd rather have a loop on the possible values instead of a switch so that you don't have to modify all you switches each time you add a value to the enum.

    All in all my little helper really ease things for my enums (of course, it adds some overhead) and it is only possible because I nest each enum in its own struct :)

    0 讨论(0)
  • 2020-12-04 06:52

    I would definitely avoid using a class for this; use a namespace instead. The question boils down to whether to use a namespace or to use unique ids for the enum values. Personally, I'd use a namespace so that my ids could be shorter and hopefully more self-explanatory. Then application code could use a 'using namespace' directive and make everything more readable.

    From your example above:

    using namespace Colors;
    
    void setPenColor( const e c ) {
        switch (c) {
            default: assert(false);
            break; case cRed: //...
            break; case cBlue: //...
            //...
        }
    }
    
    0 讨论(0)
  • 2020-12-04 06:53

    Advantage of using a class is that you can build a full-fledged class on top of it.

    #include <cassert>
    
    class Color
    {
    public:
        typedef enum
        {
            Red,
            Blue,
            Green,
            Yellow
        } enum_type;
    
    private:
        enum_type _val;
    
    public:
        Color(enum_type val = Blue)
            : _val(val)
        {
            assert(val <= Yellow);
        }
    
        operator enum_type() const
        {
            return _val;
        }
    };
    
    void SetPenColor(const Color c)
    {
        switch (c)
        {
            case Color::Red:
                // ...
                break;
        }
    }
    

    As the above example shows, by using a class you can:

    1. prohibit (sadly, not compile-time) C++ from allowing a cast from invalid value,
    2. set a (non-zero) default for newly-created enums,
    3. add further methods, like for returning a string representation of a choice.

    Just note that you need to declare operator enum_type() so that C++ would know how to convert your class into underlying enum. Otherwise, you won't be able to pass the type to a switch statement.

    0 讨论(0)
提交回复
热议问题