// This file was generated automatically // Semi-quine // // Let's make a Better Enum compose its own definition. It won't be literally as // defined, since we will lose some information about initializers, but we will // be able to preserve their numeric values. We will reserve the memory buffers // at compile time. // // There are actually better ways to do this than shown here. You could define a // macro that expands to an ENUM declaration and also stringizes it. The point // here is to show some of the reflective capabilities of Better Enums, so you // can adapt them for cases where a macro is not sufficient. #include #include #include #ifndef BETTER_ENUMS_CONSTEXPR_TO_STRING #define BETTER_ENUMS_CONSTEXPR_TO_STRING #endif #include #define HIGH_COLOR 0 ENUM(Channel, int, Red, Green, Blue) ENUM(Depth, int, TrueColor = 1, HighColor = HIGH_COLOR) // First, we need to be able to get the length of each definition above. We will // assume that the underlying type is always int, and that the spacing // convention is followed as above. This allows us to write: constexpr size_t value_length(int n, int bound = 10, size_t digits = 1) { return n < bound ? digits : value_length(n, bound * 10, digits + 1); } constexpr size_t string_length(const char *s, size_t index = 0) { return s[index] == '\0' ? index : string_length(s, index + 1); } template constexpr size_t constants_length(size_t index = 0, size_t accumulator = 0) { return index >= Enum::_size ? accumulator : constants_length( index + 1, accumulator + string_length(", ") + string_length(Enum::_names()[index]) + string_length(" = ") + value_length( Enum::_values()[index]._to_integral())); } template constexpr size_t declaration_length() { return string_length("ENUM(") + string_length(Enum::_name()) + string_length(", int") + constants_length() + string_length(")"); } // Now, we can declare: char channel_definition[declaration_length() + 1]; char depth_definition[declaration_length() + 1]; // And finally, the formatting function: template size_t format(char *buffer) { size_t offset = 0; offset += std::sprintf(buffer, "ENUM(%s, int", Enum::_name()); for (Enum value : Enum::_values()) { offset += std::sprintf(buffer + offset, ", %s = %i", value._to_string(), value._to_integral()); } offset += std::sprintf(buffer + offset, ")"); return offset; } int main() { size_t channel_length = format(channel_definition); assert(channel_length + 1 == sizeof(channel_definition)); size_t depth_length = format(depth_definition); assert(depth_length + 1 == sizeof(depth_definition)); std::cout << channel_definition << std::endl; std::cout << depth_definition << std::endl; return 0; } // This outputs: // // ENUM(Channel, int, Red = 0, Green = 1, Blue = 2) // ENUM(Depth, int, TrueColor = 1, HighColor = 0) // // This does have the advantage of not depending on anything else defined in the // program, which isn't as easy to achieve with stringization.