Counting the Elements in a C++ Enum
I recently had a special requirement to determine the number of elements in an enum type, and in the process I learned the following clever tricks.
Explanation
These techniques originated with a question on Stack Overflow: “Can the number of elements in a C++ enum class be determined?”
The Craftsperson
Basic
The simplest approach is:
| |
Since enumerator values increase from 0 by default, the number of elements can be obtained with static_cast<int>(Example::Count).
Advanced
The method above does not work for enums with custom values, for example:
| |
Having to count them manually is undeniably inconvenient and tedious.
Foolproof
| |
This is merely my opinion on the implementation: more code means more opportunities for errors.
Assessment
When you use switch with this enum class, the compiler warns that a case is missing. It is not elegant.
Moreover, the method above has limited applicability and makes the programmer the greatest risk in the system. As Laozi said: “If something can be automated, automate it.”
Macro Magic
The __LINE__ Macro
As everyone knows, __LINE__ represents the current line number. With that in mind, we can write:
| |
Subtract the line numbers, and disable clang-format to ensure that formatting does not break the layout; the result is the number of elements.
GCC’s Nonstandard __COUNTER__ Macro
__COUNTER__ is a nonstandard compiler extension provided by GNU compilers. It can be thought of as a counter representing an integer. Its value is generally initialized to 0, and it is automatically incremented by 1 each time the compiler encounters it during compilation.
This lets us implement the following:
| |
Boost
If you use Boost’s preprocessor utilities, you can use BOOST_PP_SEQ_SIZE(...) to obtain the count.
For example, the CREATE_ENUM macro can be defined as follows:
| |
Then invoke the macro:
| |
The macro expansion produces the following code:
| |
This is just the tip of the iceberg when it comes to Boost’s preprocessor utilities. For example, macros can also define to/from-string conversion utilities and ostream operators for strongly typed enums.
Read more about Boost’s preprocessor utilities.
Using Variadic __VA_ARGS__
| |
Usage:
| |
Output:
| |
Best of the Bunch—Still Variadic __VA_ARGS__
This can be solved with a trick using std::initializer_list:
| |
Usage:
| |
A Promising Future for Reflection
The C++ Reflection Technical Specification (hereafter, the Reflection TS), especially [reflect.ops.enum]/2 in the latest draft, provides the get_enumerators and TransformationTrait operations:
[reflect.ops.enum]/2
1template <Enum T> struct get_enumeratorsAll specializations of
get_enumerators<T>shall meet the requirements ofTransformationTrait(20.10.1). The nested type namedtypespecifies a metaobject type satisfyingObjectSequence, containing elements that satisfyEnumeratorand reflect the enumerators of the enum type reflected byT.
The draft’s [reflect.ops.objseq] covers ObjectSequence operations. In particular, [reflect.ops.objseq]/1 covers the get_size trait for extracting the number of elements in a metaobject satisfying ObjectSequence:
[reflect.ops.objseq]/1
1template <ObjectSequence T> struct get_size;All specializations of
get_size<T>shall meet the requirements ofUnaryTypeTrait(20.10.1), with a base characteristic ofintegral_constant<size_t,N>, whereNis the number of elements in the object sequence.
Therefore, under the form proposed and implemented in the Reflection TS, the number of elements in an enum could be computed at compile time as follows:
| |
We may see the alias templates get_enumerators_v and get_type_v introduced to simplify reflection further:
| |
As described in Herb Sutter’s [trip report on the Summer ISO C++ Standards Meeting (Rapperswil)], the Reflection TS was declared feature-complete at the ISO C++ Committee’s summer meeting beginning on June 9, 2018.
Reflection TS Feature Complete: The Reflection TS has been declared feature-complete and will undergo its main comment ballot in the summer. Note again that the TS’s current template-metaprogramming syntax is merely a placeholder; the requested feedback concerns the core “guts” of the design. The committee already knows that it intends to replace the surface syntax with a simpler programming model using ordinary compile-time code rather than
<>-style metaprogramming.
Originally planned for C++20, but it is currently unclear whether the Reflection TS still has a chance of making it into C++20.