Search code examples
c++enumsoperatorsenum-class

How to query validity of enum class used as bit mask without casting to underlying type


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.


Solution

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