// This file was generated automatically // Special values // // Suppose your project has a convention where each enum has special // invalid and default values. With Better Enums, you can // encode that directly at compile time, and then access each enum's special // values using syntax like Channel c = default_ and Channel c = invalid. This // can make your code adapt automatically to changes in enum definitions, as // well as make it easier to read and understand its intent. #include #include #include // Invalid // // Perhaps the invalid value is usually called Invalid, but not in all enums. // You can encode that using a function template for the common case, and a // macro that creates specializations: template constexpr Enum find_invalid() { return Enum::Invalid; } #define OVERRIDE_INVALID(Enum, Value) \ template<> \ constexpr Enum find_invalid() { return Enum::Value; } // Now, you can declare enums like these: ENUM(Channel, int, Red, Green, Blue, Invalid) ENUM(Compression, int, Undefined, None, Huffman) OVERRIDE_INVALID(Compression, Undefined) // and use them: static_assert(find_invalid() == +Channel::Invalid, ""); static_assert(find_invalid() == +Compression::Undefined, ""); // This even supports enums that don't have an invalid value at all. As long as // they don't have a constant called Invalid, you will get a compile-time error // if you try to call invalid() on them — as you probably should! // // Default // // To encode the policy on default values, we need to do a compile-time check // that the first value is not invalid. Otherwise, the technique is the same. template constexpr Enum find_default() { return Enum::_size < 2 ? throw std::logic_error("enum has no valid constants") : Enum::_values()[0] == find_invalid() ? Enum::_values()[1] : Enum::_values()[0]; } #define OVERRIDE_DEFAULT(Enum, Value) \ static_assert(Enum::Value != Enum::Invalid, \ #Enum ": default cannot equal invalid"); \ template<> \ constexpr Enum find_default() { return Enum::Value; } // Usage: static_assert(find_default() == +Channel::Red, ""); static_assert(find_default() == +Compression::None, ""); // And, if you do ENUM(Answer, int, Yes, No, Invalid) // OVERRIDE_DEFAULT(Answer, Invalid) // you will get a helpful compile-time error saying Answer: default cannot equal // invalid. // // Making the syntax nicer // // For the final touch, we will make the syntax better by introducing new // "keywords" called default_ and invalid in such a way that we cause the // compiler to do type inference: template struct assert_enum { using check = typename Enum::_enumerated; using type = Enum; }; struct invalid_t { template constexpr operator To() const { return find_invalid(); } template constexpr To convert() const { return find_invalid(); } }; struct default_t { template constexpr operator To() const { return find_default(); } }; constexpr invalid_t invalid{}; constexpr default_t default_{}; static_assert(+Channel::Invalid == invalid, ""); static_assert(+Compression::Undefined == invalid, ""); static_assert(+Channel::Red == default_, ""); static_assert(+Compression::None == default_, ""); // We can now have nice code such as this: int main() { Channel channel = default_; std::cout << channel._to_string() << std::endl; return 0; } // There are many possible variations of these policies, but I think most of // them can be encoded in a reasonable fashion using the tools Better Enums // provides. Enjoy!