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

Сообщение Re: Вот еще бесит отсутствие флагового типа от 05.10.2022 17:57

Изменено 05.10.2022 23:04 andrey.desman

Re: Вот еще бесит отсутствие флагового типа
Здравствуйте, 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.
  constexpr 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, например).
Средства есть. Зачем их в язык тянуть?
Re: Вот еще бесит отсутствие флагового типа
Здравствуйте, 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, например).
Средства есть. Зачем их в язык тянуть?