Should I use #define, enum or const?

bit-manipulation, c++, c-preprocessor, enums

Solution

Combine the strategies to reduce the disadvantages of a single approach. I work in embedded systems so the following solution is based on the fact that integer and bitwise operators are fast, low memory & low in flash usage.

Place the enum in a namespace to prevent the constants from polluting the global namespace.

namespace RecordType {

An enum declares and defines a compile time checked typed. Always use compile time type checking to make sure arguments and variables are given the correct type. There is no need for the typedef in C++.

enum TRecordType { xNew = 1, xDeleted = 2, xModified = 4, xExisting = 8,

Create another member for an invalid state. This can be useful as error code; for example, when you want to return the state but the I/O operation fails. It is also useful for debugging; use it in initialisation lists and destructors to know if the variable's value should be used.

xInvalid = 16 };

Consider that you have two purposes for this type. To track the current state of a record and to create a mask to select records in certain states. Create an inline function to test if the value of the type is valid for your purpose; as a state marker vs a state mask. This will catch bugs as the `typedef` is just an `int` and a value such as `0xDEADBEEF` may be in your variable through uninitialised or mispointed variables.

inline bool IsValidState( TRecordType v) {
    switch(v) { case xNew: case xDeleted: case xModified: case xExisting: return true; }
    return false;
}

 inline bool IsValidMask( TRecordType v) {
    return v >= xNew  && v < xInvalid ;
}

Add a `using` directive if you want to use the type often.

using RecordType ::TRecordType ;

The value checking functions are useful in asserts to trap bad values as soon as they are used. The quicker you catch a bug when running, the less damage it can do.

Here are some examples to put it all together.

void showRecords(TRecordType mask) {
    assert(RecordType::IsValidMask(mask));
    // do stuff;
}

void wombleRecord(TRecord rec, TRecordType state) {
    assert(RecordType::IsValidState(state));
    if (RecordType ::xNew) {
    // ...
} in runtime

TRecordType updateRecord(TRecord rec, TRecordType newstate) {
    assert(RecordType::IsValidState(newstate));
    //...
    if (! access_was_successful) return RecordType ::xInvalid;
    return newstate;
}

The only way to ensure correct value safety is to use a dedicated class with operator overloads and that is left as an exercise for another reader.

Problem

In a C++ project I'm working on, I have a flag kind of value which can have four values. Those four flags can be combined. Flags describe the records in database and can be: - new record - deleted record - modified record - existing record Now, for each record I wish to keep this attribute, so I could use an enum: ``` enum { xNew, xDeleted, xModified, xExisting } ``` However, in other places in code, I need to select which records are to be visible to the user, so I'd like to be able to pass that as a single parameter, like: ``` showRecords(xNew | xDeleted); ``` So, it seems I have three possible appoaches: ``` #define X_NEW 0x01 #define X_DELETED 0x02 #define X_MODIFIED 0x04 #define X_EXISTING 0x08 ``` or ``` typedef enum { xNew = 1, xDeleted, xModified = 4, xExisting = 8 } RecordType; ``` or ``` namespace RecordType { static const uint8 xNew = 1; static const uint8 xDeleted = 2; static const uint8 xModified = 4; static const uint8 xExisting = 8; } ``` Space requirements are important (byte vs int) but not crucial. With defines I lose type safety, and with `enum` I lose some space (integers) and probably have to cast when I want to do a bitwise operation. With `const` I think I also lose type safety since a random `uint8` could get in by mistake. Is there some other cleaner way? If not, what would you use and why? P.S. The rest of the code is rather clean modern C++ without `#define`s, and I have used namespaces and templates in few spaces, so those aren't out of question either.

Original source

Related problems