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 nameX
is a valid identifier ofEnum
and causes compile error if it’s not. (void)
suppresses compile warning/error of unused expression result.- If the first operand was compiled correctly, then
#X
is 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.
留言
張貼留言