2

I was able to define macro that defines an old-style enum and GetCount() method returning the count of enum elements:

#define DEFINE_FLAGS_ENUM(EnumName, ...) \
struct EnumName { \
    enum { __VA_ARGS__ }; \
    static constexpr std::size_t GetCount() { return std::max({__VA_ARGS__}) + 1;} \
};

With enum class my first idea was:

template <class T> struct EnumTraits;

#define NDA_FLAGS_ENUM(EnumName, ...) \
enum class EnumName { __VA_ARGS__ }; \
template<> struct EnumTraits<EnumName> { static constexpr std::size_t GetCount() { return std::max({__VA_ARGS__}) + 1; }};

but this does not compile, because VA_ARGS should be prepended with 'EnumName::'.

Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
Alexey Starinsky
  • 3,699
  • 3
  • 21
  • 57

1 Answers1

2

enum

As suggested in comments, as for your first macro DEFINE_FLAGS_ENUM, std::max does not seem to work. Thus I propose an alternative way for it.

Since our enum is defined as an anonymous one in the class EnumName, we can define the tuple of them and detect the size of it at compile-time as follows. So this macro would well work for you:

#include <tuple>

#define DEFINE_FLAGS_ENUM(EnumName, ...)                  \
struct EnumName                                           \
{                                                         \
    enum { __VA_ARGS__ };                                 \
                                                          \
    static constexpr std::size_t GetCount()               \
    {                                                     \
        using T = decltype(std::make_tuple(__VA_ARGS__)); \
        return std::tuple_size<T>::value;                 \
    }                                                     \
};

enum class

For the enum class, we can reuse the above method as follows. Here, instead of counting the elements of enum class, we again count the ones of enum which is hidden in the class EnumTraits<EnumName> as a private member:

#include <tuple>

template <class T> struct EnumTraits;

#define NDA_FLAGS_ENUM(EnumName, ...)                     \
enum class EnumName { __VA_ARGS__ };                      \
template<>                                                \
struct EnumTraits<EnumName>                               \
{                                                         \
private:                                                  \
    enum { __VA_ARGS__ };                                 \
                                                          \
public:                                                   \
    static constexpr std::size_t GetCount()               \
    {                                                     \
        using T = decltype(std::make_tuple(__VA_ARGS__)); \
        return std::tuple_size<T>::value;                 \
    }                                                     \
};

Then we can define an enum and enum class as follows:

DEMO

// enum.
DEFINE_FLAGS_ENUM(Animal, Dog, Cat)
static_assert(Animal::GetCount() == 2);

// enum class.
NDA_FLAGS_ENUM(Vehicle, Car, Train, AirPlain)
static_assert(EnumTraits<Vehicle>::GetCount() == 3);
Hiroki
  • 2,780
  • 3
  • 12
  • 26
  • 1
    Probably, we can also create std::initializer_list values and do values.end() - values.begin(). – Alexey Starinsky Feb 08 '19 at 17:36
  • 1
    @AlexeyStarinsky thx. Yes, but then `end` and `begin` must be `constexpr` ones because `GetCount()` is `constexpr`. I think that an another way is counting `','` in the string literal `#__VA_ARGS__`. – Hiroki Feb 08 '19 at 17:39
  • 1
    They are consexpr as far as I see. – Alexey Starinsky Feb 08 '19 at 17:48
  • 1
    @AlexeyStarinsky Ah Yes. And my first post (now edited) did take similar approach with the *extended constexpr*, `for(auto& i : {_VA_ARGS__}) {...}` :). – Hiroki Feb 08 '19 at 17:51
  • 1
    And even more simple: we can add Count to the private enum: private: enum { __VA_ARGS__, Count }; \ (but leave enum class as is). – Alexey Starinsky Feb 09 '19 at 09:28
  • @AlexeyStarinsky Ah, nice idea :). [dcl.enum] in the C++ standard states *If the first enumerator has no initializer, the value of the corresponding constant is zero*. So that well works. I found the same discussion [here](https://stackoverflow.com/questions/34811486/do-c-enums-start-at-0). – Hiroki Feb 09 '19 at 10:10