Sometimes stringifying C/C++ enumerator, or enum, values within its literal names are useful for debugging or logging. In this article, I’ll share how I achieve basic enum stringify and how to do it safely against changes to enum definitions.
Basic Idea
Given a boring enum Result as follows.
enum class Result : int8_t {
OK = 0,
FAILED,
};
My usual way to stringify them is using a table, i.e.
#define STR(X) #X
char const* StringifyResult(Result res) {
char const* str[] = {
STR(OK),
STR(FAILED),
};
return str[static_cast<std::underlying_type_t<Result>>(res)];
}
int main() {
printf("%s", StringifyResult(Result::OK)); // print "OK"
}
It benefits us from simplicity - simply copy’n paste those enum values then decorate them with STR(). Tons of editors can accomplish such editing in a snap.
Check Consistency
Occasionally we may change an enum definition - changes include adding, removal, and renaming. For first two cases, add a LAST enum value to end of an enum definition and static_assert the enum size. i.e.
#define STR(X) #X
enum class Result : int8_t {
OK = 0,
FAILED,
LAST,
};
char const* StringifyResult(Result res) {
char const* str[] = {
STR(OK),
STR(FAILED),
};
static_assert(Result::LAST == sizeof(str)/sizeof(char const*));
// ...
}
In case of renaming, enhance the macro STR, to validate correct enum value names, with comma operator. i.e.
#define STR(Enum, X) ((void)Enum::X, #X)
Then apply the macro, e.g.
STR(Result, OK) // Expand as ((void)Result::OK, "OK")
- The first operand,
(void)Enum::X, of comma operator makes sure that the nameXis a valid identifier ofEnumand causes compile error if it’s not. (void)suppresses compile warning/error of unused expression result.- If the first operand was compiled correctly, then
#Xis the final result of the comma expression.
Finally, whenever definition of an enum is changed, we get a compile error if the stringify function does not cover all enum values with valid names.
Written with StackEdit.
留言
張貼留言