C++ 98/C++11 enums
I am writing a cross-platform C++ library to read/write configuration files from/to multiple representation (JSON, XML, etc). In a nushell, the configuration file is tree node based. Each node is represented as a SettingNodeInterface. Each Node has a type: OBJECT, ARRAY, VALUE, etc. I was wondering what would be the best way to represent this group of related values while keeping in mind the following constraints:
- (A) only values defined in the group are valid (i.e Not other values can be passed outside the ones predefined)
- (B) the scope of this group must NOT be bigger than the class it is enclosed in.
- (C) this group must be part of a Public API
- (D) code MUST be C++98 and C++11 compatible.
- (E) A group MUST be a type (predefined or user defined type).
What are the possible solutions ?
- Macros ?
- Constraints A/B/E are NOT satisfied.
- declare static constant in a namespace ?
- Constraint A is NOT satisfied.
- Rationale: Someone else outside the public API can add more constants in this namespace since a namespace can be defined over multiple headers.
- Enums ?
- Constraint A is satisfied.
- Constraint B is satisfied.
- Constraint C is satisfied. An enum can be declared in a header.
- Constraint D is satisfied though I will have to be careful about how enums are declared and referenced in the code.
Enums are a good choice. Let's see how one should go about it.
Possible Implementations
Solution 1
class SettingNodeInterface {
public:
/**
* Types
*/
enum Type {
TYPE_UNDEFINED, /** Undefined */
TYPE_OBJECT, /** Object */
TYPE_ARRAY, /** Array */
TYPE_VALUE /** Value */
};
};
- Pros:
- Simplest solution
- Cons:
- Using the enum is confusing since it would be:
SettingNodeInterface::Type myValue = SettingNodeInterface.TYPE_ARRAY
in C++98/C++11
- One is forced to prefix all enums values with the enum type name to prevent collision AND improve code clarity.
SettingNodeInterface::Type myValue = SettingNodeInterface::Type::TYPE_ARRAY
in C++11 only- I like this notation since it is explicit that the enum value is of a type "Type" (Foo::Type::). Sadly, it is only available in C++11.
- Using the enum is confusing since it would be:
Solution 2
class SettingNodeInterface {
public:
class SettingNodeType { public:
/**
* Types
*/
enum Type {
UNDEFINED, /** Undefined */
OBJECT, /** Object */
ARRAY, /** Array */
VALUE /** Value */
};
SettingNodeType(Type t) : t_(t) {}
SettingNodeType() : t_(UNDEFINED) {}
operator const Type() const { return t_;}
private:
Type t_;
};
};
- Pros:
- Using the enum is NOT confusing this time:
SettingNodeInterface::SettingNodeType::Type myValue = SettingNodeInterface::SettingNodeType::ARRAY
in C++98/C++11
- Using the enum is NOT confusing this time:
- Cons:
- Code is NOT as easy to read as solution 1. One may wonder why there is a need for the class
SettingNodeType
. - In line with the first bullet point in pros, declaring the type is confusing:
SettingNodeInterface::SettingNodeType::Type
. One would expect something likeSettingNodeInterface::SettingNodeType
ORSettingNodeInterface::Type
- Code is NOT as easy to read as solution 1. One may wonder why there is a need for the class
Solution 3
class SettingNodeInterface {
public:
struct SettingNodeType {
enum Type {
UNDEFINED, /** Undefined */
OBJECT, /** Object */
ARRAY, /** Array */
VALUE /** Value
};
};
};
- Pros:
- Code readability improved over Solution 2 since we don't have to create a class to store our enum value.
- Cons
- Same as Solution 2's cons.
I raised this problematic StackOverflow. There may be additional inputs over there if you are curious.
Conclusion
I picked Solution 1 for the following reasons:
- I want to have an
SettingNodeInterface
class as simple as possible. - Solutions 2/3 are not better than solution 1 and not simpler in any way.