Re: Вот еще бесит отсутствие флагового типа
От: andrey.desman  
Дата: 05.10.22 17:57
Оценка: 7 (3) +1
Здравствуйте, Marty, Вы писали:

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


Примерно так:

  flags.h
#pragma once

#include <initializer_list>
#include <type_traits>

template<typename BitEnum>
class Flags
{
  using UnderlyingType = std::underlying_type_t<BitEnum>;

  template<typename T1, typename... REST>
  static constexpr UnderlyingType makeMask(T1 first, REST... rest)
  {
    static_assert((std::is_same_v<T1, BitEnum> && ... && std::is_same_v<REST, BitEnum>), "Arguments must be of enum type");
    return (UnderlyingType(first) | ... | UnderlyingType(rest));
  }

public:
  constexpr Flags() = default;

  /// Constructs a flag set by combining/disjuncting its' arguments.
  template<typename T1, typename... REST>
  constexpr Flags(T1 first, REST... rest)
    : m_flags(makeMask(first, rest...))
  {
  }

  /// Constructs a flag set by combining/disjuncting flags in passed initializer list.
  constexpr Flags(std::initializer_list<BitEnum> list)
  {
    for (auto flag : list)
      m_flags |= UnderlyingType(flag);
  }

  /// Tells whether any of specified flags is set.
  template<typename T1, typename... REST>
  constexpr bool any(T1 first, REST... rest) const
  {
    return bool(m_flags & makeMask(first, rest...));
  }

  /// Tells whether all of specified flags are set.
  template<typename T1, typename... REST>
  constexpr bool all(T1 first, REST... rest) const
  {
    const auto mask = makeMask(first, rest...);
    return (m_flags & mask) == mask;
  }

  /// Tells whether none of specified flags are set.
  template<typename T1, typename... REST>
  constexpr bool none(T1 first, REST... rest) const
  {
    const auto mask = makeMask(first, rest...);
    return !(m_flags & mask);
  }

  /// Sets specified flags.
  template<typename T1, typename... REST>
  Flags& set(T1 first, REST... rest)
  {
    m_flags |= makeMask(first, rest...);
    return *this;
  }

  /// Unsets specified flags.
  template<typename T1, typename... REST>
  Flags& reset(T1 first, REST... rest)
  {
    m_flags &= ~makeMask(first, rest...);
    return *this;
  }

  /// Unsets all flags.
  Flags& clear()
  {
    m_flags = 0;
    return *this;
  }

  constexpr explicit operator bool() const
  {
    return m_flags != 0;
  }

  constexpr bool operator!() const
  {
    return m_flags == 0;
  }

  constexpr operator BitEnum() const
  {
    return BitEnum(m_flags);
  }

  constexpr Flags operator&(Flags r)
  {
    return Flags{BitEnum(m_flags & r.m_flags)};
  }

  constexpr friend Flags operator&(BitEnum l, Flags r)
  {
    return Flags(l) & r;
  }

  constexpr Flags operator|(Flags r)
  {
    return Flags{BitEnum(m_flags | r.m_flags)};
  }

  constexpr friend Flags operator|(BitEnum l, Flags r)
  {
    return Flags(l) | r;
  }

  Flags& operator&=(Flags r)
  {
    m_flags &= r.m_flags;
    return *this;
  }

  Flags& operator|=(Flags r)
  {
    m_flags |= r.m_flags;
    return *this;
  }

private:
  UnderlyingType m_flags = 0;
};


#include "flags.h"
#include <cassert>

enum class Fruit
{
    Apple  = 0b0001,
    Orange = 0b0010,
    Kiwi   = 0b0100,
    Pear   = 0b1000,
};

using FruitFlags = Flags<Fruit>;

int main()
{
    FruitFlags kompot = {Fruit::Apple, Fruit:: Pear};

    assert(kompot & Fruit::Apple);
    assert(Fruit::Orange & (kompot | Fruit::Orange));
    assert(kompot.none(Fruit::Kiwi, Fruit::Orange));
    assert(kompot.all(Fruit::Apple, Fruit::Pear));
    assert(kompot == FruitFlags(Fruit::Apple, Fruit::Pear));

    kompot |= Fruit::Orange;
    assert(kompot & Fruit::Orange);
    kompot &= Fruit::Apple;
    kompot = Fruit::Apple & kompot;
    assert(kompot.any(Fruit::Apple, Fruit::Kiwi, Fruit::Orange));
    assert(kompot.all(Fruit::Apple));
    assert(kompot.none(Fruit::Kiwi, Fruit::Orange, Fruit::Pear));

    kompot.set(Fruit::Kiwi, Fruit::Orange, Fruit::Pear).reset(Fruit::Apple);
    assert(kompot.all(Fruit::Kiwi, Fruit::Orange, Fruit::Pear));
    assert(kompot.none(Fruit::Apple));

    return 0;
}


Можно переделать под порядковый (0, 1, 2, 3, ...) энум. Можно под большой статический, а можно под динамический (на базе boost::dynamic_bitset, например).
Средства есть. Зачем их в язык тянуть?
Отредактировано 05.10.2022 23:04 andrey.desman . Предыдущая версия . Еще …
Отредактировано 05.10.2022 17:59 andrey.desman . Предыдущая версия .
Вот еще бесит отсутствие флагового типа
От: 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: Вот еще бесит отсутствие флагового типа
От: Videoman Россия https://hts.tv/
Дата: 07.09.22 09:29
Оценка: 4 (1)
Здравствуйте, Marty, Вы писали:

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


Было
Автор: Videoman
Дата: 26.05.18
же уже. Такое тебе нужно?
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[8]: Вот еще бесит отсутствие флагового типа
От: vopl Россия  
Дата: 30.09.22 18:33
Оценка: :)
Здравствуйте, Marty, Вы писали:

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


M>А раз ты такой умный, может подскажешь, как сделать, чтобы работало if (a&b) работало?


Неа Только всякие извраты типа таких
enum class F {};

F operator&(F, F);
int operator+(F);
bool operator!(F);

int main()
{

    F f1, f2;

    if(+(f1&f2))
    {
        // blabla
    }

    if(!!(f1&f2))
    {
        // blabla
    }

    return 0;
}
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[2]: Вот еще бесит отсутствие флагового типа
От: Marty Пират https://www.youtube.com/channel/UChp5PpQ6T4-93HbNF-8vSYg
Дата: 07.09.22 13:49
Оценка:
Здравствуйте, netch80, Вы писали:

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



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

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


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

А раз ты такой умный, может подскажешь, как сделать, чтобы работало if (a&b) работало?
Маньяк Робокряк колесит по городу
Re[8]: Вот еще бесит отсутствие флагового типа
От: vopl Россия  
Дата: 30.09.22 18:20
Оценка:
Здравствуйте, Marty, Вы писали:

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


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


M>Да, спасибо, слона я что-то не приметил. Интересное решение. Интересно, а компиляцию сильно будет замедлять, по сравнению с явно объявленными функциями?


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

V>При умеренном использовании таких "флагов" — вряд ли будет заметно. Если только их нагенерировать тыщами...


Тыщами наверное нет, но я их хочу в контроллерах использовать, а там всяких битовых флагов порядочно, десятки, может сотни
Маньяк Робокряк колесит по городу
Re[2]: Вот еще бесит отсутствие флагового типа
От: Pzz Россия https://github.com/alexpevzner
Дата: 30.09.22 18:56
Оценка:
Здравствуйте, Stanislav V. Zudin, Вы писали:

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

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

Ну, видимо такой enum, у которого значения не 0, 1, 2, 3, 4, ..., а 1, 2, 4, 8, 16, ...
Re[8]: Вот еще бесит отсутствие флагового типа
От: rg45 СССР  
Дата: 05.10.22 20:09
Оценка:
Здравствуйте, Marty, Вы писали:

M>А раз ты такой умный, может подскажешь, как сделать, чтобы работало if (a&b) работало?


if(bool(a&b)) не нравится?
--
Не можешь достичь желаемого — пожелай достигнутого.
Отредактировано 05.10.2022 20:12 rg45 . Предыдущая версия .
Re[9]: Вот еще бесит отсутствие флагового типа
От: Marty Пират https://www.youtube.com/channel/UChp5PpQ6T4-93HbNF-8vSYg
Дата: 05.10.22 20:20
Оценка:
Здравствуйте, rg45, Вы писали:

M>>А раз ты такой умный, может подскажешь, как сделать, чтобы работало if (a&b) работало?


R>if(bool(a&b)) не нравится?


Конечно не нравится. Тот же if (!!(a&b)) — и тот лучше
Маньяк Робокряк колесит по городу
Re: Вот еще бесит отсутствие флагового типа
От: sergii.p  
Дата: 06.10.22 08:30
Оценка:
Здравствуйте, Marty, Вы писали:

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


на одном из проектов использовал что-то типа такого

  Код
namespace mask
{
    namespace internal
    {
        template<typename Element>
        constexpr size_t offset(Element const el, Element const base = static_cast<Element>(0))
        {
            const auto diff = toe::cast(el) - toe::cast(base);
            DEBUG_ASSERT(diff >= 0, "Offset can't be negative. Probably base element was set incorrect");
            return diff >= 0 ? static_cast<size_t>(diff) : 0;
        }


        template<typename Enum>
        constexpr Enum clearRightMostBit(Enum const val)
        {
            return static_cast<Enum>((toe::cast(val) - 1) & toe::cast(val));
        }

        template<typename Enum>
        constexpr Enum findRightMostBit(Enum const val)
        {
            return static_cast<Enum>(toe::cast(val) ^ toe::cast(clearRightMostBit(val)));
        }

        template<typename Element, typename Mask>
        constexpr Element transformMaskToValue(Mask const mask, Element const base, const size_t index = 0)
        {
            const auto maskVal = toe::cast(mask);
            if (maskVal == 0) {
                throw std::runtime_error{ "transformMaskToValue was called on the end element of range (mask is 0)" };
            }
            return (maskVal & 1) == 0
                ? transformMaskToValue<Element>(static_cast<Mask>(maskVal >> 1), base, index + 1)
                : static_cast<Element>(index + toe::cast(base));
        }
    }

    template<typename Element, typename Mask>
    constexpr Mask set(Mask const mask, Element const el, Element const base = static_cast<Element>(0))
    {
        return static_cast<Mask>(toe::cast(mask) | 1 << internal::offset(el, base));
    }

    namespace internal
    {
        template<typename Element, typename Mask>
        struct range
        {
            using value_type = Element;
            struct iterator
            {
                using iterator_category = std::input_iterator_tag;
                using value_type = typename range::value_type;
                using difference_type = std::ptrdiff_t;
                using pointer = value_type*;
                using reference = value_type;

                explicit iterator(const Mask mask_, Element const base_)
                    : mask{ mask_ }
                    , base{ base_ }
                {}
                iterator& operator++() { mask = clearRightMostBit(mask); return *this; }
                iterator operator++(int) { iterator retval = *this; ++(*this); return retval; }
                bool operator==(iterator other) const noexcept { return mask == other.mask; }
                bool operator!=(iterator other) const noexcept { return !(*this == other); }
                reference operator*() const
                {
                    return transformMaskToValue<Element>(mask, base);
                }

            private:
                Mask mask;
                Element base;
            };

            using const_iterator = iterator;

            range(Mask const mask_, Element const base_)
                : mask{mask_}
                , base{ base_ }
            {}
            range() = default;
            iterator begin() const { return iterator{ mask, base }; }
            iterator end() const { return iterator{ static_cast<Mask>(0), base }; }

        private:
            Mask mask;
            Element base;
        };
    }

    // base is a first element which is represented by first bit in mask
    template<typename Element, typename Mask>
    internal::range<Element, Mask> split(Mask const mask, Element const base = static_cast<Element>(0))
    {
        return internal::range<Element, Mask>{ mask, base };
    }

    template<typename Element, typename Mask>
    constexpr bool contains(Mask const mask, Element const el, Element const base) noexcept
    {
        return (toe::cast(mask) & (1 << internal::offset(el, base))) != 0;
    }
}


тут используется функция toe::cast. Это аналог std::underlying_type в С++20


использование:
enum class Fruit{
    Apple,
    Orange,
    Lemon
};

enum class Mixture{ Empty };

const Mixture only_orange = mask::set(Mixture::Empty, Fruit::Orange);
const Mixture mixture = mask::set(only_orange, Fruit::Lemon);

for(const auto ingredient: mixture) {
    ...
}
const bool has_apple = mask::contains(mixture, Fruit::Apple);


возможно для TS важна поддержка операторов | и &. Для этого я применял решение Videoman. Правда в данном случае это вообще невозможно, потому как для маски и элемента используются разные типы (что кмк более корректно).
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.