1

I have an enum class I use for bit masking, like so (in Unreal Engine, therefore the uint8 type)

enum class Level : uint8
{
    None = 0x0,
    Debug = 0x1,
    Info = 0x2,
    Warning = 0x4,
    ...
}

I have added inline operators for |, & and ^ such that I can use a member as the actual bitmask (e.g. mask = Level::Warning | Level::Debug), like so:

inline Level operator&(const Level& lhs, const Level& rhs)
{
    return (Level)((uint8)lhs & (uint8)rhs);
}

Now at some point I want to query, whether a bit is set or not. However, this does not seem to work without casting to uint8:

if(mask & Level::Debug) //does not work, C2451
if((uint8)(mask & Level::Debug)) //does work

Is there a way to make the query work without having to cast it to the underlying type?

EDIT: The goal is to keep the calling code as short (and readable) as possible. The meaning of & in this case seems to be as clear as using a call such as an extra any method as suggested in a similar question. Adding the == Level::Debug code also works of course, but would not shorten the code used. Changing the return type of the operator works, but basically shifts the problem to assignment like Level myLevel = Level::Debug | Level::Warning, so I wouldn't overall improve my code imo.

21
  • Change the operator to return the underlying type? Commented Feb 15, 2021 at 9:10
  • That was too obvious for me :) thanks. However, in that case something like Level myLevel = Level::Debug | Level::Warning does not work anymore. I will then have to decide which of these two I am more willing to drop, I guess? Commented Feb 15, 2021 at 9:15
  • 1
    Does this answer your question? How can I use an enum class in a boolean context? Commented Feb 15, 2021 at 9:20
  • 1
    @Tare Plain enums can lead to name collisions and are error prone because of the implicit conversions. Commented Feb 15, 2021 at 9:40
  • 1
    In current case, you might do: (mask & Level::Debug) == Level::Debug. Commented Feb 15, 2021 at 9:53

1 Answer 1

2

You need operator bool - but operator bool can only be defined for class type. So - define class - and put your enum within it, like this:

class Level
{

public:
    enum Value : std::uint8_t
    {
        None = 0x0,
        Debug = 0x1,
        Info = 0x2,
        Warning = 0x4,
        Error = 0x8
    };
    constexpr Level(Value value) noexcept : value(value) {}
    
    friend constexpr bool operator == (Level lhs, Level rhs)
    { return lhs.value == rhs.value; } 
    friend constexpr bool operator != (Level lhs, Level rhs)
    { return lhs.value != rhs.value; } 
    
    friend constexpr Level operator & (Value lhs, Value rhs)
    {
        return Value(static_cast<uint8_t>(lhs) & static_cast<uint8_t>(rhs));
    }
    friend constexpr Level operator & (Level lhs, Level rhs)
    {
        return lhs.value & rhs.value;
    }
    constexpr explicit operator bool() const noexcept { return value != Value::None; }
    
private:
    Value value;
};


Working demo.

Use like this:

int main() {
    Level l = Level::None;
    if (l & Level::Info) return -1;
}

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

5 Comments

I have tried this and there are two problems I have right now: 1.) I have added the | operator practically the same way you suggest the & operator. Still, assigning a value like Level l = Level::Debug | Leve::Warning; does not seem to work (C2664 cannot convert argument 1 from 'int' to 'Level::Value'. 2) This does not work with switch (C2450 switch expression of type ' Level' is illegal). Is ther any way around this?
You're right - I modified the response. It seems it is needed 2 implementations for each &,|,^,~ operators - for Value and Level as in my answer.
That did indeed solve the assignment problem. However the switch problem still remains. I had assumed that putting a uint8 operator (similar to the bool operator) would do the trick (and it might have), but it then seems to clash with the newly added | operator. In any case the assignments then complain about "3 similar operators"
if you want to do sth like this switch(level) { case Level::None: ... }; add special method constexpr Value getValue() const { return value; } - switch(level.getValue()) { case Level::None: ... };
And it is possible you'd need operator == and != - I just added them to answer . In c++20 - just do bool operator == (Level) const = default;

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.