
Often times in an application, you’ve got to convert between an enumeration value and a string.
It’s generally best to wrap this in an object, so you keep all the details of the conversion
contained in one place. It also makes the syntax of comparing objects a simple process of
a == b
or similar.
You’d probably want to use the Macro List Pattern to capture the details for each string and value pair. This is a good way to also associate additional metadata with an item (e.g. a human readable string, a uuid, etc)
Here’s an example:
// myvalue.h
#pragma once
// Use the Macro List Pattern to capture the specifics for the values.
#define MY_VALUE_LIST \
X(Invalid, "Invalid", 0) \
X(ThingOne, "Thing One", 1) \
X(ThingTwo, "Thing Two", 2)
class MyValue {
#define X(a, b, c) a = c,
public:
enum ID {
MY_VALUE_LIST
};
#undef X
static ID stringToID(const char *string);
static const char *stringFromID(ID val);
MyValue(ID val = Invalid) : _id(val) {}
MyValue(const char *val) : _id(stringToID(val)) {}
MyValue(const std::string &str) : _id(stringToID(str.c_str())) {}
operator==(const MyValue &o) const { return _id == o._id; }
operator==(ID v) const { return v == _id; }
operator!=(const MyValue &o) const { return _id != o._id; }
operator!=(ID v) const { return v != _id; }
bool isValid() const { return _id != Invalid; }
ID id() const { return _id; }
const char *toString() const { return stringFromID(_id); }
private:
ID _id;
}
// myvalue.cpp
#include "myvalue.h"
#define X(a, b, c) if(!strncmp(string, b, sizeof(b))) return a;
MyValue::ID MyValue::stringToID(const char *string) {
MY_VALUE_LIST
return Invalid;
}
#undef X
#define X(a, b, c) case a: ret = b; break;
const char *MyValue::stringFromID(ID val) {
const char *ret = nullptr; // or perhaps "Invalid", depends on the implementation needs
switch(val) {
MY_VALUE_LIST
}
return ret;
}
#undef X
Please note the stringToID() uses a linear search, which are fine for small lists but may be unacceptably slow for larger lists. The could be implemented as a hash lookup from a hash that’s filled at startup by the appropriate X() macro.
Once you’ve got a object for your value, you can then use it as if you were using a regular enum or integer, but it’ll handle the translation to and from a string for you.
Example:
MyValue a("Thing One"); // This will cause the string to integer lookup (slow)
MyValue b(MyValue::ThingOne); // This will cause the value to be assigned to the ID (fast)
a == b; // This will evaluate true and is fast, since it's just an integer compare