// This file was generated automatically. // Semi-quine // // Let's make a Better Enum assemble its own definition in memory. It won't be // literally as defined, since we will lose the exact initializer expressions, // but we will be able to preserve the numeric values. We will reserve the // memory buffer for the definition at compile time. // // Ok, so it's not really a quine, because we won't be writing all the code // needed to generate the definition to the buffer as well. And, there are // better ways to dump the definition than shown here. You could simply define a // macro that expands to an ENUM declaration and also stringizes it. // // But that's not the point here. The point of this page 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 // First, we will need full compile-time reflection, since we will be calling // _to_string. Let's make sure it's enabled by defining // BETTER_ENUMS_CONSTEXPR_TO_STRING before including enum.h: #ifndef BETTER_ENUMS_CONSTEXPR_TO_STRING #define BETTER_ENUMS_CONSTEXPR_TO_STRING #endif #include // Now, let's declare some enums to dump later: ENUM(Channel, int, Red, Green, Blue) ENUM(Depth, int, TrueColor = 1, HighColor = 0) // Computing the size of the buffer // // First, we need to be able to get the length of each declaration above. We // will assume that the underlying type is always int, and that the spacing // convention is followed as above. // // First, let's get the lengths of basic components: // Returns the length of the string representation of the number n 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); } // Returns the length of s constexpr size_t string_length(const char *s, size_t index = 0) { return s[index] == '\0' ? index : string_length(s, index + 1); } // Now, the length of the constant declaration. Here is where we lose // information about initializers. We are going to format the constant // declarations like this: // // Red = 0, Green = 1, Blue = 2 // TrueColor = 1, HighColor = 0 // // This is because Better Enums doesn't provide a way to know what the exact // initializer was or whether there even was one - just the numeric value of // each constant. If we were trying to be clever, we could avoid formatting // initializers for sequential values, but I won't go through this exercise // here. // Returns the length of the constants portion of the declaration of Enum, // as described above. 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())); } // Finally, we can combine these to get the length of the formatted declaration // of the whole enum: // Returns the length of the whole declaration of Enum, assuming the // underlying type is int, and the constants are initialized as assumed by // constants_length() above. template constexpr size_t declaration_length() { return string_length("ENUM(") + string_length(Enum::_name()) + string_length(", int") + constants_length() + string_length(")"); } // Formatting the enums // // Now, we can declare the buffers. The memory will be reserved at load time by // the binary's loader. The extra one byte in each buffer is for the null // terminator. char channel_definition[declaration_length() + 1]; char depth_definition[declaration_length() + 1]; // Let's also create the formatting function. This is executed at run time, but // we will be giving it pointers to our statically-allocated buffers. It will // format the enum declaration and then return the number of bytes it wrote to // the buffer, so that we can do a sanity check on it. 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; } // Checking our work // // Now, we can write and run this code. 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; } // It prints: // // ENUM(Channel, int, Red = 0, Green = 1, Blue = 2) // ENUM(Depth, int, TrueColor = 1, HighColor = 0)