方便的标志处理,其中所有标志都不适合64位

时间:2017-10-24 11:45:54

标签: c++

我想在C ++中创建一个方便的标志处理程序。

通常的想法是使用这样的东西:

enum class Flag {
    Foo = 1<<0,
    Bar = 1<<1,
    Baz = 1<<2
};

// these operators can be generalized by using template functions with SFINAE
Flag operator|(Flag a, Flag b) {
    return static_cast<Flag>(static_cast<underlying_type>(a)|static_cast<underlying_type>(b));
}
// add other operators here, like &, ^, |=, etc.

这有效,但我有两个问题:

  • Flag的使用方式不一定是一个标志,而是几个标志。这不是一个大问题,但我不认为这个好设计,因为Flag现在实际上存储了一个未列为枚举成员的值。如果我将运算符的返回值更改为underlying_type,那么我会丢失有关它存储Flag
  • 标记的信息
  • 它不能扩展:如果我有超过64个标志,此解决方案不再起作用

此处理程序的另一个要求是不仅能够处理1位标志,而且能够处理几位宽的枚举(使用这些,很容易达到64位限制),我想要控制如何在处理程序之间分配位(我的意思是,例如,我想使用此处理程序来简化处理OS调用,其中位位置由API确定)。

这个问题有哪些(方便使用,最佳,一般)解决方案?

2 个答案:

答案 0 :(得分:1)

只需使用std::vector<bool>将标志存储为位(以这种方式实现)。如果位域的大小是固定的 - 使用std::bitset

答案 1 :(得分:1)

这就是我想出的。这只是一个概念,缺少许多功能(多位标志,其他运算符等),但可以轻松添加。

以下是一个使用示例:

enum class Group1 {
    AttrA,
    AttrB,
    AttrC,
};

// count tells to Flags how many entries are there.
// no other boilerplate code is needed
constexpr int count(Group1) {
    return 3;
}

enum class Group2 {
    AttrA,
    AttrB,
};

constexpr int count(Group2) {
    return 2;
}

int main() {
    Flags<Group1, Group2> flags; // flags will have 5 used bits in it
    flags |= Group1::AttrA;
    flags |= Group1::AttrC;

    flags = Group2::AttrB|Group1::AttrA;
}

以下是解决方案:

#include <type_traits>
#include <cstdint>

namespace Private {

template <typename ...FLAGS>
struct FlagsSize;

template <typename FIRST, typename ...TAIL>
struct FlagsSize<FIRST, TAIL...> {
    static constexpr int value = count(FIRST()) + FlagsSize<TAIL...>::value;
};

template <>
struct FlagsSize<> {
    static constexpr int value = 0;
};

template <typename ELEMENT, typename ...FLAGS>
struct FlagOffset;

template <typename ELEMENT, typename ...FLAGS>
struct FlagOffset<ELEMENT, ELEMENT, FLAGS...> {
    static constexpr int value = 0;
};

template <typename ELEMENT, typename FIRST, typename ...TAIL>
struct FlagOffset<ELEMENT, FIRST, TAIL...> {
    static constexpr int value = count(FIRST()) + FlagOffset<ELEMENT, TAIL...>::value;
};

}

template <typename ...FLAGS>
class Flags {
    public:
        static const std::size_t size = Private::FlagsSize<FLAGS...>::value;
    private:
        std::uint8_t m_storage[(size+7)/8];
    public:
        Flags() {
            clearAll();
        }

        void clearAll() {
            for (std::size_t i=0; i<sizeof(m_storage); i++) {
                m_storage[i] = 0;
            }
        }
        bool get(int index) const {
            return m_storage[index>>3]&(1<<(index&7));
        }
        void set(int index, bool value = true) {
            if (value) {
                m_storage[index>>3] |= 1<<(index&7);
            } else {
                m_storage[index>>3] &= ~(1<<(index&7));
            }
        }

        template <typename FLAG, typename = std::enable_if_t<std::is_enum_v<FLAG>>>
        bool get(FLAG flag) const {
            return get(Private::FlagOffset<FLAG, FLAGS...>::value+static_cast<std::underlying_type_t<FLAG>>(flag));
        }
        template <typename FLAG, typename = std::enable_if_t<std::is_enum_v<FLAG>>>
        Flags<FLAG> get() const {
            Flags<FLAG> r;
            for (std::size_t i=0; i<count(FLAG()); i++) {
                r.set(static_cast<FLAG>(i), get(static_cast<FLAG>(i)));
            }
            return r;
        }

        template <typename FLAG, typename = std::enable_if_t<std::is_enum_v<FLAG>>>
        void set(FLAG flag, bool value) {
            set(Private::FlagOffset<FLAG, FLAGS...>::value+static_cast<std::underlying_type_t<FLAG>>(flag), value);
        }

        template <typename FLAG, typename = std::enable_if_t<std::is_enum_v<FLAG>>>
        Flags &operator|=(FLAG flag) {
            set(flag, true);
            return *this;
        }
        template <typename FLAG, typename = std::enable_if_t<std::is_enum_v<FLAG>>>
        Flags &operator|=(const Flags<FLAG> &flags) {
            for (std::size_t i=0; i<count(FLAG()); i++) {
                set(static_cast<FLAG>(i), flags.get(static_cast<FLAG>(i)));
            }
            return *this;
        }

        template <typename ...FLAG>
        Flags &operator=(const Flags<FLAG...> &flags) {
            clearAll();
            (operator|=(flags.template get<FLAG>()), ...);
            return *this;
        }
};

template <typename FLAG, typename = std::enable_if_t<std::is_enum_v<FLAG>>>
constexpr Flags<FLAG> operator|(FLAG a, FLAG b) {
    Flags<FLAG> f;
    f |= a;
    f |= b;
    return f;
}

template <typename FLAGA, typename FLAGB, typename = std::enable_if_t<std::is_enum_v<FLAGA>&&std::is_enum_v<FLAGB>>>
constexpr Flags<FLAGA, FLAGB> operator|(FLAGA a, FLAGB b) {
    Flags<FLAGA, FLAGB> f;
    f |= a;
    f |= b;
    return f;
}