Created
October 25, 2018 16:25
-
-
Save NocturnDragon/524b8b97a813e8f7a1f950c6ecc6e2be to your computer and use it in GitHub Desktop.
A supremely dirty way of converting an enum value to a string at runtime, without the use of double-declaration XMacros or its ilk.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Source: https://twitter.com/zygoloid/status/735275127873540096 | |
// | |
// A supremely dirty way of converting an enum value to a string at runtime, without | |
// the use of double-declaration XMacros or its ilk. | |
// | |
// Very silly code but it actually works. | |
// | |
#include <stdio.h> | |
// | |
// Example enum | |
// | |
enum TestEnum | |
{ | |
// List your enums | |
// They don't *actually* have to be sequential and you can assign any values | |
// you want. However, the bigger the values, the longer it will take to find | |
// your enum string! | |
Zero, One, Two, Three, Four, Five, Six, Seven, Eight, Monkeys, | |
// Uniquely named total count | |
// With enum classes this could be made a little simpler | |
TestEnum_Count | |
}; | |
#if __cplusplus < 201402L | |
namespace std { | |
template<typename T, T...> struct integer_sequence {}; | |
template<typename T, T N> using make_integer_sequence = __make_integer_seq<integer_sequence, T, N>; | |
template<size_t ...N> using index_sequence = integer_sequence<size_t, N...>; | |
template<size_t N> using make_index_sequence = make_integer_sequence<size_t, N>; | |
} | |
#endif | |
template<int n> struct StrCpy { | |
char buf[n+1]; | |
constexpr StrCpy(const char *p) | |
#if __cplusplus < 201402L | |
: StrCpy(std::make_index_sequence<n>(), p) {} | |
template<size_t ...i> | |
constexpr StrCpy(std::index_sequence<i...>, const char *p) | |
: buf{p[i]...} {} | |
#else | |
{ for (int i = 0; i < n; ++i) buf[i] = p[i]; buf[n] = 0; } | |
#endif | |
}; | |
// | |
// The magic. | |
// This is terrible and there are ways to make it even more useful. | |
// Please send help, I should not be writing crap like this! | |
// | |
template <typename ENUM_TYPE, ENUM_TYPE ENUM_VALUE> | |
const char* EnumName() | |
{ | |
constexpr const char *s = __PRETTY_FUNCTION__ + 59; | |
constexpr int n = __builtin_strlen(s) - 1; | |
static constexpr StrCpy<n> str{s}; | |
return str.buf; | |
} | |
template <typename ENUM_TYPE, ENUM_TYPE ENUM_COUNT, ENUM_TYPE ENUM_VALUE> | |
struct EnumMatch | |
{ | |
static const char* Do(ENUM_TYPE enum_value) | |
{ | |
if (enum_value == ENUM_VALUE) | |
return EnumName<ENUM_TYPE, ENUM_VALUE>(); | |
return EnumMatch<ENUM_TYPE, ENUM_COUNT, ENUM_TYPE(ENUM_VALUE+1)>::Do(enum_value); | |
} | |
}; | |
template <typename ENUM_TYPE, ENUM_TYPE ENUM_COUNT> | |
struct EnumMatch<ENUM_TYPE, ENUM_COUNT, ENUM_COUNT> | |
{ | |
static const char* Do(ENUM_TYPE enum_value) | |
{ | |
return "Enum not found"; | |
} | |
}; | |
#define ENUM_NAME(enum_type, enum_value) \ | |
EnumMatch<enum_type, enum_type##_Count, enum_type(0)>::Do(enum_value) | |
int main() | |
{ | |
for (int i = 0; i < TestEnum_Count; i++) | |
printf("%s\n", ENUM_NAME(TestEnum, TestEnum(i))); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment