## Conversions Let's begin by including `enum.h` and declaring our enum: #include #include #include ENUM(Channel, int, Cyan = 1, Magenta, Yellow, Black) We now have an `int`-sized enum with four constants. There are three groups of conversion functions: for strings, case-insensitive strings, and integers. They all follow the same pattern, so I'll explain the string functions in detail, and the rest can be understood by analogy. $internal_toc ### Strings There are three functions: 1. `._to_string` 2. `::_from_string` 3. `::_from_string_nothrow` int main() { Channel channel = Channel::Cyan; std::cout << channel._to_string() << " "; As you'd expect, the code above prints "Cyan". If `channel` is invalid — for example, if you simply cast the number "42" to `Channel` — then the result of `to_string` is undefined. --- channel = Channel::_from_string("Magenta"); std::cout << channel._to_string() << " "; This is also straightforward. If you pass a string which is not the name of a declared value, `_from_string` throws `std::runtime_error`. --- If you don't want an exception, there is `_from_string_nothrow`: better_enums::optional maybe_channel = Channel::_from_string_nothrow("Yellow"); if (!maybe_channel) std::cout << "error"; else std::cout << maybe_channel->_to_string() << " "; This returns an *optional value*, in the style of [`boost::optional`](http://www.boost.org/doc/libs/1_58_0/libs/optional/doc/html/index.html) or the proposed [`std::optional`](http://en.cppreference.com/w/cpp/experimental/optional). What that means for the above code is: - if the conversion succeeds, `maybe_channel` converts to `true` and `*maybe_channel` is the converted value of type `Channel`, - if the conversion fails, `maybe_channel` converts to `false`. In $cxx11, you can use `auto` to avoid writing out the optional type: ~~~comment auto maybe_channel = Channel::_from_string_nothrow("Yellow"); if (!maybe_channel) std::cout << "error"; else std::cout << maybe_channel->_to_string() << " "; ~~~ ### Case-insensitive strings The "`_nocase`" string conversions follow the same pattern, except for the lack of a "`to_string_nocase`". 1. `::_from_string_nocase` 2. `::_from_string_nocase_nothrow` channel = Channel::_from_string_nocase("cYaN"); std::cout << channel._to_string() << " "; maybe_channel = Channel::_from_string_nocase_nothrow("rEeD"); assert(!maybe_channel); ### Integers And, it is similar with the *representation type* `int`: 1. `._to_integral` 2. `::_from_integral` 3. `::_from_integral_nothrow` 4. `::_from_integral_unchecked` channel = Channel::Cyan; std::cout << channel._to_integral() << " "; channel = Channel::_from_integral(2); std::cout << channel._to_string() << " "; maybe_channel = Channel::_from_integral_nothrow(0); assert(!maybe_channel); That prints "1 Magenta". `_from_integral_unchecked` is a no-op unchecked cast of integers to enums, so use it carefully. channel = Channel::_from_integral_unchecked(0); // Invalid - better not to try converting it to string! ### Validity checking For completeness, Better Enums also provides three validity checking functions, one for each of the groups of conversions — string, case-insensitive string, and integer: assert(Channel::_is_valid(3)); assert(Channel::_is_valid("Magenta")); assert(Channel::_is_valid_nocase("cYaN")); --- Almost done. There is one unfortunate wrinkle. You cannot convert a literal constant such as `Channel::Cyan` directly to, for example, a string. You have to prefix it with `+`: std::cout << (+Channel::Cyan)._to_string(); This is due to some type gymnastics in the implementation of Better Enums. The reference has a [full explanation](${prefix}ApiReference.html#HelperFunctionsAndTypes). --- std::cout << std::endl; return 0; } %% description = Walkthrough of Better Enums conversion functions.