...one of the most highly
regarded and expertly designed C++ library projects in the
world.
— Herb Sutter and Andrei
Alexandrescu, C++
Coding Standards
The boost/core/scoped_enum.hpp
header
contains a number of macros that can be used to generate C++11 scoped enums
(7.2 [dcl.enum]) if the feature is supported by the compiler, otherwise emulate
it with C++03 constructs. The BOOST_NO_CXX11_SCOPED_ENUMS
macro from Boost.Config is used to detect the feature support in the compiler.
Some of the enumerations defined in the standard library are scoped enums.
enum class future_errc { broken_promise, future_already_retrieved, promise_already_satisfied, no_state };
The user can portably declare such enumeration as follows:
BOOST_SCOPED_ENUM_DECLARE_BEGIN(future_errc) { broken_promise, future_already_retrieved, promise_already_satisfied, no_state } BOOST_SCOPED_ENUM_DECLARE_END(future_errc)
These macros allow using future_errc
in almost all the cases as a scoped enum.
future_errc ev = future_errc::no_state;
It is possible to specify the underlying type of the enumeration:
BOOST_SCOPED_ENUM_UT_DECLARE_BEGIN(future_errc, unsigned int) { broken_promise, future_already_retrieved, promise_already_satisfied, no_state } BOOST_SCOPED_ENUM_DECLARE_END(future_errc)
The enumeration supports explicit conversion from the underlying type.
The enumeration can be forward declared:
BOOST_SCOPED_ENUM_FORWARD_DECLARE(future_errc);
There are however some limitations. The emulated scoped enum is not a C++
enum, so is_enum<
future_errc >
will be false_type
.
The emulated scoped enum can not be used in switch nor in template arguments. For these cases the user needs to use some helpers. Instead of
switch (ev) { case future_errc::broken_promise: // ...
use
switch (boost::native_value(ev)) { case future_errc::broken_promise: // ...
and instead of
template <> struct is_error_code_enum< future_errc > : public true_type { };
use
template <> struct is_error_code_enum< BOOST_SCOPED_ENUM_NATIVE(future_errc) > : public true_type { };
Explicit conversion to the underlying type should be performed with boost::underlying_cast
instead of static_cast
:
unsigned int val = boost::underlying_cast< unsigned int >(ev);
In C++03, scoped enums behave differently in case of calling an overloaded function when one overload takes a scoped enum as a parameter, and the other takes a parameter of an integral type. Consider the following code:
enum enum_regular { REGULAR_A, REGULAR_B }; BOOST_SCOPED_ENUM_DECLARE_BEGIN(enum_scoped) { a, b } BOOST_SCOPED_ENUM_DECLARE_END(enum_scoped) void regular_or_int(enum_regular); // (1) void regular_or_int(int); // (2) void scoped_or_int(enum_scoped); // (3) void scoped_or_int(int); // (4) regular_or_int(REGULAR_A); // calls (1) in C++03 and C++11 scoped_or_int(enum_scoped::a); // calls (3) in C++11 but (4) in C++03! scoped_or_int(enum_scoped(enum_scoped::a)); // calls (3) in C++03 and C++11
Here is usage example:
BOOST_SCOPED_ENUM_UT_DECLARE_BEGIN(algae, char) { green, red, cyan } BOOST_SCOPED_ENUM_DECLARE_END(algae) ... algae sample( algae::red ); void foo( algae color ); ... sample = algae::green; foo( algae::cyan );
In early versions of the header there were two ways to declare scoped enums, with different pros and cons to each. The other way used a different set of macros:
BOOST_SCOPED_ENUM_START(algae) { green, red, cyan }; BOOST_SCOPED_ENUM_END ... BOOST_SCOPED_ENUM(algae) sample( algae::red ); void foo( BOOST_SCOPED_ENUM(algae) color ); ... sample = algae::green; foo( algae::cyan );
Here BOOST_SCOPED_ENUM_START
corresponds to BOOST_SCOPED_ENUM_DECLARE_BEGIN
,
BOOST_SCOPED_ENUM_END
to
BOOST_SCOPED_ENUM_DECLARE_END
and BOOST_SCOPED_ENUM
to
BOOST_SCOPED_ENUM_NATIVE
.
Note also the semicolon before BOOST_SCOPED_ENUM_END
.
In the current version these macros produce equivalent result to the ones described above and are considered deprecated.
The header boost/core/underlying_type.hpp
defines
the metafunction boost::underlying_type
which can be used to obtain
the underlying type of the scoped enum. This metafunction has support for
emulated scoped enums declared with macros in boost/core/scoped_enum.hpp
. When native scoped enums are supported
by the compiler, this metafunction is equivalent to std::underlying_type
.
Unfortunately, there are configurations which implement scoped enums but
not std::underlying_type
. In this case boost::underlying_type
has to be specialized by
user. The macro BOOST_NO_UNDERLYING_TYPE
is defined to indicate such cases.
This scoped enum emulation was developed by Beman Dawes, Vicente J. Botet Escriba and Anthony Williams.
Helpful comments and suggestions were also made by Kjell Elster, Phil Endecott, Joel Falcou, Mathias Gaunard, Felipe Magno de Almeida, Matt Calabrese, Daniel James and Andrey Semashev.