Информация об изменениях

Сообщение Re: Вот еще бесит отсутствие флагового типа от 30.09.2022 16:48

Изменено 30.09.2022 16:58 Marty

Re: Вот еще бесит отсутствие флагового типа
Здравствуйте, Marty, Вы писали:

В общем, покопал я немножко инет на тему флагов, проблема, как оказалось, зудит не только у меня.

Накопал, что C++ сейчас гарантирует, что все возможные битовые комбинации значений enum'а влезут в underlying тип (если не задан явно). Похоже, что это таки подготовка к тому, чтобы из enum'а сделать флаговый тип просто каким-нибудь атрибутов, или ещё как. Но C++29 или 32, в котором это таки будет реализовано, мне ждать неохота, поэтому я накидал свой макросик (вернее, большую часть нагло стырил где-то тут — https://stackoverflow.com/questions/1448396/how-to-use-enums-as-flags-in-c)

Там, кстати, нашел, что оказывается в winnt.h есть подобный макрос, называется DEFINE_ENUM_FLAG_OPERATORS

В общем, у меня такой получился:
  Скрытый текст
#define MARTY_CPP_MAKE_ENUM_FLAGS(TEnum)                                                           inline constexpr bool operator==(TEnum a, std::underlying_type<TEnum>::type b) {                   using TUnder = typename std::underlying_type<TEnum>::type;                                     return static_cast<TUnder>(a) == b;                                                        }                                                                                              inline constexpr bool operator==(std::underlying_type<TEnum>::type a, TEnum b) {                   using TUnder = typename std::underlying_type<TEnum>::type;                                     return a == static_cast<TUnder>(b);                                                        }                                                                                              inline constexpr bool operator!=(TEnum a, std::underlying_type<TEnum>::type b) {                   using TUnder = typename std::underlying_type<TEnum>::type;                                     return static_cast<TUnder>(a) != b;                                                        }                                                                                              inline constexpr bool operator!=(std::underlying_type<TEnum>::type a, TEnum b) {                   using TUnder = typename std::underlying_type<TEnum>::type;                                     return a != static_cast<TUnder>(b);                                                        }                                                                                              inline constexpr TEnum operator<<(TEnum a, unsigned b) {                                           using TUnder = typename std::underlying_type<TEnum>::type;                                     return static_cast<TEnum>(static_cast<TUnder>(a) << b);                                    }                                                                                              inline constexpr TEnum operator>>(TEnum a, unsigned b) {                                           using TUnder = typename std::underlying_type<TEnum>::type;                                     return static_cast<TEnum>(static_cast<TUnder>(a) >> b);                                    }                                                                                              inline constexpr TEnum operator~(TEnum a) {                                                        using TUnder = typename std::underlying_type<TEnum>::type;                                     return static_cast<TEnum>(~static_cast<TUnder>(a));                                        }                                                                                              inline constexpr TEnum operator|(TEnum a, TEnum b) {                                               using TUnder = typename std::underlying_type<TEnum>::type;                                     return static_cast<TEnum>(static_cast<TUnder>(a) | static_cast<TUnder>(b));                }                                                                                              inline constexpr TEnum operator&(TEnum a, TEnum b) {                                               using TUnder = typename std::underlying_type<TEnum>::type;                                     return static_cast<TEnum>(static_cast<TUnder>(a) & static_cast<TUnder>(b));                }                                                                                              inline constexpr TEnum operator^(TEnum a, TEnum b) {                                               using TUnder = typename std::underlying_type<TEnum>::type;                                     return static_cast<TEnum>(static_cast<TUnder>(a) ^ static_cast<TUnder>(b));                }                                                                                              inline constexpr TEnum& operator<<=(TEnum& a, unsigned b) {                                        using TUnder = typename std::underlying_type<TEnum>::type;                                     a = static_cast<TEnum>(static_cast<TUnder>(a) << b );                                          return a;                                                                                  }                                                                                              inline constexpr TEnum& operator>>=(TEnum& a, unsigned b) {                                        using TUnder = typename std::underlying_type<TEnum>::type;                                     a = static_cast<TEnum>(static_cast<TUnder>(a) >> b );                                          return a;                                                                                  }                                                                                              inline constexpr TEnum& operator|=(TEnum& a, TEnum b) {                                            using TUnder = typename std::underlying_type<TEnum>::type;                                     a = static_cast<TEnum>(static_cast<TUnder>(a) | static_cast<TUnder>(b));                       return a;                                                                                  }                                                                                              inline constexpr TEnum& operator&=(TEnum& a, TEnum b) {                                            using TUnder = typename std::underlying_type<TEnum>::type;                                     a = static_cast<TEnum>(static_cast<TUnder>(a) & static_cast<TUnder>(b));                       return a;                                                                                  }                                                                                              inline constexpr TEnum& operator^=(TEnum& a, TEnum b) {                                            using TUnder = typename std::underlying_type<TEnum>::type;                                     a = static_cast<TEnum>(static_cast<TUnder>(a) ^ static_cast<TUnder>(b));                       return a;                                                                                  }

Используется как-то так:
  Скрытый текст
enum class EWinVkFlags : std::uint16_t
{
    kbdExtended     = 1,
    kbdExt          = 1,
    kbdMultivk      = 2,
    kbdSpecial      = 4,
    kbdNumpad       = 8,
    kbdUnicode      = 16,
    kbdInjectedvk   = 32,
    kbdMappedvk     = 64,
    kbdBreak        = 128

}; // enum class EWinVkFlags : std::uint16_t

MARTY_CPP_MAKE_ENUM_FLAGS(EWinVkFlags)

//...

lout << "enum_serialize_flags: " << virtual_keys::enum_serialize_flags(EWinVkFlags::kbdExt | EWinVkFlags::kbdSpecial) << "\n";

// Выводит: enum_serialize_flags: Special|Extended

Я добавил сдвиг и сравнение (==/!=) с underlying type.

Сдвиг для битовых масок имхо полезен.

Сравнение — чтобы можно было делать что-то типа if ((a&b)==0). Я обычно явно так пишу, но некоторые не пишут, и тут пока есть недоработка — нельзя просто написать if (a&b). Меня особо не парит, но если есть идеи — предлагаю обсудить
Re: Вот еще бесит отсутствие флагового типа
Здравствуйте, Marty, Вы писали:

В общем, покопал я немножко инет на тему флагов, проблема, как оказалось, зудит не только у меня.

Накопал, что C++ сейчас гарантирует, что все возможные битовые комбинации значений enum'а влезут в underlying тип (если не задан явно). Похоже, что это таки подготовка к тому, чтобы из enum'а сделать флаговый тип просто каким-нибудь атрибутов, или ещё как. Но C++29 или 32, в котором это таки будет реализовано, мне ждать неохота, поэтому я накидал свой макросик (вернее, большую часть нагло стырил где-то тут — https://stackoverflow.com/questions/1448396/how-to-use-enums-as-flags-in-c)

Там, кстати, нашел, что оказывается в winnt.h есть подобный макрос, называется DEFINE_ENUM_FLAG_OPERATORS

В общем, у меня такой получился:
  Скрытый текст
#define MARTY_CPP_MAKE_ENUM_FLAGS(TEnum)                                                       \
    inline constexpr bool operator==(TEnum a, std::underlying_type<TEnum>::type b) {           \
        using TUnder = typename std::underlying_type<TEnum>::type;                             \
        return static_cast<TUnder>(a) == b;                                                    \
    }                                                                                          \
    inline constexpr bool operator==(std::underlying_type<TEnum>::type a, TEnum b) {           \
        using TUnder = typename std::underlying_type<TEnum>::type;                             \
        return a == static_cast<TUnder>(b);                                                    \
    }                                                                                          \
    inline constexpr bool operator!=(TEnum a, std::underlying_type<TEnum>::type b) {           \
        using TUnder = typename std::underlying_type<TEnum>::type;                             \
        return static_cast<TUnder>(a) != b;                                                    \
    }                                                                                          \
    inline constexpr bool operator!=(std::underlying_type<TEnum>::type a, TEnum b) {           \
        using TUnder = typename std::underlying_type<TEnum>::type;                             \
        return a != static_cast<TUnder>(b);                                                    \
    }                                                                                          \
    inline constexpr TEnum operator<<(TEnum a, unsigned b) {                                   \
        using TUnder = typename std::underlying_type<TEnum>::type;                             \
        return static_cast<TEnum>(static_cast<TUnder>(a) << b);                                \
    }                                                                                          \
    inline constexpr TEnum operator>>(TEnum a, unsigned b) {                                   \
        using TUnder = typename std::underlying_type<TEnum>::type;                             \
        return static_cast<TEnum>(static_cast<TUnder>(a) >> b);                                \
    }                                                                                          \
    inline constexpr TEnum operator~(TEnum a) {                                                \
        using TUnder = typename std::underlying_type<TEnum>::type;                             \
        return static_cast<TEnum>(~static_cast<TUnder>(a));                                    \
    }                                                                                          \
    inline constexpr TEnum operator|(TEnum a, TEnum b) {                                       \
        using TUnder = typename std::underlying_type<TEnum>::type;                             \
        return static_cast<TEnum>(static_cast<TUnder>(a) | static_cast<TUnder>(b));            \
    }                                                                                          \
    inline constexpr TEnum operator&(TEnum a, TEnum b) {                                       \
        using TUnder = typename std::underlying_type<TEnum>::type;                             \
        return static_cast<TEnum>(static_cast<TUnder>(a) & static_cast<TUnder>(b));            \
    }                                                                                          \
    inline constexpr TEnum operator^(TEnum a, TEnum b) {                                       \
        using TUnder = typename std::underlying_type<TEnum>::type;                             \
        return static_cast<TEnum>(static_cast<TUnder>(a) ^ static_cast<TUnder>(b));            \
    }                                                                                          \
    inline constexpr TEnum& operator<<=(TEnum& a, unsigned b) {                                \
        using TUnder = typename std::underlying_type<TEnum>::type;                             \
        a = static_cast<TEnum>(static_cast<TUnder>(a) << b );                                  \
        return a;                                                                              \
    }                                                                                          \
    inline constexpr TEnum& operator>>=(TEnum& a, unsigned b) {                                \
        using TUnder = typename std::underlying_type<TEnum>::type;                             \
        a = static_cast<TEnum>(static_cast<TUnder>(a) >> b );                                  \
        return a;                                                                              \
    }                                                                                          \
    inline constexpr TEnum& operator|=(TEnum& a, TEnum b) {                                    \
        using TUnder = typename std::underlying_type<TEnum>::type;                             \
        a = static_cast<TEnum>(static_cast<TUnder>(a) | static_cast<TUnder>(b));               \
        return a;                                                                              \
    }                                                                                          \
    inline constexpr TEnum& operator&=(TEnum& a, TEnum b) {                                    \
        using TUnder = typename std::underlying_type<TEnum>::type;                             \
        a = static_cast<TEnum>(static_cast<TUnder>(a) & static_cast<TUnder>(b));               \
        return a;                                                                              \
    }                                                                                          \
    inline constexpr TEnum& operator^=(TEnum& a, TEnum b) {                                    \
        using TUnder = typename std::underlying_type<TEnum>::type;                             \
        a = static_cast<TEnum>(static_cast<TUnder>(a) ^ static_cast<TUnder>(b));               \
        return a;                                                                              \
    }

Используется как-то так:
  Скрытый текст
enum class EWinVkFlags : std::uint16_t
{
    kbdExtended     = 1,
    kbdExt          = 1,
    kbdMultivk      = 2,
    kbdSpecial      = 4,
    kbdNumpad       = 8,
    kbdUnicode      = 16,
    kbdInjectedvk   = 32,
    kbdMappedvk     = 64,
    kbdBreak        = 128

}; // enum class EWinVkFlags : std::uint16_t

MARTY_CPP_MAKE_ENUM_FLAGS(EWinVkFlags)

//...

lout << "enum_serialize_flags: " << virtual_keys::enum_serialize_flags(EWinVkFlags::kbdExt | EWinVkFlags::kbdSpecial) << "\n";

// Выводит: enum_serialize_flags: Special|Extended

Я добавил сдвиг и сравнение (==/!=) с underlying type.

Сдвиг для битовых масок имхо полезен.

Сравнение — чтобы можно было делать что-то типа if ((a&b)==0). Я обычно явно так пишу, но некоторые не пишут, и тут пока есть недоработка — нельзя просто написать if (a&b). Меня особо не парит, но если есть идеи — предлагаю обсудить


ЗЫ Кому надо — лежит тут — https://github.com/al-martyn1/marty_cpp