Вот еще бесит отсутствие флагового типа
От: Marty Пират https://www.youtube.com/channel/UChp5PpQ6T4-93HbNF-8vSYg
Дата: 07.09.22 00:31
Оценка: -1 :)))
Здравствуйте!

Как-то не продумано это. А используется везде и постоянно. За неимением флагов народ использует enum, а в новых плюсиках — enum class, но это же костыли.

Хочется типизированного флагового типа, хз, какое ключевое слово к нему прилепить, может enum class flags bla-bla

Пока обхожусь разными вариантами, в основном enum/enum class, или что-то типа такого:
template<typename UnderlayingType>
struct Flags
{
    typedef UnderlayingType underlaying_type;

    static const UnderlayingType v1 = x1;
    //...

    // Тут конструкторы и преобразователи в UnderlayingType

    // Можно брать значение, как мембер с именем value
    underlaying_type value;

};


А как вы решаете флаговою проблему?
Маньяк Робокряк колесит по городу
Отредактировано 07.09.2022 0:32 Marty . Предыдущая версия .
Re: Вот еще бесит отсутствие флагового типа
От: Stanislav V. Zudin Россия  
Дата: 07.09.22 04:11
Оценка:
Здравствуйте, Marty, Вы писали:

M>Как-то не продумано это. А используется везде и постоянно. За неимением флагов народ использует enum, а в новых плюсиках — enum class, но это же костыли.


M>Хочется типизированного флагового типа, хз, какое ключевое слово к нему прилепить, может enum class flags bla-bla


Непонятно:
1. Что именно ты подразумеваешь под "флагом"
2. Какую задачу хочешь решить.

bool theFlag;

struct Flagzz
{
  unsigned int flagA:1;
  unsigned int flagB:1;
}


Вполне себе флаги и существуют давно. Плюс упомянутые тобой енумы.
_____________________
С уважением,
Stanislav V. Zudin
Re: Вот еще бесит отсутствие флагового типа
От: netch80 Украина http://netch80.dreamwidth.org/
Дата: 07.09.22 05:05
Оценка:
Здравствуйте, Marty, Вы писали:

M> typedef UnderlayingType underlaying_type;


Не по сути вопроса: underlying (от слова lie — лежать), а не underlaying (было бы от lay — класть/ложить).

По сути: похоже, никто не понял, что такое "флаги" в твоём понимании.

Поле — набор битовых флагов?
The God is real, unless declared integer.
Re: Вот еще бесит отсутствие флагового типа
От: Videoman Россия https://hts.tv/
Дата: 07.09.22 09:29
Оценка: 4 (1)
Здравствуйте, Marty, Вы писали:

M>А как вы решаете флаговою проблему?


Было
Автор: Videoman
Дата: 26.05.18
же уже. Такое тебе нужно?
Re[2]: Вот еще бесит отсутствие флагового типа
От: Marty Пират https://www.youtube.com/channel/UChp5PpQ6T4-93HbNF-8vSYg
Дата: 07.09.22 13:49
Оценка:
Здравствуйте, netch80, Вы писали:

N>Поле — набор битовых флагов?



Да, именно, чтобы было типизированное
Маньяк Робокряк колесит по городу
Re[2]: Вот еще бесит отсутствие флагового типа
От: Андрей Тарасевич Беларусь  
Дата: 08.09.22 15:41
Оценка: +1
Здравствуйте, Stanislav V. Zudin, Вы писали:

SVZ>Здравствуйте, Marty, Вы писали:


M>>Как-то не продумано это. А используется везде и постоянно. За неимением флагов народ использует enum, а в новых плюсиках — enum class, но это же костыли.


M>>Хочется типизированного флагового типа, хз, какое ключевое слово к нему прилепить, может enum class flags bla-bla


SVZ>Непонятно:

SVZ>1. Что именно ты подразумеваешь под "флагом"
SVZ>2. Какую задачу хочешь решить.

SVZ>
bool theFlag;

SVZ>struct Flagzz
SVZ>{
SVZ>  unsigned int flagA:1;
SVZ>  unsigned int flagB:1;
SVZ>}
SVZ>


SVZ>Вполне себе флаги и существуют давно.


Это — слишком ограниченная реализация набора флагов, ибо она не поддерживает run-time адресации флагов. Это настолько серьезное ограничение, что такая реализация практически бесполезна.

Любая реализация набора флагов должна поддерживать операции вроде "проверить, выставлен ли хотя бы один флаг по маске", "сбросить все флаги по маске" и т.п. для run-time маски. По этой причине флаги всегда сразу вручную пакуют в целочисленный тип. Реализация набора флагов через битовые поля — антипаттерн, который в какой-то момент обязательно приведет к необходимости переделки кода: выкашиванию битовых полей и использованию целочисленного типа вместо них.
Best regards,
Андрей Тарасевич
Отредактировано 08.09.2022 15:43 Андрей Тарасевич . Предыдущая версия .
Re: Вот еще бесит отсутствие флагового типа
От: Sm0ke Россия ksi
Дата: 08.09.22 19:39
Оценка:
Здравствуйте, Marty, Вы писали:

M>Здравствуйте!


M>Как-то не продумано это. А используется везде и постоянно. За неимением флагов народ использует enum, а в новых плюсиках — enum class, но это же костыли.


M>Хочется типизированного флагового типа, хз, какое ключевое слово к нему прилепить, может enum class flags bla-bla


M>Пока обхожусь разными вариантами, в основном enum/enum class, или что-то типа такого:

M>
template<typename UnderlayingType>
M>struct Flags
M>{
M>    typedef UnderlayingType underlaying_type;

M>    static const UnderlayingType v1 = x1;
M>    //...

M>    // Тут конструкторы и преобразователи в UnderlayingType

M>    // Можно брать значение, как мембер с именем value
M>    underlaying_type value;

M>};


M>А как вы решаете флаговою проблему?


Если я вас правильно понял вы хотите аналог set из MySQL. https://dev.mysql.com/doc/refman/8.0/en/set.html

Я делаю как-то так:
using flags_raw = std::uintmax_t;

enum flags : flags_raw {
    flag_allow_plain    = 1 << 0,
    flag_was_refers        = 1 << 1,
    flag_was_extends    = 1 << 2,
    flag_was_fn_params    = 1 << 3,
    flag_was_colon        = 1 << 4,
};

int main() {
    flags_raw fl = flag_allow_plain | flag_was_colon; // храню в underlying
    return 0;
}

Ограничения на 64 флага пока хватает.
Отредактировано 08.09.2022 19:46 Sm0ke . Предыдущая версия .
Re[3]: Вот еще бесит отсутствие флагового типа
От: sergii.p  
Дата: 09.09.22 15:17
Оценка:
Здравствуйте, Андрей Тарасевич, Вы писали:

АТ>Любая реализация набора флагов должна поддерживать операции вроде "проверить, выставлен ли хотя бы один флаг по маске", "сбросить все флаги по маске" и т.п. для run-time маски. По этой причине флаги всегда сразу вручную пакуют в целочисленный тип. Реализация набора флагов через битовые поля — антипаттерн, который в какой-то момент обязательно приведет к необходимости переделки кода: выкашиванию битовых полей и использованию целочисленного типа вместо них.


н-р чем такая реализация не устраивает?

union BitMask
{
    unsigned int all = 0;
    struct Bits
    {
        unsigned int first: 1;
        unsigned int second: 1;
        unsigned int third: 1;
    } bits;
};


Доступен каждый бит и в тоже время наложить маску никто не запрещает.
Re[4]: Вот еще бесит отсутствие флагового типа
От: vopl Россия  
Дата: 09.09.22 15:22
Оценка:
Здравствуйте, sergii.p, Вы писали:

SP>Здравствуйте, Андрей Тарасевич, Вы писали:


АТ>>Любая реализация набора флагов должна поддерживать операции вроде "проверить, выставлен ли хотя бы один флаг по маске", "сбросить все флаги по маске" и т.п. для run-time маски. По этой причине флаги всегда сразу вручную пакуют в целочисленный тип. Реализация набора флагов через битовые поля — антипаттерн, который в какой-то момент обязательно приведет к необходимости переделки кода: выкашиванию битовых полей и использованию целочисленного типа вместо них.


SP>н-р чем такая реализация не устраивает?


SP>
SP>union BitMask
SP>{
SP>    unsigned int all = 0;
SP>    struct Bits
SP>    {
SP>        unsigned int first: 1;
SP>        unsigned int second: 1;
SP>        unsigned int third: 1;
SP>    } bits;
SP>};
SP>


SP>Доступен каждый бит и в тоже время наложить маску никто не запрещает.


Такой подход образует UB, в соседней теме чуть разбирали, отсюда и вглубь http://rsdn.org/forum/cpp/8343441.1
Автор: Zhendos
Дата: 25.08.22
Re[5]: Вот еще бесит отсутствие флагового типа
От: sergii.p  
Дата: 09.09.22 15:55
Оценка:
Здравствуйте, vopl, Вы писали:

V>Такой подход образует UB, в соседней теме чуть разбирали, отсюда и вглубь http://rsdn.org/forum/cpp/8343441.1
Автор: Zhendos
Дата: 25.08.22


тогда можно воспользоваться std::bitset. У него есть метод to_ulong(). Та же самая функциональность получается.
Re: Вот еще бесит отсутствие флагового типа
От: K13 http://akvis.com
Дата: 09.09.22 16:20
Оценка:
M>Хочется типизированного флагового типа, хз, какое ключевое слово к нему прилепить, может enum class flags bla-bla

https://github.com/Neargye/magic_enum

есть и преобразование value <-> name и список возможных значений и даже автоматизация switch
Re[6]: Вот еще бесит отсутствие флагового типа
От: Андрей Тарасевич Беларусь  
Дата: 09.09.22 16:35
Оценка:
Здравствуйте, sergii.p, Вы писали:

SP>тогда можно воспользоваться std::bitset. У него есть метод to_ulong(). Та же самая функциональность получается.


Но тогда не будет отдельных именованных полей. `std::bitset` — это лишь вариация того, о чем говорю я. (Непонятно, зачем нужная однако.)
Best regards,
Андрей Тарасевич
Re[2]: Вот еще бесит отсутствие флагового типа
От: Marty Пират https://www.youtube.com/channel/UChp5PpQ6T4-93HbNF-8vSYg
Дата: 29.09.22 11:21
Оценка:
Здравствуйте, Stanislav V. Zudin, Вы писали:

SVZ>Непонятно:

SVZ>1. Что именно ты подразумеваешь под "флагом"
SVZ>2. Какую задачу хочешь решить.

Типизировванный набор флагов, чтобы можно было делать перегрузку по этому типу. Сейчас как-то так, например:

void doSomething( SomeContext &ctx, unsigned someFlags);
void doSomething( SomeContext &ctx, unsigned someFlags, unsigned someFlagsEx);

void doSomething2( SomeContext &ctx, unsigned someFlags2);
// ...


Тут можно легко перепутать, куда какие флаги передаются, и компилятор молча схавает.


SVZ>
bool theFlag;

SVZ>struct Flagzz
SVZ>{
SVZ>  unsigned int flagA:1;
SVZ>  unsigned int flagB:1;
SVZ>}
SVZ>


SVZ>Вполне себе флаги и существуют давно. Плюс упомянутые тобой енумы.


Это битовые поля, это полное говно. И енумы тоже говно для флагов
Маньяк Робокряк колесит по городу
Re: Вот еще бесит отсутствие флагового типа
От: Marty Пират https://www.youtube.com/channel/UChp5PpQ6T4-93HbNF-8vSYg
Дата: 29.09.22 12:20
Оценка:
Здравствуйте, Marty, Вы писали:

Вот сейчас опять надо с флагами повозится, думаю, как быть.

1) enum class, + куча свободных функций operator&, operator|, operator^ и тп, в которых кастить с underlying_type, делать операцию, и кастить результат обратно. Из плюсов — оно будет двоично совместимо — т.е. где-то во внешней С-либе есть функция void doSomething(..., unsigned flags), я у себя могу написать прототип, где поменяю тип флагов с unsigned на мой enum class и всё будет работать. Из минусов — будут порождаться значения, которых нет в исходном enum, и это попахивает говнецом

2) Завернуть в структуру как-то так

struct SomeFlags
{
    unsigned value = 0;

    static const unsigned flag1 = 0x01;
    static const unsigned flag2 = 0x02;
    // ...

    explicit SomeFlags(unsigned) ...
    explicit operator unsigned() const ...
    
    // прочие операторы

};


Вроде получше, но не совсем уверен, что можно подменять тип в прототипе внешней сишной функции, хотя вроде такая структура должна по значению передаться как и примитивный тип.

Второе — если куда-то надо передать набор флагов, то придётся писать SomeFlags(SomeFlags::flag1|SomeFlags::flag2), что тоже не очень. В принципе, можно конструктор и оператор преобразования сделать не explicit — нет ли тут граблей? Не будет ли неявного преобразования через underlying_type в другой аналогичный флаговый тип? Тогда получится, что хоть этот тип можно для перегрузки использовать, но все равно можно подсунуть чужие флаги, и получается практически то же самое, от чего хотелось уйти.

Третье — я пока в рамках 0x03 стандарта прикидывал, там нельзя было в классе инициализировать кроме static const интегральных типов. Наверное, если подумать, в рамках стандартов посвежее можно сделать что-то более приятное?

В общем, кто что думает, какой способ лучше? И что надо добавить/убавить к одному из перечисленных вариантов?
Маньяк Робокряк колесит по городу
Re: Вот еще бесит отсутствие флагового типа
От: Marty Пират https://www.youtube.com/channel/UChp5PpQ6T4-93HbNF-8vSYg
Дата: 30.09.22 16:48
Оценка:
Здравствуйте, 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
Маньяк Робокряк колесит по городу
Отредактировано 30.09.2022 16:58 Marty . Предыдущая версия .
Re[2]: Вот еще бесит отсутствие флагового типа
От: vopl Россия  
Дата: 30.09.22 17:24
Оценка:
Здравствуйте, Marty, Вы писали:

M>В общем, у меня такой получился:


ИМХО, у Videoman подход более приятный, более сиплюсплюсный. И его можно и в классах использовать тоже .
Re[3]: Вот еще бесит отсутствие флагового типа
От: Marty Пират https://www.youtube.com/channel/UChp5PpQ6T4-93HbNF-8vSYg
Дата: 30.09.22 17:30
Оценка:
Здравствуйте, vopl, Вы писали:

M>>В общем, у меня такой получился:


V>ИМХО, у Videoman подход более приятный, более сиплюсплюсный. И его можно и в классах использовать тоже .


Ну, вот когда он все проблемы решит, тогда и перейду на его подход
Маньяк Робокряк колесит по городу
Re[4]: Вот еще бесит отсутствие флагового типа
От: vopl Россия  
Дата: 30.09.22 17:33
Оценка:
Здравствуйте, Marty, Вы писали:

M>Здравствуйте, vopl, Вы писали:


M>>>В общем, у меня такой получился:


V>>ИМХО, у Videoman подход более приятный, более сиплюсплюсный. И его можно и в классах использовать тоже .


M>Ну, вот когда он все проблемы решит, тогда и перейду на его подход


Так у него нет проблем, все нормально фурычит. Но, конечно же, хозяин — барин
Re[5]: Вот еще бесит отсутствие флагового типа
От: Marty Пират https://www.youtube.com/channel/UChp5PpQ6T4-93HbNF-8vSYg
Дата: 30.09.22 17:36
Оценка:
Здравствуйте, vopl, Вы писали:

V>Так у него нет проблем, все нормально фурычит. Но, конечно же, хозяин — барин


Как это нет?

V>1. Данных подход очень не удобен в случае если у нас множество вложенных наймспейсов. Каждый раз приходится "лесенкой" выходит их них, а потом объявлять шаблон полностью квалифицируя именя. Типа:
V>2. У нас флаги очень часто используются внутри классов. В этом случае все еще сильнее усложняется.
V>3. Для флагов в защищенных секциях данный подход вообще не работает.

Маньяк Робокряк колесит по городу
Re[6]: Вот еще бесит отсутствие флагового типа
От: vopl Россия  
Дата: 30.09.22 17:44
Оценка:
Здравствуйте, Marty, Вы писали:

M>Здравствуйте, vopl, Вы писали:


V>>Так у него нет проблем, все нормально фурычит. Но, конечно же, хозяин — барин


M>Как это нет?


M>

V>>1. Данных подход очень не удобен в случае если у нас множество вложенных наймспейсов. Каждый раз приходится "лесенкой" выходит их них, а потом объявлять шаблон полностью квалифицируя именя. Типа:
V>>2. У нас флаги очень часто используются внутри классов. В этом случае все еще сильнее усложняется.
V>>3. Для флагов в защищенных секциях данный подход вообще не работает.


Ну так с этой болью он пришел на форум. А ушел — с решением всех этих пунктов, смотри тут http://rsdn.org/forum/cpp/7154646.1
Автор: vopl
Дата: 26.05.18
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.