Is it possible to determine the cardinality of a c++ enum class:
enum class Example { A, B, C, D, E };
I tried to use si
There is one trick based on X()-macros: image, you have the following enum:
enum MyEnum {BOX, RECT};
Reformat it to:
#define MyEnumDef \
X(BOX), \
X(RECT)
Then the following code defines enum type:
enum MyEnum
{
#define X(val) val
MyEnumDef
#undef X
};
And the following code calculates number of enum elements:
template <typename ... T> void null(T...) {}
template <typename ... T>
constexpr size_t countLength(T ... args)
{
null(args...); //kill warnings
return sizeof...(args);
}
constexpr size_t enumLength()
{
#define XValue(val) #val
return countLength(MyEnumDef);
#undef XValue
}
...
std::array<int, enumLength()> some_arr; //enumLength() is compile-time
std::cout << enumLength() << std::endl; //result is: 2
...
One trick you can try is to add a enum value at the end of your list and use that as the size. In your example
enum class Example { A, B, C, D, E, ExampleCount };
constexpr auto TEST_START_LINE = __LINE__;
enum class TEST { // Subtract extra lines from TEST_SIZE if an entry takes more than one
ONE = 7
, TWO = 6
, THREE = 9
};
constexpr auto TEST_SIZE = __LINE__ - TEST_START_LINE - 3;
This is derived from UglyCoder's answer but improves it in three ways.
BEGIN and SIZE) (Cameron's answer also has this problem.)
It retains UglyCoder's advantage over Cameron's answer that enumerators can be assigned arbitrary values.
A problem (shared with UglyCoder but not with Cameron) is that it makes newlines and comments significant ... which is unexpected. So someone could add an entry with whitespace or a comment without adjusting TEST_SIZE's calculation.
If you make use of boost's preprocessor utilities, you can obtain the count using BOOST_PP_SEQ_SIZE(...).
For example, one could define the CREATE_ENUM macro as follows:
#include <boost/preprocessor.hpp>
#define ENUM_PRIMITIVE_TYPE std::int32_t
#define CREATE_ENUM(EnumType, enumValSeq) \
enum class EnumType : ENUM_PRIMITIVE_TYPE \
{ \
BOOST_PP_SEQ_ENUM(enumValSeq) \
}; \
static constexpr ENUM_PRIMITIVE_TYPE EnumType##Count = \
BOOST_PP_SEQ_SIZE(enumValSeq); \
// END MACRO
Then, calling the macro:
CREATE_ENUM(Example, (A)(B)(C)(D)(E));
would generate the following code:
enum class Example : std::int32_t
{
A, B, C, D, E
};
static constexpr std::int32_t ExampleCount = 5;
This is only scratching the surface with regards to the boost preprocessor tools. For example, your macro could also define to/from string conversion utilities and ostream operators for your strongly typed enum.
More on boost preprocessor tools here: https://www.boost.org/doc/libs/1_70_0/libs/preprocessor/doc/AppendixA-AnIntroductiontoPreprocessorMetaprogramming.html
As an aside, I happen to strongly agree with @FantasticMrFox that the additional Count enumerated value employed in the accepted answer will create compiler warning headaches galore if using a switch statement. I find the unhandled case compiler warning quite useful for safer code maintenance, so I wouldn't want to undermine it.
For C++17 you can use magic_enum::enum_count from lib https://github.com/Neargye/magic_enum:
magic_enum::enum_count<Example>() -> 4.
This library uses a compiler-specific hack (based on __PRETTY_FUNCTION__ / __FUNCSIG__), which works on Clang >= 5, MSVC >= 15.3 and GCC >= 9.
We go through the given interval range, and find all the enumerations with a name, this will be their count. Read more about limitations
Many more about this hack in this post https://taylorconor.com/blog/enum-reflection.
It can be solved by a trick with std::initializer_list:
#define TypedEnum(Name, Type, ...) \
struct Name { \
enum : Type{ \
__VA_ARGS__ \
}; \
static inline const size_t count = []{ \
static Type __VA_ARGS__; return std::size({__VA_ARGS__}); \
}(); \
};
Usage:
#define Enum(Name, ...) TypedEnum(Name, int, _VA_ARGS_)
Enum(FakeEnum, A = 1, B = 0, C)
int main()
{
std::cout << FakeEnum::A << std::endl
<< FakeEnun::count << std::endl;
}