├── .clang-format ├── .gitignore ├── LICENSE ├── README.md ├── include └── flag_set.hpp └── tests ├── CMakeLists.txt ├── README.md └── src └── flag-set-tests.cpp /.clang-format: -------------------------------------------------------------------------------- 1 | BasedOnStyle: WebKit 2 | 3 | Standard: Cpp11 4 | ColumnLimit: 100 5 | 6 | # Disable reflow of qdoc comments: indentation rules are different. 7 | # Translation comments are also excluded 8 | CommentPragmas: "^!|^:" 9 | 10 | # We want & and * to be attached to the type 11 | PointerBindsToType: true 12 | 13 | # We want long lines to break before the operators, but not before a '=' 14 | BreakBeforeBinaryOperators: NonAssignment 15 | 16 | # Braces are usually attached, but not after functions or classes declaration 17 | BreakBeforeBraces: Custom 18 | BraceWrapping: 19 | AfterClass: true 20 | AfterControlStatement: false 21 | AfterEnum: false 22 | AfterFunction: true 23 | AfterNamespace: true 24 | AfterObjCDeclaration: false 25 | AfterStruct: true 26 | AfterUnion: false 27 | BeforeCatch: false 28 | BeforeElse: false 29 | IndentBraces: false 30 | 31 | AlignAfterOpenBracket: true 32 | AlwaysBreakTemplateDeclarations: true 33 | AllowShortFunctionsOnASingleLine: Inline 34 | FixNamespaceComments: true 35 | Cpp11BracedListStyle: true 36 | BreakConstructorInitializers: AfterColon 37 | 38 | SortIncludes: false 39 | 40 | ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH, forever, Q_FOREVER, QBENCHMARK, QBENCHMARK_ONCE ] 41 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.a 2 | *.swp 3 | *.o 4 | *.user 5 | *.aps 6 | *.exe 7 | *.ncb 8 | *.obj 9 | *.pdb 10 | *.pyc 11 | *.suo 12 | *.swp 13 | *.user 14 | [Bb]in/ 15 | [Oo]bj/ 16 | [Ll]ib/ 17 | [Dd]ebug/ 18 | [Rr]elease/ 19 | [Bb]uild/ 20 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Arnaud Kapp (Xaqq), Barry Revzin, Mart Sõmermaa 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # flag-set-cpp 2 | 3 | `flag_set` is a type-safe class for using enums as flags in C++14 with an underlying `std::bitset`. 4 | 5 | The original idea is by [Arnaud Kapp (Xaqq)](https://codereview.stackexchange.com/questions/96146/c-flagset-typesafe-usage-of-enumeration-as-bitset-bitmask) 6 | with additions from 7 | [Barry Revzin](https://codereview.stackexchange.com/users/31292/barry). 8 | 9 | ## Usage 10 | 11 | `flag_set` is a header-only library, simply copy `include/flag_set.hpp` to your 12 | project to use it. 13 | 14 | The `enum` type of the `flag_set` **must have** a last value sentinel `_`, 15 | a single underscore character. Underscore was chosen as it stands out clearly and is 16 | unlikely to collide with a real enum value. 17 | 18 | Usage example: 19 | 20 | ```c++ 21 | #include "flag_set.hpp" 22 | 23 | enum class Options : uint8_t { 24 | FULLSCREEN, 25 | INVERT_MOUSE, 26 | FLASH, 27 | RED_BACKGROUND, 28 | RED_FOREGROUND, 29 | _ 30 | }; 31 | 32 | int main() 33 | { 34 | flag_set red(Options::RED_FOREGROUND | Options::RED_BACKGROUND); 35 | 36 | if (red[Options::RED_BACKGROUND]) // or red & Options::RED_BACKGROUND 37 | cout << "Red background activated"; 38 | } 39 | ``` 40 | 41 | See more examples in [tests](tests/src/flag-set-tests.cpp). 42 | 43 | ## Contributing 44 | 45 | Run `clang-format` on patches as follows: 46 | 47 | ```sh 48 | find include/ tests/ -iname '*.hpp' -o -iname '*.h' -o -iname '*.cpp' | xargs clang-format -i 49 | ``` 50 | -------------------------------------------------------------------------------- /include/flag_set.hpp: -------------------------------------------------------------------------------- 1 | // flag_set is a type-safe class for using enums as flags in C++14 with an underlying std::bitset. 2 | // See https://github.com/mrts/flag-set-cpp 3 | // Licence: MIT, see LICENCE 4 | 5 | #pragma once 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | template 13 | class flag_set 14 | { 15 | public: 16 | flag_set() = default; 17 | 18 | explicit flag_set(const T& val) { flags.set(static_cast(val)); } 19 | 20 | // Binary operations. 21 | 22 | flag_set& operator&=(const T& val) noexcept 23 | { 24 | bool tmp = flags.test(static_cast(val)); 25 | flags.reset(); 26 | flags.set(static_cast(val), tmp); 27 | return *this; 28 | } 29 | 30 | flag_set& operator&=(const flag_set& o) noexcept 31 | { 32 | flags &= o.flags; 33 | return *this; 34 | } 35 | 36 | flag_set& operator|=(const T& val) noexcept 37 | { 38 | flags.set(static_cast(val)); 39 | return *this; 40 | } 41 | 42 | flag_set& operator|=(const flag_set& o) noexcept 43 | { 44 | flags |= o.flags; 45 | return *this; 46 | } 47 | 48 | // The resulting bitset can contain at most 1 bit. 49 | flag_set operator&(const T& val) const 50 | { 51 | flag_set ret(*this); 52 | ret &= val; 53 | 54 | assert(ret.flags.count() <= 1); 55 | return ret; 56 | } 57 | 58 | flag_set operator&(const flag_set& val) const 59 | { 60 | flag_set ret(*this); 61 | ret.flags &= val.flags; 62 | 63 | return ret; 64 | } 65 | 66 | // The resulting bitset contains at least 1 bit. 67 | flag_set operator|(const T& val) const 68 | { 69 | flag_set ret(*this); 70 | ret |= val; 71 | 72 | assert(ret.flags.count() >= 1); 73 | return ret; 74 | } 75 | 76 | flag_set operator|(const flag_set& val) const 77 | { 78 | flag_set ret(*this); 79 | ret.flags |= val.flags; 80 | 81 | return ret; 82 | } 83 | 84 | flag_set operator~() const 85 | { 86 | flag_set cp(*this); 87 | cp.flags.flip(); 88 | 89 | return cp; 90 | } 91 | 92 | // The bitset evaluates to true if any bit is set. 93 | explicit operator bool() const { return flags.any(); } 94 | 95 | // Methods from std::bitset. 96 | 97 | bool operator==(const flag_set& o) const { return flags == o.flags; } 98 | 99 | std::size_t size() const { return flags.size(); } 100 | 101 | std::size_t count() const { return flags.count(); } 102 | 103 | flag_set& set() 104 | { 105 | flags.set(); 106 | return *this; 107 | } 108 | 109 | flag_set& reset() 110 | { 111 | flags.reset(); 112 | return *this; 113 | } 114 | 115 | flag_set& flip() 116 | { 117 | flags.flip(); 118 | return *this; 119 | } 120 | 121 | flag_set& set(const T& val, bool value = true) 122 | { 123 | flags.set(static_cast(val), value); 124 | return *this; 125 | } 126 | 127 | flag_set& reset(const T& val) 128 | { 129 | flags.reset(static_cast(val)); 130 | return *this; 131 | } 132 | 133 | flag_set& flip(const T& val) 134 | { 135 | flags.flip(static_cast(val)); 136 | return *this; 137 | } 138 | 139 | constexpr bool operator[](const T& val) const { return flags[static_cast(val)]; } 140 | 141 | std::string to_string() const { return flags.to_string(); } 142 | 143 | // Operator for outputting to std::ostream. 144 | friend std::ostream& operator<<(std::ostream& stream, const flag_set& self) 145 | { 146 | return stream << self.flags; 147 | } 148 | 149 | private: 150 | using u_type = std::underlying_type_t; 151 | 152 | // _ is last value sentinel and must be present in enum T. 153 | std::bitset(T::_)> flags; 154 | }; 155 | 156 | template 157 | struct is_enum_that_contains_sentinel : std::false_type 158 | { 159 | }; 160 | 161 | template 162 | struct is_enum_that_contains_sentinel(T::_))> : std::is_enum 163 | { 164 | }; 165 | 166 | // Operator that combines two enumeration values into a flag_set only if the 167 | // enumeration contains the sentinel `_`. 168 | template 169 | std::enable_if_t::value, flag_set> operator|(const T& lhs, 170 | const T& rhs) 171 | { 172 | flag_set fs; 173 | fs |= lhs; 174 | fs |= rhs; 175 | 176 | return fs; 177 | } 178 | -------------------------------------------------------------------------------- /tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.8.0) 2 | 3 | project(flag-set) 4 | 5 | # Common compile options. 6 | 7 | set(CMAKE_CXX_STANDARD 14) 8 | 9 | if(MSVC) 10 | add_compile_options(/W4 /WX) 11 | else(MSVC) 12 | add_compile_options(-Wall -Wextra -pedantic -Werror) 13 | endif(MSVC) 14 | 15 | # Tests with Google Test. 16 | 17 | enable_testing() 18 | 19 | find_package(GTest REQUIRED) 20 | 21 | set(TEST_EXE ${PROJECT_NAME}-test) 22 | 23 | include_directories(../include) 24 | 25 | add_executable(${TEST_EXE} 26 | src/${PROJECT_NAME}-tests.cpp 27 | ) 28 | 29 | target_link_libraries(${TEST_EXE} 30 | GTest::GTest GTest::Main 31 | ) 32 | 33 | add_test(${TEST_EXE} ${TEST_EXE}) 34 | -------------------------------------------------------------------------------- /tests/README.md: -------------------------------------------------------------------------------- 1 | # Tests for flag-set-cpp 2 | 3 | Building tests: 4 | 5 | apt install build-essential pkg-config cmake libgtest-dev 6 | sudo bash -c 'cd /usr/src/googletest && cmake . && cmake --build . --target install' 7 | 8 | cd build 9 | cmake .. # optionally with -DCMAKE_BUILD_TYPE=Debug 10 | cmake --build . # optionally with VERBOSE=1 11 | 12 | Running tests, inside `build` directory: 13 | 14 | ctest # optionally with -V 15 | -------------------------------------------------------------------------------- /tests/src/flag-set-tests.cpp: -------------------------------------------------------------------------------- 1 | #include "flag_set.hpp" 2 | 3 | #include "gtest/gtest.h" 4 | 5 | #include 6 | 7 | enum class Options : uint8_t { 8 | FULLSCREEN, 9 | INVERT_MOUSE, 10 | FLASH, 11 | RED_BACKGROUND, 12 | RED_FOREGROUND, 13 | _ 14 | }; 15 | 16 | TEST(flag_set, test_and) 17 | { 18 | flag_set red(Options::RED_FOREGROUND | Options::RED_BACKGROUND); 19 | 20 | auto result = red & Options::RED_BACKGROUND; 21 | EXPECT_TRUE(result); 22 | EXPECT_EQ(result.count(), 1); 23 | 24 | result = red & Options::RED_FOREGROUND; 25 | EXPECT_TRUE(result); 26 | EXPECT_EQ(result.count(), 1); 27 | 28 | result = red & (Options::RED_FOREGROUND | Options::RED_BACKGROUND); 29 | EXPECT_TRUE(result); 30 | EXPECT_EQ(result.count(), 2); 31 | 32 | result = ~red & Options::RED_BACKGROUND; 33 | EXPECT_FALSE(result); 34 | EXPECT_EQ(result.count(), 0); 35 | 36 | flag_set red_foreground(Options::RED_FOREGROUND); 37 | red &= Options::RED_FOREGROUND; 38 | EXPECT_TRUE(red == red_foreground); 39 | } 40 | 41 | TEST(flag_set, test_or) 42 | { 43 | flag_set red; 44 | red |= Options::RED_FOREGROUND | Options::RED_BACKGROUND; 45 | EXPECT_TRUE(red); 46 | EXPECT_EQ(red.count(), 2); 47 | 48 | flag_set options; 49 | options |= (Options::FULLSCREEN | Options::FLASH); 50 | 51 | auto result = options & (Options::FULLSCREEN | Options::FLASH | Options::RED_FOREGROUND); 52 | EXPECT_TRUE(result); 53 | 54 | flag_set expected; 55 | expected |= Options::FULLSCREEN; 56 | EXPECT_EQ(options & expected, expected); 57 | 58 | result = options & (Options::RED_FOREGROUND | Options::RED_BACKGROUND); 59 | EXPECT_FALSE(result); 60 | 61 | EXPECT_FALSE(options & red); 62 | 63 | EXPECT_TRUE(!(options & Options::INVERT_MOUSE)); 64 | 65 | options |= ~red; 66 | 67 | EXPECT_TRUE(options & Options::INVERT_MOUSE); 68 | } 69 | 70 | TEST(flag_set, test_set_reset) 71 | { 72 | flag_set options; 73 | EXPECT_EQ(options.count(), 0); 74 | 75 | options.set(); 76 | EXPECT_EQ(options.count(), 5); 77 | EXPECT_EQ(options.size(), 5); 78 | EXPECT_EQ(options.to_string(), "11111"); 79 | 80 | options.reset(); 81 | EXPECT_EQ(options.count(), 0); 82 | 83 | options.set(Options::FLASH); 84 | EXPECT_EQ(options.count(), 1); 85 | EXPECT_TRUE(options[Options::FLASH]); 86 | 87 | options.set(Options::FLASH, false); 88 | EXPECT_EQ(options.count(), 0); 89 | 90 | options.set(Options::FLASH); 91 | options.set(Options::INVERT_MOUSE); 92 | EXPECT_EQ(options.count(), 2); 93 | } 94 | --------------------------------------------------------------------------------