## General underlying types The underlying type of a Better Enum doesn't have to be an integral type. It can be any literal type `T`, as long as you provide a `constexpr` two-way mapping between `T` and an integral type of your choosing. It also works in $cxx98, though, of course, `T` doesn't have to be literal and the mapping doesn't have to be `constexpr` — everything will be done by Better Enums at run time. Doing this enables the following usage: // The type. A color triplet. struct html_color { uint8_t r, g, b; constexpr html_color(uint8_t _r, uint8_t g, uint8_t b) : r(_r), g(_g), b(_b) { } }; // The enum. ENUM(Color, html_color, darksalmon = 0xc47451, purplemimosa = 0x9e7bff, slimegreen = 0xbce954) // The usage. Color c = Color::darksalmon; std::cout << "Red component: " << c->r << std::endl; switch (c) { case Color::darksalmon: // ... case Color::purplemimosa: // ... case Color::slimegreen: // ... } As you can see, you can have an enumerated set of any literal type, and safely use the values in `switch`, with the compiler checking exhaustiveness. You can also access the type's members using the `enum->underlying_member` syntax. You do have to supply the mapping to an integral type, however. One option is: // The mapping. It just stuffs bits. template <> struct ::better_enums::underlying_traits { using integral_representation = unsigned int; constexpr static html_color from_integral(unsigned int i) { return html_color(i >> 16 & 0xff, i >> 8 & 0xff, i & 0xff); } constexpr static unsigned int to_integral(html_color c) { return (unsigned int)c.r << 16 | (unsigned int)c.g << 8 | c.b; } }; ### Using constructors in initializers The declaration above used only numeric initializers. It is possible to use the type's own constructors, provided the type has a `constexpr` conversion to your chosen integral type: // The type. struct html_color { uint8_t r, g, b; constexpr html_color(uint8_t _r, uint8_t g, uint8_t b) : r(_r), g(_g), b(_b) { } // This is new: constexpr operator unsigned int() const { return (unsigned int)r << 16 | (unsigned int)g << 8 | b; } }; // The enum. ENUM(Color, html_color, darksalmon = 0xc47451, purplemimosa = 0x9e7bff, slimegreen = 0xbce954, celeste = html_color(0x50, 0xeb, 0xec)) This is not possible at all in $cxx98, however. ### Letting the compiler enumerate your literal type You don't have to use initializers. For example, as long as your example type `file_descriptor` knows how to deal with the values, you can have the compiler generate them in sequence: ENUM(FD, file_descriptor, STDIN, STDOUT, STDERR, SomePipeYourDaemonHas, ...) SAMPLE You can see the code "in action" in the [test case][test]. Be aware that it's not very "nice," because it uses conditional compilation to run under multiple compilers. I haven't written a clean sample or documentation yet simply because this feature is in a very early stage of development. [test]: $repo/blob/master/test/cxxtest/underlying.h ### Discussion This feature is still semi-experimental, though I expect it to remain stable, except perhaps that I will make it possible to infer the type `integral_representation`. Any opinions are welcome. - The main reason Better Enums needs you to supply and explicit mapping is because it can't just get the "bits" of objects of underlying type in `constexpr` code. Both `reinterpret_cast` and union abuse seem to be forbidden in `constexpr` functions. - There is currently no way to have two different integral representaitons for the same underlying type in different enums. I don't think that's a major use case at this point, however. %% description = "Using Better Enums with non-integral underlying types."