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?

3
  • coliru.stacked-crooked.com/a/d3c5c6929fe0357e :( I really thought that would work Commented Oct 4, 2017 at 21:39
  • Same issue as with my merge method :) Commented Oct 4, 2017 at 21:49
  • 1
    Seems a little ridiculous. All the needed classes are fully declared :( Commented Oct 4, 2017 at 21:52

3 Answers 3

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?

Sign up to request clarification or add additional context in comments.

2 Comments

How is this "going against the concept of type-safe enum"?
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.
1

What about casting the values of Elem enum?

        first = int(Elem::A) | int(Elem::B),
        second = int(Elem::A) | int(Elem::C),

Comments

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)
    };
};

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.