3

Here is what I need to do: define, inside a class, two enumerations, the second having elements defined using elements values from the first.

So something like this:

class MyClass
{
    public:
        enum class Elem {
            A=1, B=2, C=4, D=8
        };
        enum class Group {
            first = Elem::A | Elem::B,
            second = Elem::A | Elem::C,
            //...
        }; <-- compilation error
};

However, this does not compile due to the fact that | is not defined by default for enum classes.

I tried to define the | operator for Elem enum, outside of the MyClass class (after the class body), but the operator is then not known at the time the Group enum is defined.

So I then tried the following, i.e. defining a constexpr function inside the class:

class MyClass
{
    public:
        enum class Elem {
            A=1, B=2, C=4, D=8
        };

        constexpr static unsigned int merge(
            std::initializer_list<MyClass::Elem> list)
        {
            //Test only: should be an '|' on all list elements
            return 1;
        }

        enum class Group {
            first = merge({Elem::A,Elem::B}),
            second = merge({Elem::A,Elem::C}),
            /*...*/
        };
};

But I get the following error:

error: static constexpr unsigned int merge(std::initializer_list list) called in a constant expression

I understood from here and there that the merge method is considered declared and usable only after the class has been fully declared.

The last solution I can think of is using macros like this:

#define MERGE2ELEMS( a, b ) static_cast<unsigned int>(a) | static_cast<unsigned int>(b)
#define MERGE3ELEMS( a, b, c ) static_cast<unsigned int>(a) | MERGE2ELEMS( b, c )
#define MERGE4ELEMS( a, b, c, d ) static_cast<unsigned int>(a) | MERGE3ELEMS( b, c, d )
#define MERGE5ELEMS( a, b, c, d, e ) static_cast<unsigned int>(a) | MERGE4ELEMS( b, c, d, e )
...

But I need to be able to merge up to 20 Elems and writing 20 macros like this does not seem to be a suitable solution.

What would be the way to go here?

max66
  • 65,235
  • 10
  • 71
  • 111
Silverspur
  • 891
  • 1
  • 12
  • 33

3 Answers3

2

You may play with the order of definition:

class MyClass {
   public:
    enum class Elem { A = 1, B = 2, C = 4, D = 8 };
    enum class Group;
};

constexpr MyClass::Elem operator|(
    const MyClass::Elem& l, const MyClass::Elem& r) {
    return static_cast<MyClass::Elem>(
              static_cast<int>(l) | static_cast<int>(r)
    );
}

enum class MyClass::Group {
    first = Elem::A | Elem::B,
    second = Elem::A | Elem::C,

};

Live demo

But the entire idea sorta goes against the concept of type-safe enum. Maybe you could just use the good old unsafe ones?

Ivan Aksamentov - Drop
  • 12,860
  • 3
  • 34
  • 61
  • How is this "going against the concept of type-safe enum"? – Silverspur Oct 04 '17 at 22:28
  • 1
    Well, you are giving up a bit of type safety by casting here. My old guts' feelings tell me that it might be not the last place where you'd want to cast. Wild guess, but next day, or somewhere along the way, you will want to check if one of the OR`ed flags is set, right? Here comes AND. Then you want to pass it to C API and will implement a conversion. This way little by little you will give it up anyway and will end up with a messy "C+" hackery. But you know your project and your feeling of discipline better, so in the end it's up to you to decide. – Ivan Aksamentov - Drop Oct 04 '17 at 22:41
1

What about casting the values of Elem enum?

        first = int(Elem::A) | int(Elem::B),
        second = int(Elem::A) | int(Elem::C),
max66
  • 65,235
  • 10
  • 71
  • 111
1

You need a static cast to the integral type corresponding to enumerator so there will be built-in operator |:

class MyClass
{
    public: enum class Elem: unsigned int
    {
        A=1, B=2, C=4, D=8
    };
    public: enum class Group: unsigned int
    {
        first  = static_cast<unsigned int>(Elem::A) | static_cast<unsigned int>(Elem::B)
    ,   second = static_cast<unsigned int>(Elem::A) | static_cast<unsigned int>(Elem::C)
    };
};
user7860670
  • 35,849
  • 4
  • 58
  • 84