├── .gitignore ├── meta ├── TypeList.cpp ├── Type.cpp ├── ValueList.cpp ├── details │ ├── BitIntrinsics.cpp │ ├── BitStorage.cpp │ ├── BitIntrinsics.h │ └── BitStorage.h ├── Name.cpp ├── Value.cpp ├── check.cpp ├── check.h ├── Name.h ├── Type.h ├── Value.h ├── TypeList.h └── ValueList.h ├── classic ├── Flags.cpp └── Flags.h ├── tagtype ├── Flags.cpp └── Flags.h ├── repeated ├── Flags.cpp └── Flags.h ├── tagvalue ├── Flags.cpp └── Flags.h ├── bitnumber ├── Flags.cpp └── Flags.h ├── flagstest.cpp ├── cpp17_flags.qbs ├── main.cpp ├── Readme.md └── LICENSE /.gitignore: -------------------------------------------------------------------------------- 1 | *.user 2 | *.user* -------------------------------------------------------------------------------- /meta/TypeList.cpp: -------------------------------------------------------------------------------- 1 | #include "TypeList.h" 2 | -------------------------------------------------------------------------------- /meta/Type.cpp: -------------------------------------------------------------------------------- 1 | #include "Type.h" 2 | 3 | 4 | -------------------------------------------------------------------------------- /meta/ValueList.cpp: -------------------------------------------------------------------------------- 1 | #include "ValueList.h" 2 | 3 | 4 | -------------------------------------------------------------------------------- /meta/details/BitIntrinsics.cpp: -------------------------------------------------------------------------------- 1 | #include "BitIntrinsics.h" 2 | -------------------------------------------------------------------------------- /meta/Name.cpp: -------------------------------------------------------------------------------- 1 | #include "Name.h" 2 | 3 | namespace meta { 4 | 5 | // TODO 6 | 7 | } // namespace meta 8 | -------------------------------------------------------------------------------- /meta/Value.cpp: -------------------------------------------------------------------------------- 1 | #include "Value.h" 2 | 3 | namespace meta { 4 | 5 | // TODO 6 | 7 | } // namespace meta 8 | -------------------------------------------------------------------------------- /meta/check.cpp: -------------------------------------------------------------------------------- 1 | #include "check.h" 2 | 3 | namespace meta { 4 | 5 | // TODO 6 | 7 | } // namespace meta 8 | -------------------------------------------------------------------------------- /classic/Flags.cpp: -------------------------------------------------------------------------------- 1 | #include "Flags.h" 2 | 3 | namespace classic { 4 | 5 | // TODO 6 | 7 | } // namespace classic 8 | -------------------------------------------------------------------------------- /tagtype/Flags.cpp: -------------------------------------------------------------------------------- 1 | #include "Flags.h" 2 | 3 | namespace tagtype { 4 | 5 | // TODO 6 | 7 | } // namespace tagtype 8 | -------------------------------------------------------------------------------- /repeated/Flags.cpp: -------------------------------------------------------------------------------- 1 | #include "Flags.h" 2 | 3 | namespace repeated { 4 | 5 | // TODO 6 | 7 | } // namespace repeated 8 | -------------------------------------------------------------------------------- /tagvalue/Flags.cpp: -------------------------------------------------------------------------------- 1 | #include "Flags.h" 2 | 3 | namespace tagvalue { 4 | 5 | // TODO 6 | 7 | } // namespace tagvalue 8 | -------------------------------------------------------------------------------- /bitnumber/Flags.cpp: -------------------------------------------------------------------------------- 1 | #include "Flags.h" 2 | 3 | namespace bitnumber { 4 | 5 | // TODO 6 | 7 | } // namespace bitnumber 8 | -------------------------------------------------------------------------------- /meta/details/BitStorage.cpp: -------------------------------------------------------------------------------- 1 | #include "BitStorage.h" 2 | 3 | namespace meta::details { 4 | 5 | // TODO 6 | 7 | } // namespace meta::details 8 | -------------------------------------------------------------------------------- /meta/check.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | namespace meta { 5 | 6 | template 7 | constexpr bool check() { 8 | return std::tuple::fail(); 9 | } 10 | 11 | } // namespace meta 12 | -------------------------------------------------------------------------------- /meta/Name.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "type_traits" 3 | 4 | namespace meta { 5 | 6 | // invalid prototype is required for HasName to compile 7 | constexpr auto name() -> void; 8 | 9 | template 10 | struct HasName : std::false_type {}; 11 | 12 | template 13 | struct HasName()))>> : std::true_type {}; 14 | 15 | template 16 | constexpr auto hasName = HasName::value; 17 | 18 | } // namespace meta 19 | -------------------------------------------------------------------------------- /flagstest.cpp: -------------------------------------------------------------------------------- 1 | #include "tagtype/Flags.h" 2 | 3 | #include 4 | 5 | class flagsTest : public QObject { 6 | Q_OBJECT 7 | 8 | private slots: 9 | void test__tagtype_Flags__all() { 10 | using namespace meta; 11 | auto flags = tagtype::Flags::setAll(); 12 | auto anded = flags & tagtype::Flag{}; 13 | QCOMPARE(anded, tagtype::Flag{}); 14 | } 15 | }; 16 | 17 | QTEST_APPLESS_MAIN(flagsTest) 18 | 19 | #include "flagstest.moc" 20 | -------------------------------------------------------------------------------- /meta/Type.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | namespace meta { 5 | 6 | template 7 | struct Type { 8 | template class B> 9 | constexpr static auto to() -> B { 10 | return {}; 11 | } 12 | }; 13 | 14 | template 15 | constexpr bool operator==(Type, Type) noexcept { 16 | if constexpr (std::is_same_v) 17 | return std::true_type{}; 18 | else 19 | return std::false_type{}; 20 | } 21 | 22 | template 23 | constexpr bool operator!=(Type a, Type b) noexcept { 24 | return !(a == b); 25 | } 26 | 27 | } // namespace meta 28 | -------------------------------------------------------------------------------- /meta/Value.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | namespace meta { 5 | 6 | template 7 | struct Value { 8 | template class T> 9 | constexpr static auto to() -> T { 10 | return {}; 11 | } 12 | constexpr static auto value() -> decltype(V) { return V; } 13 | }; 14 | 15 | template 16 | constexpr bool operator==(Value, Value) noexcept { 17 | if constexpr (std::is_same_v) 18 | return std::bool_constant{}; 19 | else 20 | return std::false_type{}; 21 | } 22 | 23 | template 24 | constexpr bool operator!=(Value a, Value b) noexcept { 25 | return !(a == b); 26 | } 27 | 28 | } // namespace meta 29 | -------------------------------------------------------------------------------- /meta/details/BitIntrinsics.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | namespace meta::details { 4 | 5 | struct BitIntrinsics { 6 | // Returns number of zero bits preceding least significant 1 bit. 7 | // Undefined for zero value. 8 | static inline int countOfTrailingZeros(uint32_t); 9 | 10 | // Returns number of zero bits following most significant 1 bit. 11 | // Undefined for zero value. 12 | static inline int countLeadingZeros(uint32_t); 13 | 14 | // Returns the number of bits set. 15 | static inline int countSetBits(uint32_t); 16 | }; 17 | 18 | #ifdef __GNUC__ 19 | int BitIntrinsics::countOfTrailingZeros(uint32_t value) { return __builtin_ctz(value); } 20 | 21 | int BitIntrinsics::countLeadingZeros(uint32_t value) { return __builtin_clz(value); } 22 | 23 | int BitIntrinsics::countSetBits(uint32_t value) { return __builtin_popcount(value); } 24 | 25 | #elif defined(_MSC_VER) 26 | 27 | #include 28 | #pragma intrinsic(_BitScanForward) 29 | #pragma intrinsic(_BitScanReverse) 30 | 31 | int BitIntrinsics::countOfTrailingZeros(uint32_t value) { 32 | unsigned long result; // NOLINT 33 | _BitScanForward(&result, value); // NOLINT 34 | return static_cast(result); 35 | } 36 | 37 | int BitIntrinsics::countLeadingZeros(uint32_t value) { 38 | unsigned long result; // NOLINT 39 | _BitScanReverse(&result, value); // NOLINT 40 | return 31 - static_cast(result); 41 | } 42 | 43 | int BitIntrinsics::countSetBits(uint32_t value) { 44 | // Manually count set bits. 45 | value = ((value >> 1) & 0x55555555) + (value & 0x55555555); 46 | value = ((value >> 2) & 0x33333333) + (value & 0x33333333); 47 | value = ((value >> 4) & 0x0f0f0f0f) + (value & 0x0f0f0f0f); 48 | value = ((value >> 8) & 0x00ff00ff) + (value & 0x00ff00ff); 49 | value = ((value >> 16) & 0x0000ffff) + (value & 0x0000ffff); 50 | return value; 51 | } 52 | 53 | #else 54 | #error Unsupported compiler 55 | #endif 56 | /* 57 | template 58 | constexpr auto count_zeros_impl(uint32_t v) -> uint32_t { 59 | if constexpr (n == 0) return 0; 60 | if constexpr (n == 1) return !(v & 1); 61 | constexpr auto h = n / 2; 62 | constexpr auto hm = (1<(lower); 65 | else return h + count_zeros_impl(v >> h); 66 | } 67 | 68 | static_assert(count_zeros_impl<5>(15) == 0); 69 | static_assert(count_zeros_impl<5>(8) == 3); 70 | static_assert(count_zeros_impl<5>(4) == 2); 71 | static_assert(count_zeros_impl<5>(2) == 1); 72 | static_assert(count_zeros_impl<5>(1) == 0); 73 | static_assert(count_zeros_impl<32>(1<<24) == 24); 74 | */ 75 | auto count_zeros(uint32_t v) -> int { 76 | // return count_zeros_impl<4>(v); 77 | if (v) 78 | return BitIntrinsics::countOfTrailingZeros(v); 79 | else 80 | return 0; 81 | } 82 | 83 | } // namespace meta::details 84 | -------------------------------------------------------------------------------- /meta/details/BitStorage.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | 6 | namespace meta::details { 7 | 8 | template 9 | struct BitStorage; 10 | 11 | template<> 12 | struct BitStorage { 13 | using This = BitStorage; 14 | using Index = size_t; 15 | 16 | explicit constexpr BitStorage(Index idx) noexcept 17 | : v(1 << idx) {} 18 | 19 | template 20 | explicit constexpr BitStorage(std::index_sequence) noexcept 21 | : v((0 | ... | (1 << I))) {} 22 | 23 | explicit constexpr BitStorage(std::initializer_list i) noexcept 24 | : v([=] { 25 | auto s = size_t{}; 26 | for (auto v : i) s |= (1 << v); 27 | return s; 28 | }()) {} 29 | 30 | constexpr BitStorage() noexcept = default; 31 | constexpr BitStorage(const This &) noexcept = default; 32 | constexpr BitStorage(This &&) noexcept = default; 33 | constexpr auto operator=(const This &) noexcept -> This & = default; 34 | constexpr auto operator=(This &&) noexcept -> This & = default; 35 | 36 | constexpr bool operator==(const This &o) const noexcept { return v == o.v; } 37 | constexpr bool operator!=(const This &o) const noexcept { return !(*this == o); } 38 | 39 | constexpr bool operator[](Index idx) const noexcept { return (v >> idx) & 1; } 40 | 41 | constexpr auto set(Index idx) const noexcept -> This { return *this | idx; } 42 | constexpr auto reset(Index idx) const noexcept -> This { return *this & ~BitStorage{idx}; } 43 | constexpr auto flip(Index idx) const noexcept -> This { return *this ^ idx; } 44 | 45 | constexpr static auto setAll() noexcept -> This { 46 | auto r = This{}; 47 | r.v = std::numeric_limits::max(); 48 | return r; 49 | } 50 | constexpr static auto resetAll() noexcept -> This { return This{}; } 51 | constexpr auto flipAll() const noexcept -> This { return ~*this; } 52 | 53 | constexpr auto operator~() const noexcept -> This { 54 | auto r = This{}; 55 | r.v = ~v; 56 | return r; 57 | } 58 | 59 | constexpr auto operator|(This o) const noexcept -> This { 60 | auto r = This{}; 61 | r.v = v | o.v; 62 | return r; 63 | } 64 | constexpr auto operator|(Index idx) const noexcept -> This { 65 | auto r = This{}; 66 | r.v = v | (1 << idx); 67 | return r; 68 | } 69 | 70 | constexpr auto operator&(This o) const noexcept -> This { 71 | auto r = This{}; 72 | r.v = v & o.v; 73 | return r; 74 | } 75 | constexpr auto operator&(Index idx) const noexcept -> This { 76 | auto r = This{}; 77 | r.v = v & (1 << idx); 78 | return r; 79 | } 80 | 81 | constexpr auto operator^(This o) const noexcept -> This { 82 | auto r = This{}; 83 | r.v = v ^ o.v; 84 | return r; 85 | } 86 | constexpr auto operator^(Index idx) const noexcept -> This { 87 | auto r = This{}; 88 | r.v = v ^ (1 << idx); 89 | return r; 90 | } 91 | 92 | private: 93 | unsigned int v{}; 94 | }; 95 | 96 | template 97 | constexpr auto SelectBitStorage() { 98 | constexpr auto bytes = (bits + 7) / 8; 99 | if constexpr (bytes <= sizeof(unsigned int)) { 100 | return BitStorage{}; 101 | } 102 | throw "Not enough bits in an unsigned int (TODO: write additional storage)"; 103 | } 104 | 105 | } // namespace meta::details 106 | -------------------------------------------------------------------------------- /meta/TypeList.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "Type.h" 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | namespace meta { 10 | 11 | namespace details { 12 | 13 | template 14 | constexpr auto head() noexcept -> Type { 15 | return {}; 16 | } 17 | 18 | template 19 | constexpr auto indexOf(std::index_sequence = {}) noexcept -> size_t { 20 | constexpr auto max = std::numeric_limits::max(); 21 | return std::min({max, (Type{} == Type{} ? I : max)...}); 22 | } 23 | 24 | template 25 | constexpr auto isTypeSet(std::index_sequence s = {}) noexcept -> bool { 26 | return (true && ... && (I == indexOf(s))); 27 | } 28 | 29 | template 30 | struct Types { 31 | template class N> 32 | constexpr static auto to() { 33 | return N{}; 34 | } 35 | }; 36 | 37 | template 38 | struct ConditionalType { 39 | template 40 | constexpr auto operator+=(Types o) const noexcept { 41 | if constexpr (C) 42 | return Types{}; 43 | else 44 | return o; 45 | } 46 | }; 47 | 48 | template 49 | constexpr auto filter() noexcept { 50 | return (ConditionalType{} != Type{}, A>{} += ... += Types<>{}); 51 | } 52 | 53 | } // namespace details 54 | 55 | template 56 | struct TypeList { 57 | using IndexSequence = std::index_sequence_for; 58 | static constexpr auto indexSequence = IndexSequence{}; 59 | 60 | template 61 | static constexpr auto indexOf(Type = {}) noexcept -> size_t { 62 | return details::indexOf(indexSequence); 63 | } 64 | template 65 | static constexpr auto filter(Type = {}) noexcept { 66 | return details::filter().template to(); 67 | } 68 | static constexpr auto isSet = details::isTypeSet(indexSequence); 69 | 70 | template 71 | static constexpr void each(F &&f) noexcept { 72 | (f(Type{}), ...); 73 | } 74 | template class B> 75 | static constexpr auto to() noexcept -> B { 76 | return {}; 77 | } 78 | }; 79 | 80 | template 81 | constexpr auto operator+(TypeList, TypeList) -> TypeList { 82 | return {}; 83 | } 84 | 85 | template 86 | constexpr auto indexOf(TypeList a = {}, Type = {}) noexcept -> size_t { 87 | return details::indexOf(a.indexSequence); 88 | } 89 | 90 | template 91 | constexpr auto filter(TypeList = {}, Type = {}) noexcept { 92 | return details::filter().template to(); 93 | } 94 | 95 | static_assert(TypeList::indexOf() == 0, ""); 96 | static_assert(TypeList::indexOf() == 1, ""); 97 | static_assert(TypeList::indexOf() == 2, ""); 98 | static_assert(TypeList::indexOf() == std::numeric_limits::max(), ""); 99 | static_assert(TypeList<>::indexOf() == std::numeric_limits::max(), ""); 100 | 101 | static_assert(TypeList::isSet == true, "isSet failed"); 102 | static_assert(TypeList::isSet == false, "isSet failed"); 103 | 104 | static_assert(std::is_same_v::filter()), TypeList>, 105 | "filter failed"); 106 | 107 | } // namespace meta 108 | -------------------------------------------------------------------------------- /cpp17_flags.qbs: -------------------------------------------------------------------------------- 1 | import qbs 2 | 3 | Project { 4 | minimumQbsVersion: "1.7.1" 5 | 6 | StaticLibrary { 7 | name: "000_meta" 8 | Depends { name: "cpp" } 9 | cpp.cxxLanguageVersion: "c++17" 10 | cpp.includePaths: ["."] 11 | cpp.cxxFlags: { 12 | if (qbs.toolchain.contains('msvc')) return "/await"; 13 | if (qbs.toolchain.contains('clang')) return ["-fcoroutines-ts"]; 14 | } 15 | cpp.cxxStandardLibrary: { 16 | if (qbs.toolchain.contains('clang')) return "libc++"; 17 | } 18 | 19 | files: [ 20 | "meta/Name.cpp", 21 | "meta/Name.h", 22 | "meta/check.cpp", 23 | "meta/check.h", 24 | "meta/details/BitIntrinsics.cpp", 25 | "meta/details/BitIntrinsics.h", 26 | "meta/Type.cpp", 27 | "meta/Type.h", 28 | "meta/TypeList.cpp", 29 | "meta/TypeList.h", 30 | "meta/Value.cpp", 31 | "meta/Value.h", 32 | "meta/ValueList.cpp", 33 | "meta/ValueList.h", 34 | "meta/details/BitStorage.cpp", 35 | "meta/details/BitStorage.h", 36 | ] 37 | Export { 38 | Depends { name: "cpp" } 39 | cpp.cxxLanguageVersion: "c++17" 40 | cpp.includePaths: ["."] 41 | cpp.cxxFlags: { 42 | if (qbs.toolchain.contains('msvc')) return "/await"; 43 | if (qbs.toolchain.contains('clang')) return ["-fcoroutines-ts"]; 44 | } 45 | cpp.cxxStandardLibrary: { 46 | if (qbs.toolchain.contains('clang')) return "libc++"; 47 | } 48 | cpp.staticLibraries: { 49 | if (qbs.toolchain.contains('clang')) return ["c++", "c++abi"]; 50 | } 51 | } 52 | } 53 | 54 | StaticLibrary { 55 | name: "002_classic" 56 | Depends { name: "000_meta" } 57 | files: [ 58 | "classic/Flags.cpp", 59 | "classic/Flags.h", 60 | ] 61 | } 62 | 63 | StaticLibrary { 64 | name: "003_bitnumber" 65 | Depends { name: "000_meta" } 66 | files: [ 67 | "bitnumber/Flags.cpp", 68 | "bitnumber/Flags.h", 69 | ] 70 | } 71 | 72 | StaticLibrary { 73 | name: "004_tagtype" 74 | Depends { name: "000_meta" } 75 | files: [ 76 | "tagtype/Flags.cpp", 77 | "tagtype/Flags.h", 78 | ] 79 | } 80 | StaticLibrary { 81 | name: "005_tagvalue" 82 | Depends { name: "000_meta" } 83 | files: [ 84 | "tagvalue/Flags.cpp", 85 | "tagvalue/Flags.h", 86 | ] 87 | } 88 | 89 | StaticLibrary { 90 | name: "006_repeated" 91 | Depends { name: "000_meta" } 92 | files: [ 93 | "repeated/Flags.cpp", 94 | "repeated/Flags.h", 95 | ] 96 | } 97 | 98 | Application { 99 | name: "flags_tests" 100 | Depends { name: "Qt.testlib" } 101 | Depends { name: "004_tagtype" } 102 | consoleApplication: true 103 | // Qt.core exports conflicting settings (see QBS-1225) 104 | Depends { name: "cpp" } 105 | cpp.cxxLanguageVersion: "c++17" 106 | cpp.includePaths: ["."] 107 | cpp.cxxStandardLibrary: { 108 | if (qbs.toolchain.contains('clang')) return "libc++"; 109 | } 110 | files: [ 111 | "flagstest.cpp" 112 | ] 113 | } 114 | 115 | Application { 116 | name: "flags_app" 117 | consoleApplication: true 118 | Depends { name: "000_meta" } 119 | Depends { name: "002_classic" } 120 | Depends { name: "003_bitnumber" } 121 | Depends { name: "004_tagtype" } 122 | Depends { name: "005_tagvalue" } 123 | Depends { name: "006_repeated" } 124 | files: [ 125 | "main.cpp", 126 | ] 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /classic/Flags.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | namespace classic { 6 | 7 | #define ENABLE_CLASSIC_FLAGS_OP(Flags) \ 8 | constexpr auto operator|(Flags::Enum e1, Flags::Enum e2) noexcept->Flags { return Flags(e1) | e2; } 9 | 10 | template 11 | struct Flags { 12 | using This = Flags; 13 | static_assert(std::is_enum_v, "Flags only works for enums!"); 14 | 15 | using Enum = T; 16 | using Value = std::underlying_type_t; 17 | 18 | template 19 | constexpr Flags(Enum v, Args... args) noexcept 20 | : Flags(build(v, args...)) {} 21 | 22 | constexpr Flags() noexcept = default; 23 | constexpr Flags(const This &) noexcept = default; 24 | constexpr Flags(This &&) noexcept = default; 25 | ~Flags() = default; 26 | constexpr auto operator=(const This &) noexcept -> This & = default; 27 | constexpr auto operator=(This &&) noexcept -> This & = default; 28 | 29 | constexpr auto operator=(Enum e) noexcept -> This & { 30 | v = static_cast(e); 31 | return *this; 32 | } 33 | 34 | constexpr void swap(This &fl) noexcept { std::swap(v, fl.v); } 35 | 36 | constexpr bool operator==(This f) const noexcept { return v == f.v; } 37 | constexpr bool operator!=(This f) const noexcept { return v != f.v; } 38 | 39 | constexpr bool operator[](Enum t) const noexcept { return (v & t) != 0; } 40 | 41 | constexpr bool all(This f) const noexcept { return (v & f.v) == f.v; } 42 | template 43 | constexpr bool all(Enum b, Args... args) const noexcept { 44 | return all(build(b, args...)); 45 | } 46 | 47 | constexpr bool any(This f) const noexcept { return (v & f.v) != 0; } 48 | template 49 | constexpr bool any(Enum b, Args... args) const noexcept { 50 | return any(build(b, args...)); 51 | } 52 | 53 | constexpr bool none() const noexcept { return v == 0; } 54 | constexpr bool none(This f) const noexcept { return (v & f.v) == 0; } 55 | template 56 | constexpr bool none(Enum b, Args... args) const noexcept { 57 | return none(build(b, args...)); 58 | } 59 | 60 | constexpr static auto resetAll() noexcept -> This { return Flags{}; } 61 | 62 | constexpr auto set(This b) const noexcept -> This { return This{v | b.v}; } 63 | template 64 | constexpr auto set(Enum b, Args... args) const noexcept -> This { 65 | return set(build(b, args...)); 66 | } 67 | 68 | constexpr auto reset(This b) const noexcept -> This { return This{v & ~b.v}; } 69 | template 70 | constexpr auto reset(Enum b, Args... args) const noexcept -> This { 71 | return reset(build(b, args...)); 72 | } 73 | 74 | constexpr auto flip(This b) const noexcept -> This { return This{v ^ b.v}; } 75 | template 76 | constexpr auto flip(Enum b, Args... args) const noexcept -> This { 77 | return flip(build(b, args...)); 78 | } 79 | 80 | constexpr auto mask(This b) const noexcept -> This { return This{v & b.v}; } 81 | template 82 | constexpr auto mask(Enum b, Args... args) const noexcept -> This { 83 | return mask(build(b, args...)); 84 | } 85 | 86 | template 87 | constexpr auto operator|(B b) const noexcept -> This { 88 | return set(b); 89 | } 90 | template 91 | constexpr auto operator&(B b) const noexcept -> This { 92 | return mask(b); 93 | } 94 | template 95 | constexpr auto operator^(B b) const noexcept -> This { 96 | return flip(b); 97 | } 98 | 99 | template 100 | constexpr auto operator|=(B b) noexcept -> This & { 101 | return *this = set(b); 102 | } 103 | template 104 | constexpr auto operator&=(B b) noexcept -> This & { 105 | return *this = mask(b); 106 | } 107 | template 108 | constexpr auto operator^=(B b) noexcept -> This & { 109 | return *this = flip(b); 110 | } 111 | 112 | template 113 | constexpr void each_set(F &&f) const noexcept { 114 | auto vt = Value{}; 115 | while (true) { 116 | const auto t = static_cast(1 << vt); 117 | const auto ft = Flags{t}; 118 | if (ft.v > v) break; 119 | if (any(ft)) f(t); 120 | vt++; 121 | } 122 | } 123 | 124 | private: 125 | constexpr Flags(Value v) noexcept 126 | : v(v) {} 127 | 128 | template 129 | static constexpr auto build(Args... args) noexcept -> Flags { 130 | return Flags{(... | static_cast(args))}; 131 | } 132 | 133 | private: 134 | Value v{}; 135 | }; 136 | 137 | template 138 | auto operator<<(Out &out, Flags f) 139 | -> std::enable_if_t()), decltype(out)>, decltype(out)> // 140 | { 141 | using flags_type = Flags; 142 | if (f == flags_type{}) return out << ""; 143 | f.each_set([&, first = true](T t) mutable { 144 | if (!first) 145 | out << " | "; 146 | else 147 | first = false; 148 | out << t; 149 | }); 150 | return out; 151 | } 152 | 153 | } // namespace classic 154 | -------------------------------------------------------------------------------- /bitnumber/Flags.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | 6 | namespace bitnumber { 7 | 8 | #define ENABLE_BITNUMBER_FLAGS_OP(T) \ 9 | constexpr auto operator|(T::EnumType e1, T::EnumType e2) noexcept->T { return T(e1) | e2; } 10 | 11 | template 12 | struct Flags { 13 | using This = Flags; 14 | static_assert(std::is_enum_v, "flags only works for enums!"); 15 | 16 | using EnumType = T; 17 | using ValueType = std::underlying_type_t; 18 | using BitType = V; 19 | 20 | constexpr Flags(T v) noexcept 21 | : Flags(BitType{1} << static_cast(v)) {} 22 | 23 | template 24 | constexpr Flags(T v, Args... args) noexcept 25 | : Flags(build(v, args...)) {} 26 | 27 | constexpr Flags() noexcept = default; 28 | ~Flags() noexcept = default; 29 | constexpr Flags(const This &) noexcept = default; 30 | constexpr Flags(This &&) noexcept = default; 31 | constexpr auto operator=(const This &) noexcept -> This & = default; 32 | constexpr auto operator=(This &&) noexcept -> This & = default; 33 | 34 | constexpr void swap(This &fl) noexcept { std::swap(v, fl.v); } 35 | 36 | constexpr bool operator==(const This &f) const noexcept { return v == f.v; } 37 | constexpr bool operator!=(const This &f) const noexcept { return v != f.v; } 38 | 39 | template 40 | constexpr bool operator[](T t) const noexcept { 41 | return (v & This{t}.v) != BitType{}; 42 | } 43 | 44 | constexpr bool all(This b) const noexcept { return (v & b.v) == b.v; } 45 | template 46 | constexpr bool all(T b, Args... args) const noexcept { 47 | return all(build(b, args...)); 48 | } 49 | 50 | constexpr bool any(This f) const noexcept { return (v & f.v) != BitType{}; } 51 | template 52 | constexpr bool any(T b, Args... args) const noexcept { 53 | return any(build(b, args...)); 54 | } 55 | 56 | constexpr bool none() const noexcept { return v == 0; } 57 | constexpr bool none(This f) const noexcept { return (v & f.v) == BitType{}; } 58 | template 59 | constexpr bool none(T b, Args... args) const noexcept { 60 | return none(build(b, args...)); 61 | } 62 | 63 | constexpr static auto resetAll() noexcept -> This { return Flags{}; } 64 | 65 | constexpr auto set(This b) const noexcept -> This { return This{v | b.v}; } 66 | template 67 | constexpr auto set(T b, Args... args) const noexcept -> This { 68 | return set(build(b, args...)); 69 | } 70 | 71 | constexpr auto reset(This b) const noexcept -> This { return This{v & ~b.v}; } 72 | template 73 | constexpr auto reset(T b, Args... args) const noexcept -> This { 74 | return reset(build(b, args...)); 75 | } 76 | 77 | constexpr auto flip(This b) const noexcept -> This { return This{v ^ b.v}; } 78 | template 79 | constexpr auto flip(T b, Args... args) const noexcept -> This { 80 | return flip(build(b, args...)); 81 | } 82 | 83 | constexpr auto mask(This b) const noexcept -> This { return This{v & b.v}; } 84 | template 85 | constexpr auto mask(T b, Args... args) const noexcept -> This { 86 | return mask(build(b, args...)); 87 | } 88 | 89 | constexpr This operator|(const This &f) const noexcept { return {v | f.v}; } 90 | 91 | constexpr Flags clear(const Flags &f) const noexcept { return {v & ~f.v}; } 92 | 93 | template 94 | constexpr bool clear(const T &v, Args &&... args) const noexcept { 95 | return clear(build(v, args...)); 96 | } 97 | 98 | template 99 | constexpr auto operator|(B b) const noexcept -> This { 100 | return set(b); 101 | } 102 | template 103 | constexpr auto operator&(B b) const noexcept -> This { 104 | return mask(b); 105 | } 106 | template 107 | constexpr auto operator^(B b) const noexcept -> This { 108 | return flip(b); 109 | } 110 | 111 | template 112 | constexpr auto operator|=(B b) noexcept -> This & { 113 | return *this = set(b); 114 | } 115 | template 116 | constexpr auto operator&=(B b) noexcept -> This & { 117 | return *this = mask(b); 118 | } 119 | template 120 | constexpr auto operator^=(B b) noexcept -> This & { 121 | return *this = flip(b); 122 | } 123 | 124 | template 125 | constexpr void each_set(F &&f) const noexcept { 126 | auto vt = ValueType{}; 127 | while (true) { 128 | const auto t = static_cast(vt); 129 | const auto ft = Flags{t}; 130 | if (ft.v > v) break; 131 | if (any(t)) f(t); 132 | vt++; 133 | } 134 | } 135 | 136 | private: 137 | constexpr Flags(BitType v) noexcept 138 | : v(v) {} 139 | 140 | template 141 | static constexpr auto build(Args... args) noexcept -> Flags { 142 | return Flags{(... | Flags{args})}; 143 | } 144 | 145 | private: 146 | BitType v{}; 147 | }; 148 | 149 | template 150 | auto operator<<(Out &out, Flags f) 151 | -> std::enable_if_t()), decltype(out)>, decltype(out)> // 152 | { 153 | using flags_type = Flags; 154 | if (f == flags_type{}) return out << ""; 155 | f.each_set([&, first = true](T t) mutable { 156 | if (!first) 157 | out << " | "; 158 | else 159 | first = false; 160 | out << t; 161 | }); 162 | return out; 163 | } 164 | 165 | } // namespace bitnumber 166 | -------------------------------------------------------------------------------- /meta/ValueList.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "Value.h" 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | namespace meta { 9 | 10 | namespace details { 11 | 12 | template 13 | constexpr auto head() noexcept -> decltype(V) { 14 | return {}; 15 | } 16 | 17 | template 18 | constexpr bool isSameType() noexcept { 19 | if constexpr (sizeof...(W) >= 1) // 20 | return (true && ... && std::is_same_v()), decltype(W)>); 21 | return false; 22 | } 23 | 24 | template 25 | constexpr auto commonType() noexcept { 26 | if constexpr (isSameType()) return head(); 27 | } 28 | 29 | template 30 | constexpr auto underlyingType() noexcept { 31 | if constexpr (std::is_enum_v) { 32 | return std::underlying_type_t{}; 33 | } 34 | else { 35 | return std::declval(); 36 | } 37 | } 38 | 39 | template 40 | constexpr auto indexOf(std::index_sequence = {}) noexcept -> size_t { 41 | constexpr auto max = std::numeric_limits::max(); 42 | return std::min({max, (Value{} == Value{} ? I : max)...}); 43 | } 44 | 45 | template 46 | constexpr auto isValueSet(std::index_sequence s = {}) noexcept -> bool { 47 | return (true && ... && (I == indexOf(s))); 48 | } 49 | 50 | template 51 | struct ValueFold { 52 | template 53 | constexpr auto operator||(ValueFold o) const noexcept { 54 | if constexpr (C) 55 | return *this; 56 | else 57 | return o; 58 | } 59 | constexpr auto value() const noexcept -> Value { return {}; } 60 | }; 61 | 62 | template 63 | constexpr auto atIndex(std::index_sequence = {}) noexcept { 64 | return (ValueFold{} || ... || ValueFold{}).value(); 65 | } 66 | 67 | template 68 | constexpr auto min() noexcept { 69 | if constexpr (isSameType()) 70 | return std::min({A...}); 71 | else 72 | return nullptr; 73 | } 74 | 75 | template 76 | constexpr auto max() noexcept { 77 | if constexpr (isSameType()) 78 | return std::max({A...}); 79 | else 80 | return nullptr; 81 | } 82 | 83 | template 84 | struct Values { 85 | template class N> 86 | constexpr static auto to() { 87 | return N{}; 88 | } 89 | }; 90 | 91 | template 92 | struct ConditionalValue { 93 | template 94 | constexpr auto operator+=(Values o) const noexcept { 95 | if constexpr (C) 96 | return Values{}; 97 | else 98 | return o; 99 | } 100 | }; 101 | 102 | template 103 | constexpr auto filter() noexcept { 104 | return (ConditionalValue{} != Value{}, A>{} += ... += Values<>{}); 105 | } 106 | 107 | } // namespace details 108 | 109 | template 110 | struct ValueList { 111 | static constexpr bool isSameType = details::isSameType(); 112 | using commonType = decltype(details::commonType()); 113 | using underlyingType = decltype(details::underlyingType()); 114 | 115 | using IndexSequence = std::make_index_sequence; 116 | static constexpr auto indexSequence = IndexSequence{}; 117 | 118 | static constexpr auto min = details::min(); 119 | static constexpr auto max = details::max(); 120 | static constexpr bool isSet = details::isValueSet(indexSequence); 121 | 122 | template 123 | static constexpr auto indexOf(Value = {}) noexcept -> size_t { 124 | return details::indexOf(indexSequence); 125 | } 126 | 127 | template 128 | static constexpr auto atIndex(Value = {}) noexcept { 129 | return details::atIndex(indexSequence); 130 | } 131 | 132 | template 133 | static constexpr auto filter(Value = {}) noexcept { 134 | return details::filter().template to(); 135 | } 136 | template 137 | static constexpr void each(F &&f) noexcept { 138 | (f(Value{}), ...); 139 | } 140 | template class T> 141 | static constexpr auto to() noexcept -> T { 142 | return {}; 143 | } 144 | }; 145 | 146 | template 147 | constexpr auto operator+(ValueList, ValueList) -> ValueList { 148 | return {}; 149 | } 150 | 151 | static_assert(ValueList<1, 2, 3>::indexOf<1>() == 0, "indexOf does not work"); 152 | static_assert(ValueList<1, 2, 3>::indexOf<2>() == 1, "indexOf does not work"); 153 | static_assert(ValueList<1, 2, 3>::indexOf<3>() == 2, "indexOf does not work"); 154 | static_assert(ValueList<0, 2, 3>::indexOf<1>() == std::numeric_limits::max(), "indexOf does not work"); 155 | static_assert(ValueList<>::indexOf<1>() == std::numeric_limits::max(), "indexOf does not work"); 156 | 157 | static_assert(ValueList<1, 2, 3>::isSameType, "sameType does not work"); 158 | static_assert(!ValueList<1, 'a', 3>::isSameType, "sameType does not work"); 159 | 160 | static_assert(std::is_same_v::commonType, int>, "commonType does not work"); 161 | static_assert(std::is_same_v::commonType, void>, "commonType does not work"); 162 | 163 | static_assert(ValueList<1, 2, 3>::min == 1, "min does not work"); 164 | static_assert(ValueList<1, 2, 3>::max == 3, "max does not work"); 165 | 166 | static_assert(ValueList<1, 2, 3>::isSet, "isSet does not work"); 167 | static_assert(!ValueList<1, 2, 2>::isSet, "isSet does not work"); 168 | 169 | static_assert(std::is_same_v::filter<2>()), ValueList<1, 3>>, "filter does not work"); 170 | 171 | } // namespace meta 172 | -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | namespace legacy_enum_approach { 5 | enum Animals { 6 | ANIMALS_CAT = 1 << 0, 7 | ANIMALS_DOG = 1 << 1, 8 | ANIMALS_WOLF = 1 << 3, 9 | }; 10 | static_assert(std::is_same_v() | std::declval()), int>, // 11 | "default fallback to int"); 12 | 13 | auto operator|(Animals a, Animals b) -> Animals { return Animals(int(a) | b); } 14 | auto operator&(Animals a, Animals b) -> Animals { return Animals(int(a) & b); } 15 | 16 | static_assert(std::is_same_v() | std::declval()), Animals>, 17 | "operator keeps types"); 18 | 19 | auto operator<<(std::ostream &out, Animals a) -> std::ostream & { 20 | auto p = [&, first = true ](const char *t) mutable { 21 | if (first) 22 | first = false; 23 | else 24 | out << " | "; 25 | out << t; 26 | }; 27 | if (a & ANIMALS_CAT) p("Cat"); 28 | if (a & ANIMALS_DOG) p("Dog"); 29 | if (a & ANIMALS_WOLF) p("Wolf"); 30 | return out; 31 | } 32 | 33 | void usage() { 34 | constexpr auto Cat = ANIMALS_CAT; 35 | auto a = Cat | ANIMALS_DOG; 36 | std::cout << Cat << '\n'; 37 | std::cout << a << '\n'; 38 | std::cout << ((a & Cat) | ANIMALS_WOLF) << '\n'; 39 | } 40 | } // namespace legacy_enum_approach 41 | 42 | #include "classic/Flags.h" 43 | namespace classic_enum_class_approach { 44 | enum class Animal { 45 | Cat = 1 << 0, 46 | Dog = 1 << 1, 47 | Wolf = 1 << 3, 48 | }; 49 | using Animals = classic::Flags; 50 | 51 | // hint: use this outside of namespace or it wont be usable there! 52 | ENABLE_CLASSIC_FLAGS_OP(Animals) 53 | 54 | auto operator<<(std::ostream &out, Animal a) -> std::ostream & { 55 | switch (a) { 56 | case Animal::Cat: return out << "Cat"; 57 | case Animal::Dog: return out << "Dog"; 58 | case Animal::Wolf: return out << "Wolf"; 59 | } 60 | } 61 | 62 | void usage() { 63 | constexpr auto Cat = Animal::Cat; 64 | auto a = Cat | Animal::Dog; 65 | std::cout << Cat << '\n'; 66 | std::cout << a << '\n'; 67 | std::cout << ((a & Cat) | Animal::Wolf) << '\n'; 68 | } 69 | 70 | } // namespace classic_enum_class_approach 71 | 72 | #include "bitnumber/Flags.h" 73 | namespace bit_number_approach { 74 | enum class Animal { Cat, Dog, Wolf = 3 }; 75 | using Animals = bitnumber::Flags; 76 | 77 | // hint: use this outside of namespace or it wont be usable there! 78 | ENABLE_BITNUMBER_FLAGS_OP(Animals) 79 | 80 | auto operator<<(std::ostream &out, Animal a) -> std::ostream & { 81 | switch (a) { 82 | case Animal::Cat: return out << "Cat"; 83 | case Animal::Dog: return out << "Dog"; 84 | case Animal::Wolf: return out << "Wolf"; 85 | } 86 | } 87 | 88 | void usage() { 89 | constexpr auto Cat = Animal::Cat; 90 | auto a = Cat | Animal::Dog; 91 | std::cout << Cat << '\n'; 92 | std::cout << a << '\n'; 93 | std::cout << ((a & Cat) | Animal::Wolf) << '\n'; 94 | } 95 | } // namespace bit_number_approach 96 | 97 | #include "tagtype/Flags.h" 98 | namespace tag_type_approach { 99 | 100 | struct Animal { 101 | struct Cat; 102 | struct Dog; 103 | struct Wolf; 104 | }; 105 | using Animals = tagtype::Flags; 106 | 107 | using AnimalCat = Animal::Cat; 108 | auto operator<<(std::ostream &out, tagtype::Flag) -> std::ostream & { return out << "Cat"; } 109 | auto operator<<(std::ostream &out, tagtype::Flag) -> std::ostream & { return out << "Dog"; } 110 | auto operator<<(std::ostream &out, tagtype::Flag) -> std::ostream & { return out << "Wolf"; } 111 | 112 | template 113 | constexpr auto Flag = tagtype::Flag{}; 114 | 115 | void usage() { 116 | using Cat = Animal::Cat; // use type aliases for shorter names 117 | constexpr auto DogF = Flag; // flag types works only if you use them one by one 118 | auto a = Animals(Flag, DogF); 119 | std::cout << Flag << '\n'; 120 | std::cout << a << '\n'; 121 | std::cout << (a & Flag | Flag) << '\n'; 122 | } 123 | } // namespace tag_type_approach 124 | 125 | #include "tagvalue/Flags.h" 126 | namespace tag_value_approach { 127 | 128 | enum class Animal { Cat, Dog }; 129 | enum class AnimalWolf {}; 130 | constexpr auto AnimalCat = Animal::Cat; 131 | 132 | using Animals = tagvalue::Flags; 133 | 134 | #if true // one stream operator per type 135 | using tagvalue::operator<<; 136 | auto operator<<(std::ostream &out, tagvalue::Typed a) -> std::ostream & { 137 | switch (a.value) { 138 | case AnimalCat: return out << "Cat"; 139 | case Animal::Dog: return out << "Dog"; 140 | } 141 | } 142 | auto operator<<(std::ostream &out, tagvalue::Typed) -> std::ostream & { return out << "Wolf"; } 143 | #else // individual stream operators 144 | } // namespace tag_value_approach 145 | namespace meta { 146 | auto operator<<(std::ostream &out, tagvalue::Flag) -> std::ostream & { 147 | return out << "Cat"; 148 | } 149 | auto operator<<(std::ostream &out, tagvalue::Flag) -> std::ostream & { 150 | return out << "Dog"; 151 | } 152 | auto operator<<(std::ostream &out, tagvalue::Flag) -> std::ostream & { 153 | return out << "Wolf"; 154 | } 155 | } // namespace meta 156 | namespace tag_value_approach { 157 | #endif 158 | 159 | template 160 | constexpr auto Flag = tagvalue::Flag{}; 161 | 162 | void usage() { 163 | constexpr auto Cat = Animal::Cat; 164 | constexpr auto DogF = Flag; 165 | auto a = Animals(Flag, DogF); 166 | std::cout << Flag << '\n'; 167 | std::cout << a << '\n'; 168 | std::cout << (a & Flag | Flag) << '\n'; 169 | } 170 | } // namespace tag_value_approach 171 | 172 | #include "repeated/Flags.h" 173 | namespace repeated_bit_approach { 174 | 175 | enum class Animal { Cat, Dog, Wolf = 3 }; 176 | constexpr auto AnimalCat = Animal::Cat; 177 | 178 | using Animals = repeated::Flags; 179 | ENABLE_REPEATED_FLAGS_OP(Animals) 180 | 181 | auto operator<<(std::ostream &out, Animal a) -> std::ostream & { 182 | switch (a) { 183 | case AnimalCat: return out << "Cat"; 184 | case Animal::Dog: return out << "Dog"; 185 | case Animal::Wolf: return out << "Wolf"; 186 | } 187 | } 188 | 189 | void usage() { 190 | constexpr auto Cat = Animal::Cat; 191 | auto a = Cat | Animal::Dog; 192 | std::cout << Cat << '\n'; 193 | std::cout << a << '\n'; 194 | std::cout << ((a & Cat) | Animal::Wolf) << '\n'; 195 | } 196 | } // namespace repeated_bit_approach 197 | 198 | int main() { 199 | std::cout << "\n-- legacy enum approach --\n"; 200 | legacy_enum_approach::usage(); 201 | 202 | std::cout << "\n-- classic enum class approach --\n"; 203 | classic_enum_class_approach::usage(); 204 | 205 | std::cout << "\n-- bit number approach --\n"; 206 | bit_number_approach::usage(); 207 | 208 | std::cout << "\n-- tag type approach --\n"; 209 | tag_type_approach::usage(); 210 | 211 | std::cout << "\n-- tag value approach --\n"; 212 | tag_value_approach::usage(); 213 | 214 | std::cout << "\n-- repeated bit approach --\n"; 215 | repeated_bit_approach::usage(); 216 | } 217 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | # C++17 Flags 2 | 3 | ### Summary of flags with C++17. 4 | 5 | For the new experimental compiler of Rebuild language project I needed all kinds of flags. 6 | This ended up being a rabbit hole. Here is what I digged out of that. 7 | 8 | ## Legacy enum approach 9 | 10 | Basically taken from C to C++. 11 | 12 | ```cpp 13 | enum Animals { 14 | ANIMALS_CAT = 1 << 0, 15 | ANIMALS_DOG = 1 << 1, 16 | ANIMALS_WOLF = 1 << 3, 17 | }; 18 | // define operator to prevent fallback to int 19 | auto operator|(Animals a, Animals b) -> Animals { return Animals(int(a) | b); } 20 | auto operator&(Animals a, Animals b) -> Animals { return Animals(int(a) & b); } 21 | 22 | // define stream operator for bitmask 23 | auto operator<<(std::ostream &out, Animals a) -> std::ostream & { 24 | // …see main.cpp for details 25 | } 26 | 27 | void usage() { 28 | auto a = ANIMALS_CAT | ANIMALS_DOG; 29 | std::cout << a << '\n'; 30 | std::cout << ((a & ANIMALS_CAT) | ANIMALS_WOLF) << '\n'; 31 | } 32 | ``` 33 | 34 | References: 35 | * C-APIs: Posix, ffmpeg, win32 … 36 | 37 | Advantages: 38 | * very well known pattern 39 | * syntax compatible with C 40 | 41 | Disadvantages: 42 | * enum value names leak to namespace 43 | * programmer has to ensure that all bits are unique 44 | * hard to spot skipped bits 45 | * no iteration on bits possible 46 | * no type safety (easy to mix different masks) 47 | 48 | 49 | ## Classical `enum class` approach 50 | 51 | The entries inside the `enum class` are the bit mask for each flag. 52 | 53 | ```cpp 54 | enum class Animal { 55 | Cat = 1 << 0, 56 | Dog = 1 << 1, 57 | Wolf = 1 << 3, 58 | }; 59 | using Animals = classic::Flags; 60 | 61 | ENABLE_CLASSIC_FLAGS_OP(Animals) 62 | 63 | // stream operator for a single value 64 | auto operator<<(std::ostream &out, Animal a) -> std::ostream & { 65 | // …see main.cpp for details 66 | } 67 | 68 | void usage() { 69 | auto a = Animal::Cat | Animal::Dog; 70 | std::cout << a << '\n'; 71 | std::cout << ((a & Animal::Cat) | Animal::Wolf) << '\n'; 72 | } 73 | ``` 74 | 75 | References: 76 | * Qt framework: `QFlags` 77 | * most of the flags helpers out there 78 | 79 | Advantages: 80 | * many C and C++ programmers are familiar with this approach. 81 | * programmer sees everything 82 | * programmer controls underlying data type 83 | * allows skipped bits 84 | 85 | Disadvantages: 86 | * programmer has to ensure that all bits are unique 87 | * hard to spot skipped bits 88 | * no iteration over unset bits possible 89 | 90 | Restrictions: 91 | * can be implemented in any C++ version 92 | * benefits from C++11 `enum class` but requires macro or type trait to allow `operator |` for enum values 93 | 94 | 95 | ## Bit number based approach 96 | 97 | **New** modification of the classical `enum class` approach.\ 98 | The entries contain the bit number of the flag. 99 | 100 | ```cpp 101 | enum class Animal { Cat, Dog, Wolf = 3 }; 102 | using Animals = bitnumber::Flags; 103 | 104 | ENABLE_BITNUMBER_FLAGS_OP(Animals) 105 | 106 | // stream operator for a single value 107 | auto operator<<(std::ostream &out, Animal a) -> std::ostream & { 108 | // …see main.cpp for details 109 | } 110 | 111 | void usage() { 112 | auto a = Animal::Cat | Animal::Dog; 113 | std::cout << a << '\n'; 114 | std::cout << ((a & Animal::Cat) | Animal::Wolf) << '\n'; 115 | } 116 | ``` 117 | 118 | References: 119 | * **new** *(I could not find an existing implementation)* 120 | 121 | Advantages: 122 | * automatic enum values work 123 | * programmer still has control 124 | * allows skipped bits 125 | 126 | Disadvantages: 127 | * programmer has to ensure all bits are unique 128 | * no iteration over unset bits possible 129 | * flag storage data type is disconnected 130 | * no way to ensure all flags fit 131 | 132 | Restrictions: 133 | * same as classical approach 134 | 135 | 136 | ## Tag type based approach 137 | 138 | All flags are represented by tag types. 139 | 140 | _Hint_: A tag type is a type we only use to get a unique name. 141 | 142 | ```cpp 143 | struct Animal { 144 | struct Cat; 145 | struct Dog; 146 | struct Wolf; 147 | }; 148 | using Animals = tagtype::Flags; 149 | 150 | auto operator<<(std::ostream &out, tagtype::Flag) -> std::ostream & { 151 | return out << "Cat"; 152 | } // … repeated for other types 153 | 154 | void usage() { 155 | auto a = Animals(Flag, Flag); 156 | std::cout << a << '\n'; 157 | std::cout << (a & Flag | Flag) << '\n'; 158 | } 159 | ``` 160 | 161 | References: 162 | * Compmaniak's `typed_flags<…>` see https://github.com/compmaniak/typed_flags 163 | 164 | Advantages: 165 | * full encapsulation 166 | * flags manages bit layout 167 | * flag type knows all possible flags 168 | * can iterate over all possible flags 169 | * types are quite flexible 170 | 171 | Disadvantages: 172 | * tags have to be repeated 173 | * less control over bit layout 174 | * hard to deprecate flags or skip bits 175 | * tag types are no values 176 | 177 | Restrictions: 178 | * requires C++11 `template` parameter packs 179 | * benefits from C++17 fold expressions - http://en.cppreference.com/w/cpp/language/fold 180 | 181 | 182 | ## Tag value based approach 183 | 184 | Each flag is represented by a tag value. 185 | 186 | ```cpp 187 | enum class Animal { Cat, Dog }; 188 | enum class AnimalWolf {}; // any type allowed 189 | 190 | using Animals = tagvalue::Flags; 191 | 192 | using tagvalue::operator<<; 193 | auto operator<<(std::ostream &out, tagvalue::Typed a) -> std::ostream & { 194 | // … see main.cpp 195 | } 196 | auto operator<<(std::ostream &out, tagvalue::Typed) -> std::ostream & { 197 | return out << "Wolf"; 198 | } 199 | 200 | void usage() { 201 | auto a = Animals(Flag, Flag); 202 | std::cout << a << '\n'; 203 | std::cout << (a & Flag | Flag) << '\n'; 204 | } 205 | ``` 206 | 207 | References: 208 | * *new* (I could not find any usages of this approach) 209 | 210 | Advantages: 211 | * full encapsulation 212 | * no restrictions to the values (they are just tags) 213 | * can iterate over all possible flags 214 | 215 | Disadvantages: 216 | * tags have to be repeated 217 | * less control over bit layout 218 | 219 | Restrictions: 220 | * requires the C++17 `template` feature 221 | 222 | 223 | ## Repeated bit value approach 224 | 225 | The underlying values of the tags are used as bit number. 226 | 227 | ```cpp 228 | enum class Animal { Cat, Dog, Wolf = 3 }; 229 | using Animals = repeated::Flags 230 | ENABLE_REPEATED_FLAGS_OP(Animals) 231 | 232 | auto operator<<(std::ostream &out, Animal a) -> std::ostream & { 233 | // … see main.cpp 234 | } 235 | 236 | void usage() { 237 | auto a = Animal::Cat | Animal::Dog; 238 | std::cout << a << '\n'; 239 | std::cout << ((a & Animal::Cat) | Animal::Wolf) << '\n'; 240 | } 241 | ``` 242 | 243 | References: 244 | * *new* (I could not find any usages of this approach) 245 | 246 | Advantages: 247 | * full encapsulation 248 | * underlying type will always fit 249 | * duplicate bit values lead to compile error 250 | * can iterate over all possible flags 251 | 252 | Disadvantages: 253 | * enum values have to be repeated 254 | 255 | Restrictions: 256 | * requires the C++17 `template` feature 257 | * C++14 requires to repeat the enum type as well. `repeated` 258 | 259 | 260 | ## Summary 261 | 262 | There is no perfect solution in C++. 263 | C++17 makes some new variants more viable. 264 | 265 | ## Contribute 266 | 267 | Feel free to add PR for improvements or new flag implementations. 268 | 269 | ## License 270 | 271 | Apache License 272 | 273 | see LICENSE file for details. -------------------------------------------------------------------------------- /tagtype/Flags.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "meta/Type.h" 3 | #include "meta/TypeList.h" 4 | 5 | #include "meta/details/BitStorage.h" 6 | 7 | #include 8 | #include 9 | 10 | namespace tagtype { 11 | 12 | template 13 | struct Flag {}; 14 | 15 | template 16 | struct FlagList {}; 17 | 18 | template 19 | struct Flags { 20 | using This = Flags; 21 | using AllOptions = meta::TypeList; 22 | using FilteredOptions = decltype(AllOptions::template filter()); 23 | static_assert(FilteredOptions::isSet, "do not repeat types"); 24 | 25 | constexpr static auto options = FilteredOptions{}; 26 | constexpr static auto bitCount = sizeof...(A); 27 | 28 | template 29 | constexpr static auto indexOf(Flag = {}) noexcept { 30 | static_assert(!std::is_same_v, "void is not a valid tag"); 31 | return AllOptions::template indexOf(); 32 | } 33 | 34 | template 35 | constexpr Flags(Flag, Flag...) noexcept 36 | : storage(std::index_sequence(), indexOf()...>{}) { 37 | static_assert(((bitCount > indexOf()) && ... && (bitCount > indexOf()))); 38 | } 39 | 40 | template 41 | constexpr Flags(FlagList = {}) noexcept 42 | : storage(std::index_sequence()...>{}) { 43 | static_assert((true && ... && (bitCount > indexOf()))); 44 | } 45 | 46 | constexpr Flags() noexcept = default; 47 | ~Flags() noexcept = default; 48 | constexpr Flags(const This &) noexcept = default; 49 | constexpr Flags(This &&) noexcept = default; 50 | constexpr auto operator=(const This &) noexcept -> This & = default; 51 | constexpr auto operator=(This &&) noexcept -> This & = default; 52 | 53 | constexpr bool operator==(const This &o) const noexcept { return storage == o.storage; } 54 | constexpr bool operator!=(const This &o) const noexcept { return !(*this == o); } 55 | 56 | template 57 | constexpr bool operator[](Flag) const noexcept { 58 | constexpr auto index = indexOf(); 59 | static_assert(bitCount > index); 60 | return storage[index]; 61 | } 62 | 63 | constexpr bool all() const noexcept { return all(setAll()); } 64 | constexpr bool all(This b) const noexcept { return (storage & b.storage) == b.storage; } 65 | template 66 | constexpr bool all(FlagList b = {}) const noexcept { 67 | static_assert(((bitCount > indexOf()) && ... && (bitCount > indexOf()))); 68 | return all(This{b}); 69 | } 70 | template 71 | constexpr bool all(Flag, Flag...) const noexcept { 72 | return all(); 73 | } 74 | 75 | constexpr bool any() const noexcept { return any(setAll()); } 76 | constexpr bool any(This b) const noexcept { return (storage & b.storage) != Storage{}; } 77 | template 78 | constexpr bool any(FlagList b = {}) const noexcept { 79 | static_assert(((bitCount > indexOf()) && ... && (bitCount > indexOf()))); 80 | return any(This{b}); 81 | } 82 | template 83 | constexpr bool any(Flag, Flag...) const noexcept { 84 | return any(); 85 | } 86 | 87 | constexpr bool none() const noexcept { return any(setAll()); } 88 | constexpr bool none(This b) const noexcept { return (storage & b.storage) == Storage{}; } 89 | template 90 | constexpr bool none(FlagList b = {}) const noexcept { 91 | static_assert(((bitCount > indexOf()) && ... && (bitCount > indexOf()))); 92 | return none(This{b}); 93 | } 94 | template 95 | constexpr bool none(Flag, Flag...) const noexcept { 96 | return none(); 97 | } 98 | 99 | constexpr static auto setAll() noexcept -> This { return This{options.template to()}; } 100 | constexpr static auto resetAll() noexcept -> This { return This{}; } 101 | constexpr auto flipAll() const noexcept -> This { 102 | auto r = This{}; 103 | r.storage = storage ^ setAll().storage; 104 | return r; 105 | } 106 | 107 | constexpr auto set(This b) const noexcept -> This { 108 | auto r = This{}; 109 | r.storage = storage | b.storage; 110 | return r; 111 | } 112 | template 113 | constexpr auto set(FlagList b = {}) const noexcept -> This { 114 | static_assert((true && ... && (bitCount > indexOf()))); 115 | return set(This{b}); 116 | } 117 | template 118 | constexpr auto set(Flag, Flag...) const noexcept -> This { 119 | return set(); 120 | } 121 | 122 | constexpr auto reset(This b) const noexcept -> This { 123 | auto r = This{}; 124 | r.storage = storage & ~b.storage; 125 | return r; 126 | } 127 | template 128 | constexpr auto reset(FlagList b = {}) const noexcept -> This { 129 | static_assert((true && ... && (bitCount > indexOf()))); 130 | return reset(This{b}); 131 | } 132 | template 133 | constexpr auto reset(Flag, Flag...) const noexcept -> This { 134 | reset(); 135 | } 136 | 137 | constexpr auto flip(This b) const noexcept -> This { 138 | auto r = This{}; 139 | r.storage = storage ^ b.storage; 140 | return r; 141 | } 142 | template 143 | constexpr auto flip(FlagList b = {}) noexcept -> This { 144 | static_assert((true && ... && (bitCount > indexOf()))); 145 | return flip(This{b}); 146 | } 147 | template 148 | constexpr auto flip(Flag, Flag...) noexcept -> This { 149 | return flip(); 150 | } 151 | 152 | constexpr auto mask(This b) const noexcept -> This { 153 | auto r = This{}; 154 | r.storage = storage & b.storage; 155 | return r; 156 | } 157 | template 158 | constexpr auto mask(FlagList b = {}) noexcept -> This { 159 | static_assert((true && ... && (bitCount > indexOf()))); 160 | return mask(This{b}); 161 | } 162 | template 163 | constexpr auto mask(Flag, Flag...) noexcept -> This { 164 | return mask(); 165 | } 166 | 167 | constexpr auto operator~() const noexcept -> This { return flipAll(); } 168 | 169 | template 170 | constexpr auto operator|(B b) const noexcept -> This { 171 | return set(b); 172 | } 173 | template 174 | constexpr auto operator&(B b) const noexcept -> This { 175 | return mask(b); 176 | } 177 | template 178 | constexpr auto operator^(B b) const noexcept -> This { 179 | return flip(b); 180 | } 181 | 182 | template 183 | constexpr auto operator|=(B b) noexcept -> This & { 184 | return *this = set(b); 185 | } 186 | template 187 | constexpr auto operator&=(B b) noexcept -> This & { 188 | return *this = mask(b); 189 | } 190 | template 191 | constexpr auto operator^=(B b) noexcept -> This & { 192 | return *this = flip(b); 193 | } 194 | 195 | template 196 | constexpr void each(F &&f) const noexcept { 197 | options.each([&, ff = std::forward(f)](auto value) mutable { 198 | auto flag = value.template to(); 199 | ff(flag, (*this)[flag]); 200 | }); 201 | } 202 | 203 | template 204 | constexpr void each_set(F &&f) const noexcept { 205 | each([ff = std::forward(f)](auto flag, bool s) mutable { 206 | if (s) ff(flag); 207 | }); 208 | } 209 | 210 | template 211 | constexpr void each_reset(F &&f) const noexcept { 212 | each([ff = std::forward(f)](auto flag, bool s) mutable { 213 | if (!s) ff(flag); 214 | }); 215 | } 216 | 217 | private: 218 | using Storage = decltype(meta::details::SelectBitStorage()); 219 | Storage storage{}; 220 | }; 221 | 222 | static_assert(Flags{FlagList{}} == FlagList{}, ""); 223 | static_assert((Flags{} | Flag{}) == Flag{}, ""); 224 | static_assert((Flags{}.set() & FlagList{}) == Flag{}, ""); 225 | static_assert(Flags{}.flip().any(Flag{}, Flag{}), ""); 226 | static_assert((Flags::setAll() & Flag{}) == Flag{}, "not all set"); 227 | 228 | template 229 | auto operator<<(Out &out, Flag) -> Out & { 230 | return out << ""; 231 | } 232 | 233 | template 234 | auto operator<<(Out &out, Flags t) -> Out & { 235 | using Flags = Flags; 236 | if (t == Flags{}) return out << ""; 237 | t.each_set([&, first = true](auto f) mutable { 238 | if (!first) 239 | out << " | "; 240 | else 241 | first = false; 242 | out << f; 243 | }); 244 | return out; 245 | } 246 | 247 | } // namespace tagtype 248 | -------------------------------------------------------------------------------- /repeated/Flags.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "meta/Value.h" 3 | #include "meta/ValueList.h" 4 | 5 | #include "meta/details/BitStorage.h" 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | namespace repeated { 12 | 13 | #define ENABLE_REPEATED_FLAGS_OP(T) \ 14 | constexpr auto operator|(T::EnumType e1, T::EnumType e2) noexcept->T { return T(e1) | e2; } 15 | 16 | template 17 | struct FlagList {}; 18 | 19 | template 20 | struct Flags { 21 | using This = Flags; 22 | using Options = meta::ValueList; 23 | static_assert(Options::isSameType, "all Flag values shoud have same type"); 24 | static_assert(Options::isSet, "Flag bit values have to be unique"); 25 | using EnumType = typename Options::commonType; 26 | static_assert(std::is_enum_v, "use Flags with enum types"); 27 | 28 | using UnderlyingType = typename Options::underlyingType; 29 | static constexpr auto options = Options{}; 30 | static constexpr auto min = static_cast(options.min); 31 | static constexpr auto max = static_cast(options.max); 32 | static constexpr auto bitCount = 1 + max - min; 33 | 34 | constexpr static auto indexOf(EnumType b) noexcept -> size_t { return static_cast(b) - min; } 35 | 36 | template 37 | constexpr Flags(EnumType v, Args... args) noexcept 38 | : storage({indexOf(v), indexOf(args)...}) {} 39 | 40 | template 41 | constexpr Flags(FlagList = {}) noexcept 42 | : storage(std::index_sequence{}) {} 43 | 44 | constexpr Flags() noexcept = default; 45 | ~Flags() noexcept = default; 46 | constexpr Flags(const This &) noexcept = default; 47 | constexpr Flags(This &&) noexcept = default; 48 | constexpr auto operator=(const This &) noexcept -> This & = default; 49 | constexpr auto operator=(This &&) noexcept -> This & = default; 50 | 51 | constexpr bool operator==(const This &o) const noexcept { return storage == o.storage; } 52 | constexpr bool operator!=(const This &o) const noexcept { return !(*this == o); } 53 | 54 | constexpr bool operator[](EnumType b) const noexcept { return storage[indexOf(b)]; } 55 | 56 | constexpr bool all() const noexcept { return all(setAll()); } 57 | constexpr bool all(This b) const noexcept { return (storage & b.storage) == b.storage; } 58 | template 59 | constexpr bool all(FlagList b = {}) const noexcept { 60 | static_assert(((bitCount > indexOf(B)) && ... && (bitCount > indexOf(C)))); 61 | return all(This{b}); 62 | } 63 | template 64 | constexpr bool all(EnumType b, Args... args) const noexcept { 65 | return all(build(b, args...)); 66 | } 67 | 68 | constexpr bool any() const noexcept { return any(setAll()); } 69 | constexpr bool any(This b) const noexcept { return (storage & b.storage) != Storage{}; } 70 | template 71 | constexpr bool any(FlagList b = {}) const noexcept { 72 | static_assert(((bitCount > indexOf(B)) && ... && (bitCount > indexOf(C)))); 73 | return any(This{b}); 74 | } 75 | template 76 | constexpr bool any(EnumType b, Args... args) const noexcept { 77 | return any(build(b, args...)); 78 | } 79 | 80 | constexpr bool none() const noexcept { return any(setAll()); } 81 | constexpr bool none(This b) const noexcept { return (storage & b.storage) == Storage{}; } 82 | template 83 | constexpr bool none(FlagList b = {}) const noexcept { 84 | static_assert(((bitCount > indexOf(B)) && ... && (bitCount > indexOf(C)))); 85 | return none(This{b}); 86 | } 87 | template 88 | constexpr bool none(EnumType b, Args... args) const noexcept { 89 | return none(build(b, args...)); 90 | } 91 | 92 | constexpr static auto setAll() noexcept -> This { return This{options.template to()}; } 93 | constexpr static auto resetAll() noexcept -> This { return This{}; } 94 | constexpr auto flipAll() const noexcept -> This { 95 | auto r = This{}; 96 | r.storage = storage ^ setAll().storage; 97 | return r; 98 | } 99 | 100 | constexpr auto set(This b) const noexcept -> This { 101 | auto r = This{}; 102 | r.storage = storage | b.storage; 103 | return r; 104 | } 105 | template 106 | constexpr auto set(FlagList b = {}) const noexcept -> This { 107 | static_assert((true && ... && (bitCount > indexOf(B)))); 108 | return set(This{b}); 109 | } 110 | template 111 | constexpr auto set(EnumType b, Args... args) const noexcept -> This { 112 | return set(build(b, args...)); 113 | } 114 | 115 | constexpr auto reset(This b) const noexcept -> This { 116 | auto r = This{}; 117 | r.storage = storage & ~b.storage; 118 | return r; 119 | } 120 | template 121 | constexpr auto reset(FlagList b = {}) const noexcept -> This { 122 | static_assert((true && ... && (bitCount > indexOf(B)))); 123 | return reset(This{b}); 124 | } 125 | template 126 | constexpr auto reset(EnumType b, Args... args) const noexcept -> This { 127 | return reset(build(b, args...)); 128 | } 129 | 130 | constexpr auto flip(This b) const noexcept -> This { 131 | auto r = This{}; 132 | r.storage = storage ^ b.storage; 133 | return r; 134 | } 135 | template 136 | constexpr auto flip(FlagList b = {}) noexcept -> This { 137 | static_assert((true && ... && (bitCount > indexOf(B)))); 138 | return flip(This{b}); 139 | } 140 | template 141 | constexpr auto flip(EnumType b, Args... args) const noexcept -> This { 142 | return flip(build(b, args...)); 143 | } 144 | 145 | constexpr auto mask(This b) const noexcept -> This { 146 | auto r = This{}; 147 | r.storage = storage & b.storage; 148 | return r; 149 | } 150 | template 151 | constexpr auto mask(FlagList b = {}) noexcept -> This { 152 | static_assert((true && ... && (bitCount > indexOf(B)))); 153 | return mask(This{b}); 154 | } 155 | template 156 | constexpr auto mask(EnumType b, Args... args) const noexcept -> This { 157 | return mask(build(b, args...)); 158 | } 159 | 160 | constexpr auto operator~() const noexcept -> This { return flipAll(); } 161 | 162 | template 163 | constexpr auto operator|(B b) const noexcept -> This { 164 | return set(b); 165 | } 166 | template 167 | constexpr auto operator&(B b) const noexcept -> This { 168 | return mask(b); 169 | } 170 | template 171 | constexpr auto operator^(B b) const noexcept -> This { 172 | return flip(b); 173 | } 174 | 175 | template 176 | constexpr auto operator|=(B b) noexcept -> This & { 177 | return *this = set(b); 178 | } 179 | template 180 | constexpr auto operator&=(B b) noexcept -> This & { 181 | return *this = mask(b); 182 | } 183 | template 184 | constexpr auto operator^=(B b) noexcept -> This & { 185 | return *this = flip(b); 186 | } 187 | 188 | template 189 | constexpr void each(F &&f) const noexcept { 190 | options.each([&, ff = std::forward(f)](auto value) mutable { 191 | auto flag = value.value(); 192 | ff(flag, (*this)[flag]); 193 | }); 194 | } 195 | 196 | template 197 | constexpr void each_set(F &&f) const noexcept { 198 | each([ff = std::forward(f)](auto flag, bool s) mutable { 199 | if (s) ff(flag); 200 | }); 201 | } 202 | 203 | template 204 | constexpr void each_reset(F &&f) const noexcept { 205 | each([ff = std::forward(f)](auto flag, bool s) mutable { 206 | if (!s) ff(flag); 207 | }); 208 | } 209 | 210 | private: 211 | template 212 | static constexpr auto build(Args... args) noexcept -> Flags { 213 | return Flags{args...}; 214 | } 215 | 216 | private: 217 | using Storage = decltype(meta::details::SelectBitStorage()); 218 | Storage storage{}; 219 | }; 220 | 221 | namespace test { 222 | 223 | enum class TE { n1, n2, n3 = 4 }; 224 | static_assert(Flags{TE::n1, TE::n2} == FlagList{}, ""); 225 | static_assert((Flags{} | TE::n1) == FlagList{}, ""); 226 | static_assert((Flags{}.set(TE::n1, TE::n3) & FlagList{}) == FlagList{}, 227 | ""); 228 | static_assert(Flags{}.flip(TE::n2, TE::n3).any(TE::n1, TE::n2), ""); 229 | static_assert((Flags::setAll() & TE::n2) == FlagList{}, "not all set"); 230 | 231 | } // namespace test 232 | 233 | template 234 | auto operator<<(Out &out, Flags t) -> Out & { 235 | using Flags = Flags; 236 | if (t == Flags{}) return out << ""; 237 | t.each_set([&, first = true](auto f) mutable { 238 | if (!first) 239 | out << " | "; 240 | else 241 | first = false; 242 | out << f; 243 | }); 244 | return out; 245 | } 246 | 247 | } // namespace repeated 248 | -------------------------------------------------------------------------------- /tagvalue/Flags.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "meta/Value.h" 3 | #include "meta/ValueList.h" 4 | 5 | #include "meta/details/BitStorage.h" 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | namespace tagvalue { 12 | 13 | template 14 | struct Flag {}; 15 | 16 | template 17 | struct FlagList {}; 18 | 19 | template 20 | struct Flags { 21 | using This = Flags; 22 | using AllOptions = meta::ValueList; 23 | using FilteredOptions = decltype(AllOptions::template filter()); 24 | static_assert(FilteredOptions::isSet, "do not repeat values"); 25 | 26 | constexpr static auto options = FilteredOptions{}; 27 | constexpr static auto bitCount = sizeof...(A); 28 | 29 | template 30 | constexpr static auto indexOf(Flag = {}) noexcept { 31 | static_assert(!std::is_same_v, "nullptr is not a valid tag"); 32 | return AllOptions::template indexOf(); 33 | } 34 | 35 | template 36 | constexpr Flags(Flag, Flag...) noexcept 37 | : storage(std::index_sequence(), indexOf()...>{}) { 38 | static_assert(((bitCount > indexOf()) && ... && (bitCount > indexOf()))); 39 | } 40 | 41 | template 42 | constexpr Flags(FlagList = {}) noexcept 43 | : storage(std::index_sequence()...>{}) { 44 | static_assert((true && ... && (bitCount > indexOf()))); 45 | } 46 | 47 | constexpr Flags() noexcept = default; 48 | ~Flags() noexcept = default; 49 | constexpr Flags(const This &) noexcept = default; 50 | constexpr Flags(This &&) noexcept = default; 51 | constexpr auto operator=(const This &) noexcept -> This & = default; 52 | constexpr auto operator=(This &&) noexcept -> This & = default; 53 | 54 | constexpr bool operator==(const This &o) const noexcept { return storage == o.storage; } 55 | constexpr bool operator!=(const This &o) const noexcept { return !(*this == o); } 56 | 57 | template 58 | constexpr bool operator[](Flag) const noexcept { 59 | constexpr auto index = indexOf(); 60 | static_assert(bitCount > index); 61 | return storage[index]; 62 | } 63 | 64 | constexpr bool all() const noexcept { return all(setAll()); } 65 | constexpr bool all(This b) const noexcept { return (storage & b.storage) == b.storage; } 66 | template 67 | constexpr bool all(FlagList b = {}) const noexcept { 68 | static_assert(((bitCount > indexOf()) && ... && (bitCount > indexOf()))); 69 | return all(This{b}); 70 | } 71 | template 72 | constexpr bool all(Flag, Flag...) const noexcept { 73 | return all(); 74 | } 75 | 76 | constexpr bool any() const noexcept { return any(setAll()); } 77 | constexpr bool any(This b) const noexcept { return (storage & b.storage) != Storage{}; } 78 | template 79 | constexpr bool any(FlagList b = {}) const noexcept { 80 | static_assert(((bitCount > indexOf()) && ... && (bitCount > indexOf()))); 81 | return any(This{b}); 82 | } 83 | template 84 | constexpr bool any(Flag, Flag...) const noexcept { 85 | return any(); 86 | } 87 | 88 | constexpr bool none() const noexcept { return any(setAll()); } 89 | constexpr bool none(This b) const noexcept { return (storage & b.storage) == Storage{}; } 90 | template 91 | constexpr bool none(FlagList b = {}) const noexcept { 92 | static_assert(((bitCount > indexOf()) && ... && (bitCount > indexOf()))); 93 | return none(This{b}); 94 | } 95 | template 96 | constexpr bool none(Flag, Flag...) const noexcept { 97 | return none(); 98 | } 99 | 100 | constexpr static auto setAll() noexcept -> This { return This{options.template to()}; } 101 | constexpr static auto resetAll() noexcept -> This { return This{}; } 102 | constexpr auto flipAll() const noexcept -> This { 103 | auto r = This{}; 104 | r.storage = storage ^ setAll().storage; 105 | return r; 106 | } 107 | 108 | constexpr auto set(This b) const noexcept -> This { 109 | auto r = This{}; 110 | r.storage = storage | b.storage; 111 | return r; 112 | } 113 | template 114 | constexpr auto set(FlagList b = {}) const noexcept -> This { 115 | static_assert((true && ... && (bitCount > indexOf()))); 116 | return set(This{b}); 117 | } 118 | template 119 | constexpr auto set(Flag, Flag...) const noexcept -> This { 120 | return set(); 121 | } 122 | 123 | constexpr auto reset(This b) const noexcept -> This { 124 | auto r = This{}; 125 | r.storage = storage & ~b.storage; 126 | return r; 127 | } 128 | template 129 | constexpr auto reset(FlagList b = {}) const noexcept -> This { 130 | static_assert((true && ... && (bitCount > indexOf()))); 131 | return reset(This{b}); 132 | } 133 | template 134 | constexpr auto reset(Flag, Flag...) const noexcept -> This { 135 | reset(); 136 | } 137 | 138 | constexpr auto flip(This b) const noexcept -> This { 139 | auto r = This{}; 140 | r.storage = storage ^ b.storage; 141 | return r; 142 | } 143 | template 144 | constexpr auto flip(FlagList b = {}) noexcept -> This { 145 | static_assert((true && ... && (bitCount > indexOf()))); 146 | return flip(This{b}); 147 | } 148 | template 149 | constexpr auto flip(Flag, Flag...) noexcept -> This { 150 | return flip(); 151 | } 152 | 153 | constexpr auto mask(This b) const noexcept -> This { 154 | auto r = This{}; 155 | r.storage = storage & b.storage; 156 | return r; 157 | } 158 | template 159 | constexpr auto mask(FlagList b = {}) noexcept -> This { 160 | static_assert((true && ... && (bitCount > indexOf()))); 161 | return mask(This{b}); 162 | } 163 | template 164 | constexpr auto mask(Flag, Flag...) noexcept -> This { 165 | return mask(); 166 | } 167 | 168 | constexpr auto operator~() const noexcept -> This { return flipAll(); } 169 | 170 | template 171 | constexpr auto operator|(B b) const noexcept -> This { 172 | return set(b); 173 | } 174 | template 175 | constexpr auto operator&(B b) const noexcept -> This { 176 | return mask(b); 177 | } 178 | template 179 | constexpr auto operator^(B b) const noexcept -> This { 180 | return flip(b); 181 | } 182 | 183 | template 184 | constexpr auto operator|=(B b) noexcept -> This & { 185 | return *this = set(b); 186 | } 187 | template 188 | constexpr auto operator&=(B b) noexcept -> This & { 189 | return *this = mask(b); 190 | } 191 | template 192 | constexpr auto operator^=(B b) noexcept -> This & { 193 | return *this = flip(b); 194 | } 195 | 196 | template 197 | constexpr void each(F &&f) const noexcept { 198 | options.each([&, ff = std::forward(f)](auto value) mutable { 199 | auto flag = value.template to(); 200 | ff(flag, (*this)[flag]); 201 | }); 202 | } 203 | 204 | template 205 | constexpr void each_set(F &&f) const noexcept { 206 | each([ff = std::forward(f)](auto flag, bool s) mutable { 207 | if (s) ff(flag); 208 | }); 209 | } 210 | 211 | template 212 | constexpr void each_reset(F &&f) const noexcept { 213 | each([ff = std::forward(f)](auto flag, bool s) mutable { 214 | if (!s) ff(flag); 215 | }); 216 | } 217 | 218 | private: 219 | using Storage = decltype(meta::details::SelectBitStorage()); 220 | Storage storage{}; 221 | }; 222 | 223 | static_assert(Flags<1, 2, 3>{FlagList<1, 2>{}} == FlagList<1, 2>{}, ""); 224 | static_assert((Flags<1, 2>{} | Flag<1>{}) == Flag<1>{}, ""); 225 | static_assert((Flags<1, 2, 3>{}.set<1, 3>() & FlagList<1, 2>{}) == Flag<1>{}, ""); 226 | static_assert(Flags<1, 2, 3>{}.flip<2, 3>().any(Flag<1>{}, Flag<2>{}), ""); 227 | static_assert((Flags<1, 2, 3>::setAll() & Flag<2>{}) == Flag<2>{}, "not all set"); 228 | 229 | template 230 | struct Typed { 231 | T value; 232 | 233 | Typed(T v) 234 | : value(v) {} 235 | }; 236 | template 237 | struct HasTyped : std::false_type {}; 238 | 239 | template 240 | struct HasTyped(), Typed{A}))>> : std::true_type {}; 241 | 242 | template 243 | constexpr bool hasTyped = HasTyped::value; 244 | 245 | template 246 | auto operator<<(Out &out, Typed) -> Out & { 247 | return out << ""; 248 | } 249 | 250 | template 251 | auto operator<<(Out &out, Flag) -> Out & { 252 | return out << Typed{A}; 253 | } 254 | 255 | template 256 | auto operator<<(Out &out, Flags t) -> Out & { 257 | using Flags = Flags; 258 | if (t == Flags{}) return out << ""; 259 | t.each_set([&, first = true](auto f) mutable { 260 | if (!first) 261 | out << " | "; 262 | else 263 | first = false; 264 | out << f; 265 | }); 266 | return out; 267 | } 268 | 269 | } // namespace tagvalue 270 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | --------------------------------------------------------------------------------