4.6 KiB
Conversions
Let's begin by including enum.h and declaring our enum:
#include <cassert>
#include <iostream>
<em>#include <enum.h></em>
<em>BETTER_ENUM</em>(<em>Channel</em>, <em>int</em>, <em>Cyan</em> = <em>1</em>, <em>Magenta</em>, <em>Yellow</em>, <em>Black</em>)
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:
._to_string::_from_string::_from_string_nothrow
int main()
{
<em>Channel channel = Channel::Cyan</em>;
std::cout << <em>channel._to_string()</em> << " ";
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 = <em>Channel::_from_string("Magenta")</em>;
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:
<em>better_enums::optional<Channel></em> maybe_channel =
<em>Channel::_from_string_nothrow("Yellow")</em>;
if (<em>!maybe_channel</em>)
std::cout << "error";
else
std::cout << <em>maybe_channel-></em>_to_string() << " ";
This returns an optional value, in the style of
boost::optional
or the proposed
std::optional.
What that means for the above code is:
- if the conversion succeeds,
maybe_channelconverts totrueand*maybe_channelis the converted value of typeChannel, - if the conversion fails,
maybe_channelconverts tofalse.
In $cxx11, you can use auto to avoid writing out the optional type:
<em>auto</em> maybe_channel = <em>Channel::_from_string_nothrow("Yellow")</em>;
if (<em>!maybe_channel</em>)
std::cout << "error";
else
std::cout << <em>maybe_channel-></em>_to_string() << " ";
Case-insensitive strings
The "_nocase" string conversions follow the same pattern, except for the lack
of a "to_string_nocase".
-
::_from_string_nocase -
::_from_string_nocase_nothrowchannel = 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:
-
._to_integral -
::_from_integral -
::_from_integral_nothrow -
::_from_integral_uncheckedchannel = 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 = <em>Channel::_from_integral_unchecked(0)</em>;
// <em>Invalid</em> - 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(<em>Channel::_is_valid(3)</em>);
assert(<em>Channel::_is_valid("Magenta")</em>);
assert(<em>Channel::_is_valid_nocase("cYaN")</em>);
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 << (<em>+Channel::Cyan</em>)._to_string();
This is due to some type gymnastics in the implementation of Better Enums. The reference has a full explanation.
std::cout << std::endl;
return 0;
}
%% description = Better Enums conversion functions. Converting to string, from string, to int, from int, and validation, both case-sensitive and case-insensitive. Exception-throwing and non-throwing variants presented.