最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

c++ - Make bitmask from enum definition - Stack Overflow

programmeradmin0浏览0评论

How can the following MakeMask function be implemented?

Say I have a scoped enum that represents a set of bitflags. I want to create a consteval function that returns a bitmask of that enum. This bitmask represents all the bits present in the given enum.

We can get the enum type using std::underlying_type, but we can just assume uint32_t for the discussion.

enum class Flags : uint32_t {
    A = 1 << 0,
    B = 1 << 1,
    C = 1 << 2,

    X = 1 << 7,
};


template<typename T>
consteval uint32_t MakeMask() {
    // ...
}

// M is a bitmask with elements A, B, C, and X enabled.
constexpr uint32_t M = MakeMask<Flags>();

// M is like: 0b0100_0111

How can the following MakeMask function be implemented?

Say I have a scoped enum that represents a set of bitflags. I want to create a consteval function that returns a bitmask of that enum. This bitmask represents all the bits present in the given enum.

We can get the enum type using std::underlying_type, but we can just assume uint32_t for the discussion.

enum class Flags : uint32_t {
    A = 1 << 0,
    B = 1 << 1,
    C = 1 << 2,

    X = 1 << 7,
};


template<typename T>
consteval uint32_t MakeMask() {
    // ...
}

// M is a bitmask with elements A, B, C, and X enabled.
constexpr uint32_t M = MakeMask<Flags>();

// M is like: 0b0100_0111
Share Improve this question edited Jan 30 at 16:00 Dess asked Jan 30 at 15:53 DessDess 2,67927 silver badges41 bronze badges 6
  • 1 Add mask = (1 << 18) - 1 to the end of the enum, and you're done! Or better mask [[maybe_unused]] = (1 << 18) - 1. – HolyBlackCat Commented Jan 30 at 16:01
  • There is no way to get all enumerators from an enum in standard C++. magic_enum uses a hack to work around that, but, as all hacks, it works in specific conditions only. – Yksisarvinen Commented Jan 30 at 16:02
  • C++ doesn't have reflection so you can't query the enum and get its enumerators. There isn't a way to make this generic without a reflection mechanism. – NathanOliver Commented Jan 30 at 16:02
  • This is impossible until C++26 with the static reflection. Nowadays you can use boost or other reflection libraries, they require special enum definitions or unportable std::source_location. – 3CxEZiVlQ Commented Jan 30 at 16:03
  • 1 Despite hundreds of examples online to the contrary : enums are NOT bitmasks, you can use a namespace with constexpr std::uint32_t values. Or you can make a class with bitmask operations and constexpr constants as members and a cast operator to std::uint32_t if you feel so inclined (which will actually give you some strong typing too). And then there is std::bitset which also might help you out – Pepijn Kramer Commented Jan 30 at 16:28
 |  Show 1 more comment

2 Answers 2

Reset to default 0

An alternative solution would be to have your mask within your enum itself.

enum class Flags : uint32_t {
    A    = 1 << 0,
    B    = 1 << 1,
    C    = 1 << 2,
    X    = 1 << 7,    
    MASK = A + B + C + X
};

It's much simpler than many think. You just need good old <bitset>. But I would blend some C++20 spice in first:

#include <concepts>
#include <type_traits>

template<typename E>
concept flags_enumeration =
    ( std::is_enum_v<T>
    & requires{
       { std::to_underlying(E::last_flag) }
       ->std::unsigned_integral;
       requires (std::to_underlying(E::last_flag) > 0);
       requires (std::to_underlying(E::last_flag) < 64 );
};

Now I can decorate std::bitset:

#include <bitset>

template<flags_enumeration FE>
struct flagset{
   std::bitset<std::to_underlying(E::last_flag)> value;

    constexpr auto& set(std::same_as<FE> ...fe){
        if (sizeof...(fe))
           (void)(value.set(std::to_underlying(fe)), ...);
        else
           value.set());
        return *this;
    };//set

constexpr auto& reset(std::same_as<FE> ...fe){
        if (sizeof...(fe))
           (void)(value.reset(std::to_underlying(fe)), ...);
        else
           value.reset());
        return *this;
    };//reset

constexpr auto& flip(std::same_as<FE> ...fe){
        if (sizeof...(fe))
           (void)(value.flip(std::to_underlying(fe)), ...);
        else
           value.flip());
        return *this;
    };//flip

constexpr bool test(std::same_as<FE> ...fe) const {
        return (sizeof...(fe))
             ? (value.test(std::to_underlying(fe)) and ...);
             : value.all();
    };//test
};//flagset

The above snippet defines flagset as an aggregate, because I want to avoid the wrapping the full set of operations available on `std::bitset'. But it is possible to provide a more complete class. Its Usage would be very simple:

enum class my_flags
:    std::uint16_t{
     flag1,
     flag2,
     flag3,
     last_flag = flag3
};//enum class my flags;

flagset<my_flags> flg;
flg.set(my_flags::flag1,my_flags::flag3);
flagset<my_flags> flg2;

flg2.value |= flg.value;

auto intflags = flg2.value.to_ulonglong();

Note that no member of the enumeration needs an enumerator value; last_flag is the only exception, and marks the end of flags. Each flag is eventually an automatically generated bit index.

The flags_enumeration concept defines valid enumerations as being unsigned and having a none-zero last_flag below the bitdepth of unsigned long long returned by the bitset::to_ulonglong method. A better design would use <limits> instead of magic number 64, but that would complicate the example snippet here.

发布评论

评论列表(0)

  1. 暂无评论