gpt4 book ai didi

c++ - 追求更好的位标志枚举

转载 作者:IT老高 更新时间:2023-10-28 22:15:26 29 4
gpt4 key购买 nike

好的,所以我们在 C++ 17 中,对于 C++ 中非常棒的 bitflags 接口(interface)仍然没有令人满意的答案。

我们有 enum将它们的成员值渗入封闭范围,但隐式转换为它们的基础类型,因此可以用作位标志,但拒绝重新分配回枚举而不进行转换。

我们有 enum class这解决了名称范围问题,以便它们的值必须显式命名 MyEnum::MyFlag甚至 MyClass::MyEnum::MyFlag ,但它们不会隐式转换为它们的底层类型,因此不能在没有无休止的来回转换的情况下用作位标志。

最后,我们有来自 C 的旧位域。如:

struct FileFlags {
unsigned ReadOnly : 1;
unsigned Hidden : 1;
...
};

其缺点是没有将自身作为一个整体进行初始化的好方法 - 必须求助于使用 memset 或强制转换地址或类似的方法来覆盖整个值或一次全部初始化或以其他方式一次操作多个位。它也无法命名给定标志的值,而不是它的地址 - 所以没有代表 0x02 的名称,而在使用枚举时有这样的名称,因此使用枚举很容易命名组合标志,例如 FileFlags::ReadOnly | FileFlags::Hidden - 对于位域来说,根本没有一个好方法。

另外我们还有简单的 constexpr#define命名位值,然后根本不使用枚举。这有效,但将位值与基础位标志类型完全分离。也许这最终不是最糟糕的方法,特别是如果位标志值是 constexpr在一个结构中给他们自己的名字范围?
struct FileFlags {
constexpr static uint16_t ReadOnly = 0x01u;
constexpr static uint16_t Hidden = 0x02u;
...
}

所以,就目前而言,我们有很多技巧,但没有一种技巧可以说是一种真正可靠的表达方式

Here is a type which has the following valid bit-flags in it, it has its own name-scope, and these bits and type should be freely usable with standard bitwise operators such as | & ^ ~, and they should be comparable to integral values such as 0, and the result of any bitwise operators should remain the named type, and not devolve into an integral



综上所述,有许多尝试在 C++ 中尝试生成上述实体 -
  • Windows 操作系统团队开发了一个简单的宏,该宏生成 C++ 代码以在给定的枚举类型上定义必要的缺失运算符 DEFINE_ENUM_FLAG_OPERATORS(EnumType)然后定义运算符 | & ^ ~ 以及相关的赋值操作,例如 |= 等。
  • 'grisumbras' 有一个公共(public) GIT 项目,用于启用具有作用域枚举的位标志语义 here , 使用 enable_if元编程以允许给定的枚举转换为位标志类型,该类型支持缺少的运算符并再次静默返回。
  • 在不知道以上内容的情况下,我编写了一个相对简单的 bit_flags 包装器,它在自身上定义了所有按位运算符,以便可以使用 bit_flags<EnumType> flags然后 flags具有按位语义。这不能做的是让枚举的基数实际上直接正确地处理按位运算符,所以你不能说 EnumType::ReadOnly | EnumType::Hidden即使使用 bit_flags<EnumType>因为底层枚举本身仍然不支持必要的运算符。我最终不得不做与上面的 #1 和 #2 基本相同的事情,并启用 operator | (EnumType, EnumType)对于各种按位运算符,要求用户为其枚举声明元类型的特化,例如 template <> struct is_bitflag_enum<EnumType> : std::true_type {};

  • 最终,#1、#2 和 #3 的问题在于,不可能(据我所知)在枚举本身(如#1)上定义缺失的运算符或定义必要的启动器类型(例如 template <> struct is_bitflag_enum<EnumType> : std::true_type {}; 如#2 和部分#3) 在类范围内。这些必须发生在类或结构之外,因为 C++ 根本没有我所知道的允许我在类中进行此类声明的机制。

    所以现在,我希望拥有一组应限定于给定类的标志,但我不能在类头中使用这些标志(例如默认初始化、内联函数等),因为我无法启用任何允许将枚举视为位标志的机制,直到类定义的右大括号之后。或者,我可以在它们所属的类之外定义所有此类标志枚举,以便我可以在用户类定义之前调用“将此枚举设为按位类型”,以充分利用该功能客户端类 - 但现在位标志在外部范围内,而不是与类本身相关联。

    这不是世界末日——以上都不是。但是所有这些都会在编写我的代码时引起无尽的头痛 - 并阻止我以最自然的方式编写它 - 即给定的标志枚举属于(作用域到)该客户端类中的特定类,但带有按位标志-semantics(我的方法 #3 几乎允许这样做 - 只要所有内容都由 bit_flags 包装 - 显式启用所需的按位兼容性)。

    所有这一切仍然让我觉得这可能比现在好得多!

    肯定应该有 - 也许是,但我还没有弄清楚 - 枚举方法可以在它们上启用按位运算符,同时允许它们在封闭的类范围内声明和使用......

    有没有人有我上面没有考虑过的wip或方法,这会让我“在所有可能的世界中做到最好”?

    最佳答案

    您可以在将枚举作为值的封闭类中使用友元函数。这可以在宏中使用来定义必要的功能,所有功能都在类范围内。

    例如,为了避免 is_bitflag_enum trait to specialize,专门化一个包含枚举和操作符的结构体。这就像#2,仍然不能在类里面完成。

    #include <type_traits>

    template<class Tag>
    struct bitflag {
    enum class type;

    #define DEFINE_BITFLAG_OPERATOR(OP) \
    friend constexpr type operator OP(type lhs, type rhs) noexcept { \
    typedef typename ::std::underlying_type<type>::type underlying; \
    return static_cast<type>(static_cast<underlying>(lhs) OP static_cast<underlying>(rhs)); \
    } \
    friend constexpr type& operator OP ## = (type& lhs, type rhs) noexcept { \
    return (lhs = lhs OP rhs); \
    }

    DEFINE_BITFLAG_OPERATOR(|)
    DEFINE_BITFLAG_OPERATOR(&)
    DEFINE_BITFLAG_OPERATOR(^)

    #undef DEFINE_BITFLAG_OPERATOR

    #define DEFINE_BITFLAG_OPERATOR(OP) \
    friend constexpr bool operator OP(type lhs, typename ::std::underlying_type<type>::type rhs) noexcept { \
    return static_cast<typename ::std::underlying_type<type>::type>(lhs) OP rhs; \
    } \
    friend constexpr bool operator OP(typename ::std::underlying_type<type>::type lhs, type rhs) noexcept { \
    return lhs OP static_cast<typename ::std::underlying_type<type>::type>(rhs); \
    }

    DEFINE_BITFLAG_OPERATOR(==)
    DEFINE_BITFLAG_OPERATOR(!=)
    DEFINE_BITFLAG_OPERATOR(<)
    DEFINE_BITFLAG_OPERATOR(>)
    DEFINE_BITFLAG_OPERATOR(>=)
    DEFINE_BITFLAG_OPERATOR(<=)

    #undef DEFINE_BITFLAG_OPERATOR

    friend constexpr type operator~(type e) noexcept {
    return static_cast<type>(~static_cast<typename ::std::underlying_type<type>::type>(e));
    }

    friend constexpr bool operator!(type e) noexcept {
    return static_cast<bool>(static_cast<typename ::std::underlying_type<type>::type>(e));
    }
    };

    // The `struct file_flags_tag` (Which declares a new type) differentiates between different
    // enum classes declared
    template<> enum class bitflag<struct file_flags_tag>::type {
    none = 0,
    readable = 1 << 0,
    writable = 1 << 1,
    executable = 1 << 2,
    hidden = 1 << 3
    };

    using file_flags = bitflag<file_flags_tag>::type;

    bool is_executable(file_flags f) {
    return (f & file_flags::executable) == 0;
    }

    您还可以创建一个宏来定义每个友元函数。这就像#1,但它都在一个类范围内。
    #include <type_traits>

    #define MAKE_BITFLAG_FRIEND_OPERATORS_BITWISE(OP, ENUM_TYPE) \
    friend constexpr ENUM_TYPE operator OP(ENUM_TYPE lhs, ENUM_TYPE rhs) noexcept { \
    typedef typename ::std::underlying_type<ENUM_TYPE>::type underlying; \
    return static_cast<ENUM_TYPE>(static_cast<underlying>(lhs) OP static_cast<underlying>(rhs)); \
    } \
    friend constexpr ENUM_TYPE& operator OP ## = (ENUM_TYPE& lhs, ENUM_TYPE rhs) noexcept { \
    return (lhs = lhs OP rhs); \
    }

    #define MAKE_BITFLAG_FRIEND_OPERATORS_BOOLEAN(OP, ENUM_TYPE) \
    friend constexpr bool operator OP(ENUM_TYPE lhs, typename ::std::underlying_type<ENUM_TYPE>::type rhs) noexcept { \
    return static_cast<typename ::std::underlying_type<ENUM_TYPE>::type>(lhs) OP rhs; \
    } \
    friend constexpr bool operator OP(typename ::std::underlying_type<ENUM_TYPE>::type lhs, ENUM_TYPE rhs) noexcept { \
    return lhs OP static_cast<typename ::std::underlying_type<ENUM_TYPE>::type>(rhs); \
    }


    #define MAKE_BITFLAG_FRIEND_OPERATORS(ENUM_TYPE) \
    public: \
    MAKE_BITFLAG_FRIEND_OPERATORS_BITWISE(|, ENUM_TYPE) \
    MAKE_BITFLAG_FRIEND_OPERATORS_BITWISE(&, ENUM_TYPE) \
    MAKE_BITFLAG_FRIEND_OPERATORS_BITWISE(^, ENUM_TYPE) \
    MAKE_BITFLAG_FRIEND_OPERATORS_BOOLEAN(==, ENUM_TYPE) \
    MAKE_BITFLAG_FRIEND_OPERATORS_BOOLEAN(!=, ENUM_TYPE) \
    MAKE_BITFLAG_FRIEND_OPERATORS_BOOLEAN(<, ENUM_TYPE) \
    MAKE_BITFLAG_FRIEND_OPERATORS_BOOLEAN(>, ENUM_TYPE) \
    MAKE_BITFLAG_FRIEND_OPERATORS_BOOLEAN(>=, ENUM_TYPE) \
    MAKE_BITFLAG_FRIEND_OPERATORS_BOOLEAN(<=, ENUM_TYPE) \
    friend constexpr ENUM_TYPE operator~(ENUM_TYPE e) noexcept { \
    return static_cast<ENUM_TYPE>(~static_cast<typename ::std::underlying_type<ENUM_TYPE>::type>(e)); \
    } \
    friend constexpr bool operator!(ENUM_TYPE e) noexcept { \
    return static_cast<bool>(static_cast<typename ::std::underlying_type<ENUM_TYPE>::type>(e)); \
    }

    // ^ The above in a header somewhere

    class my_class {
    public:
    enum class my_flags {
    none = 0, flag_a = 1 << 0, flag_b = 1 << 2
    };

    MAKE_BITFLAG_FRIEND_OPERATORS(my_flags)

    bool has_flag_a(my_flags f) {
    return (f & my_flags::flag_a) == 0;
    }
    };

    关于c++ - 追求更好的位标志枚举,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49653901/

    29 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com