C++11 standard conformant bitmasks using enum class

后端 未结 5 1127
春和景丽
春和景丽 2021-01-31 03:17

Can you implement standard conformant (as described in 17.5.2.1.3 of the n3242 draft) type safe bitmasks using enum class? The way I read it, a type T is a bitmask if it support

相关标签:
5条回答
  • 2021-01-31 03:55

    I think you can... You'll have to add operators for bitmasky things. I didn't do it here but you could easily overload any relational operator.

      /**
       *
       */
      // NOTE: I changed to a more descriptive and consistent name
      //       This needs to be a real bitmask type.
      enum class file_permissions : int
      {
        no_perms        = 0,
    
        owner_read      =  0400,
        owner_write     =  0200,
        owner_exe       =  0100,
        owner_all       =  0700,
    
        group_read      =   040,
        group_write     =   020,
        group_exe       =   010,
        group_all       =   070,
    
        others_read     =    04,
        others_write    =    02,
        others_exe      =    01,
        others_all      =    07,
    
        all_all     = owner_all | group_all | others_all, // 0777
    
        set_uid_on_exe  = 04000,
        set_gid_on_exe  = 02000,
        sticky_bit      = 01000,
    
        perms_mask      = all_all | set_uid_on_exe | set_gid_on_exe | sticky_bit, // 07777
    
        perms_not_known = 0xffff,
    
        add_perms       = 0x1000,
        remove_perms    = 0x2000,
        symlink_perms   = 0x4000
      };
    
      inline constexpr file_permissions
      operator&(file_permissions x, file_permissions y)
      {
        return static_cast<file_permissions>
          (static_cast<int>(x) & static_cast<int>(y));
      }
    
      inline constexpr file_permissions
      operator|(file_permissions x, file_permissions y)
      {
        return static_cast<file_permissions>
          (static_cast<int>(x) | static_cast<int>(y));
      }
    
      inline constexpr file_permissions
      operator^(file_permissions x, file_permissions y)
      {
        return static_cast<file_permissions>
          (static_cast<int>(x) ^ static_cast<int>(y));
      }
    
      inline constexpr file_permissions
      operator~(file_permissions x)
      {
        return static_cast<file_permissions>(~static_cast<int>(x));
      }
    
      inline file_permissions &
      operator&=(file_permissions & x, file_permissions y)
      {
        x = x & y;
        return x;
      }
    
      inline file_permissions &
      operator|=(file_permissions & x, file_permissions y)
      {
        x = x | y;
        return x;
      }
    
      inline file_permissions &
      operator^=(file_permissions & x, file_permissions y)
      {
        x = x ^ y;
        return x;
      }
    
    0 讨论(0)
  • 2021-01-31 03:56

    Scoped enumerations (those created with either enum class or enum struct) are not classes. They cannot have member functions, they just provide enclosed enumerators (not visible at namespace level).

    0 讨论(0)
  • 2021-01-31 04:01

    A short example of enum-flags below.

    #indlude "enum_flags.h"
    
    ENUM_FLAGS(foo_t)
    enum class foo_t
        {
         none           = 0x00
        ,a              = 0x01
        ,b              = 0x02
        };
    
    ENUM_FLAGS(foo2_t)
    enum class foo2_t
        {
         none           = 0x00
        ,d              = 0x01
        ,e              = 0x02
        };  
    
    int _tmain(int argc, _TCHAR* argv[])
        {
        if(flags(foo_t::a & foo_t::b)) {};
        // if(flags(foo2_t::d & foo_t::b)) {};  // Type safety test - won't compile if uncomment
        };
    

    ENUM_FLAGS(T) is a macro, defined in enum_flags.h (less then 100 lines, free to use with no restrictions).

    0 讨论(0)
  • 2021-01-31 04:14

    I'm not entirely sure what your acceptance criteria are, but you can just make operator & return a wrapper class with appropriate conversions and an explicit operator bool:

    #include <type_traits>
    
    template<typename T> using Underlying = typename std::underlying_type<T>::type;
    template<typename T> constexpr Underlying<T>
    underlying(T t) { return Underlying<T>(t); }
    
    template<typename T> struct TruthValue {
        T t;
        constexpr TruthValue(T t): t(t) { }
        constexpr operator T() const { return t; }
        constexpr explicit operator bool() const { return underlying(t); }
    };
    
    enum class Color { RED = 0xff0000, GREEN = 0x00ff00, BLUE = 0x0000ff };
    constexpr TruthValue<Color>
    operator&(Color l, Color r) { return Color(underlying(l) & underlying(r)); }
    

    All your other operators can continue to return Color, of course:

    constexpr Color
    operator|(Color l, Color r) { return Color(underlying(l) | underlying(r)); }
    constexpr Color operator~(Color c) { return Color(~underlying(c)); }
    
    int main() {
        constexpr Color YELLOW = Color::RED | Color::GREEN;
        constexpr Color WHITE = Color::RED | Color::GREEN | Color::BLUE;
        static_assert(YELLOW == (WHITE & ~Color::BLUE), "color subtraction");
        return (YELLOW & Color::BLUE) ? 1 : 0;
    }
    
    0 讨论(0)
  • 2021-01-31 04:16

    Missing from the list are the operator != and ==

    Those operators are already supported by enumeration types, integer types and std::bitset so there's no need to overload them.

    and to allow sorting one probably also wants to overload <.

    Why do you want to sort bitmasks? Is (a|b) greater than (a|c)? Is std::ios::in less than std::ios::app? Does it matter? The relational operators are always defined for enumeration types and integer types anyway.

    To answer the main question, you would implement & as an overloaded non-member function:

    Foo operator&(Foo l, Foo r)
    {
        typedef std::underlying_type<Foo>::type ut;
        return static_cast<Foo>(static_cast<ut>(l) & static_cast<ut>(r));
    }
    

    I believe all the required operations for bitmask types could be defined for scoped enums, but not the requirements such as

    Ci & Cj is nonzero and Ci & Cj iszero

    and:

    The value Y is set in the object X is the expression X & Y is nonzero.

    Since scoped enums don't support impicit conversion to integer types, you cannot reliably test if it's nonzero or not. You would need to write if ((X&Y) != bitmask{}) and I don't think that's the intention of the committee.

    (I initially thought they could be used to define bitmask types, then remembered I'd tried to implement one using scoped enums and encountered the problem with testing for zero/nonzero.)

    Edit: I've just remembered that std::launch is a scoped enum type and a bitmask type ... so apparently scoped enums can be bitmask types!

    0 讨论(0)
提交回复
热议问题