Here is my source (as answer) , how to implement underlying_value, and to_enum functions. How to automatically convert strongly typed enum into int?
underlying_value - no problem. but, to_enum - has problem.
see:
enum class E{ a = 1, b = 3, c = 5 };
auto e_a = utils::underlying_value(E::a); //OK
E t = utils::to_enum<E>( 2 ) ; // compiled, but it's incorrect. I think here must throws exception?
Q: how to implement to_enum correctly?
Despite the comments on the question, this can be done in C++11, though to do it without code repetition you will eventually have to wrap the
enum class
declaration in a macro. That may make my answer unsuitable, depending on your needs. Either way, doing the checked conversion requires some machinery, so I will get to the macro last.The basic idea is to use
constexpr
functions to scan an array:This prints
3
. If the line withbad_converted
is uncommented, this code will not compile at all, as it says. The checked conversion can be done either at runtime or during compilation. It will be done during compilation if the argument toto_enum
is a compile-time constant. Also, as you can probably see, this does a linear scan ofvalues
, but that can be replaced with another algorithm if it becomes a performance problem for a very large enum.The code I just showed is a sketch that shows the underlying method. In order to make this less of a pain to use, you should wrap the declaration of
E
in a macro that will automatically generate thevalues[]
array and the associated functions. I will show and rationalize the contents of this macro one point at a time.The basic macro looks like this
So, in this example,
__VA_ARGS__
will be the tokensa = 1, b = 3, c = 5
. So, we can declare the enum itself inside the macro as follows:However, we cannot simply declare:
because that expands to
which is not scoped (missing
TypeName::
in front of each value), and is not valid C++ because of the extra assignment operators inside the array initializer. I will solve the second problem first. You need to define a class like this one:Now, you can write
(swallow_assignment<E>)E::a = 1
. What will happen is, at compile time,E::a
will get converted to the assignable value(swallow_assignment<E>)E::a
, which has the same internal representation asE::a
. That value will then ignore the assignment of1
, and then will be converted back toE::a
.What's left is to prefix each of the declared constants so that we get
which will now be a valid initializer. This can be done with a mapping macro. I won't go into the details here, because that is a whole separate topic, but such a macro can be found here https://github.com/aantron/better-enums/blob/e28177b11a9e3d7152c5216d84fdf8939aff0eba/enum_preprocessor_map.h. Boost might also have a better one. Whatever macro you are using, I will assume that its signature is
PP_MAP(prefix, __VA_ARGS__)
. The sketch for the final macro definition for the whole enum then becomes:You will probably want to stuff these definitions into a specialization of a traits type, so that you can use this macro with more than one
enum class
(otherwise the arrays namedvalues
will collide). You may have to use weak symbols to avoid linking problems if you makevalues
a static member of a traits class, however.These last points are left as an exercise, because this answer is already way too long :) I have a library which does all of the above, though it wraps an
enum
instead of providing a traits specialization for anenum class
. There is an unpublished branch with a combination ofenum class
/traits, however. You can see the library here: http://aantron.github.io/better-enums. The library's::_from_integral()
method corresponds to theto_enum
function in your question, and it does both run-time and compile-time conversions.