├── examples ├── CMakeLists.txt └── file_permission.cpp ├── include ├── CMakeLists.txt └── river │ └── flag.hpp ├── tests ├── CMakeLists.txt └── main.cpp ├── .github └── workflows │ └── c-cpp.yml ├── .clang-format ├── CMakeLists.txt ├── LICENSE.md ├── .gitignore └── README.md /examples/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(file_permission file_permission.cpp) 2 | target_link_libraries(file_permission PRIVATE river::flag) -------------------------------------------------------------------------------- /include/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(river-flag INTERFACE) 2 | add_library(river::flag ALIAS river-flag) 3 | target_include_directories(river-flag INTERFACE ${CMAKE_CURRENT_LIST_DIR}) 4 | target_compile_features(river-flag INTERFACE cxx_std_20) -------------------------------------------------------------------------------- /tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(tests main.cpp) 2 | target_link_libraries(tests PRIVATE river::flag) 3 | if(MSVC) 4 | target_compile_options(tests PRIVATE /permissive- /W4 /WX /w14640 /w14242 /w14826) 5 | else() 6 | target_compile_options(tests PRIVATE -Wall -Wextra -pedantic -Wconversion -Wsign-conversion -Werror) 7 | endif() 8 | add_test(tests tests) -------------------------------------------------------------------------------- /.github/workflows/c-cpp.yml: -------------------------------------------------------------------------------- 1 | name: C/C++ CI 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | jobs: 10 | build: 11 | 12 | runs-on: ubuntu-latest 13 | 14 | steps: 15 | - uses: actions/checkout@v2 16 | - name: run-cmake 17 | uses: lukka/run-cmake@v2.5 18 | with: 19 | cmakeAppendedArgs: "-DFLAG_INCLUDE_TESTS=1" 20 | -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | Language: Cpp 2 | BasedOnStyle: LLVM 3 | AlignConsecutiveAssignments: true 4 | AlignOperands: true 5 | AllowShortFunctionsOnASingleLine: Empty 6 | AlwaysBreakTemplateDeclarations: Yes 7 | BinPackArguments: false 8 | BinPackParameters: false 9 | BreakConstructorInitializers: BeforeComma 10 | BreakInheritanceList: BeforeComma 11 | ColumnLimit: 100 12 | IncludeBlocks: Regroup 13 | KeepEmptyLinesAtTheStartOfBlocks: false -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.8) 2 | project("Flag" 3 | VERSION 0.0 4 | LANGUAGES CXX) 5 | 6 | set(CXX_STANDARD 20) 7 | 8 | add_subdirectory(include) 9 | 10 | if (${FLAG_INCLUDE_EXAMPLES}) 11 | message(STATUS "Including examples") 12 | enable_testing() 13 | add_subdirectory(examples) 14 | endif() 15 | 16 | if (${FLAG_INCLUDE_TESTS}) 17 | message(STATUS "Including tests") 18 | enable_testing() 19 | add_subdirectory(tests) 20 | endif() -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Uy Ha 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 | -------------------------------------------------------------------------------- /examples/file_permission.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | namespace filesystem { 6 | enum class FilePermission : std::uint16_t { 7 | all_execute = 0b0'000'000'001, 8 | all_write = 0b0'000'000'010, 9 | all_read = 0b0'000'000'100, 10 | 11 | group_execute = 0b0'000'001'000, 12 | group_write = 0b0'000'010'000, 13 | group_read = 0b0'000'100'000, 14 | 15 | user_execute = 0b0'001'000'000, 16 | user_write = 0b0'010'000'000, 17 | user_read = 0b0'100'000'000, 18 | 19 | directory = 0b1'000'000'000 20 | }; 21 | IS_FLAG_ENUM(FilePermission); // Opting in making FilePermission enum as a flag enum 22 | } // namespace filesystem 23 | 24 | int main() { 25 | using namespace river::flags; // All operators are define in the river namespace 26 | using filesystem::FilePermission; 27 | 28 | // type of file_permission is std::uint16_t 29 | auto file_permission = FilePermission::all_execute | FilePermission::user_execute; 30 | 31 | assert(has(file_permission)); 32 | assert(has(file_permission)); 33 | 34 | // Add group_execute to file_permission 35 | file_permission |= FilePermission::group_execute; 36 | assert(has(file_permission)); 37 | 38 | // Toggle user_execute bit of file_permission 39 | file_permission ^= FilePermission::user_execute; 40 | assert(!has(file_permission)); 41 | file_permission ^= FilePermission::user_execute; 42 | assert(has(file_permission)); 43 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Build folders 2 | build/** 3 | 4 | # VS Code 5 | .vscode/settings.json 6 | 7 | # Visual Studio 8 | out/** 9 | .vs/** 10 | CMakeSettings.json 11 | 12 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider 13 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 14 | 15 | # User-specific stuff 16 | .idea/**/workspace.xml 17 | .idea/**/tasks.xml 18 | .idea/**/usage.statistics.xml 19 | .idea/**/dictionaries 20 | .idea/**/shelf 21 | 22 | # Generated files 23 | .idea/**/contentModel.xml 24 | 25 | # Sensitive or high-churn files 26 | .idea/**/dataSources/ 27 | .idea/**/dataSources.ids 28 | .idea/**/dataSources.local.xml 29 | .idea/**/sqlDataSources.xml 30 | .idea/**/dynamic.xml 31 | .idea/**/uiDesigner.xml 32 | .idea/**/dbnavigator.xml 33 | 34 | # Gradle 35 | .idea/**/gradle.xml 36 | .idea/**/libraries 37 | 38 | # Gradle and Maven with auto-import 39 | # When using Gradle or Maven with auto-import, you should exclude module files, 40 | # since they will be recreated, and may cause churn. Uncomment if using 41 | # auto-import. 42 | # .idea/artifacts 43 | # .idea/compiler.xml 44 | # .idea/jarRepositories.xml 45 | # .idea/modules.xml 46 | # .idea/*.iml 47 | # .idea/modules 48 | # *.iml 49 | # *.ipr 50 | 51 | # CMake 52 | cmake-build-*/ 53 | 54 | # Mongo Explorer plugin 55 | .idea/**/mongoSettings.xml 56 | 57 | # File-based project format 58 | *.iws 59 | 60 | # IntelliJ 61 | out/ 62 | 63 | # mpeltonen/sbt-idea plugin 64 | .idea_modules/ 65 | 66 | # JIRA plugin 67 | atlassian-ide-plugin.xml 68 | 69 | # Cursive Clojure plugin 70 | .idea/replstate.xml 71 | 72 | # Crashlytics plugin (for Android Studio and IntelliJ) 73 | com_crashlytics_export_strings.xml 74 | crashlytics.properties 75 | crashlytics-build.properties 76 | fabric.properties 77 | 78 | # Editor-based Rest Client 79 | .idea/httpRequests 80 | 81 | # Android studio 3.1+ serialized cache file 82 | .idea/caches/build_file_checksums.ser -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # A C++20 library for using bitwise operators on flag enums 2 | 3 | This library provides a set of free function templates which perform bitwise operations on scoped enums that satisfy the `Flag` concept. When these function templates are used, instead of return `int` like normal bitwise operators, they return the underlying type of the input enum. 4 | 5 | ## Examples 6 | 7 | ```cpp 8 | #include 9 | #include 10 | #include 11 | 12 | namespace filesystem { 13 | enum class FilePermission : std::uint16_t { 14 | all_execute = 0b0'000'000'001, 15 | all_write = 0b0'000'000'010, 16 | all_read = 0b0'000'000'100, 17 | 18 | group_execute = 0b0'000'001'000, 19 | group_write = 0b0'000'010'000, 20 | group_read = 0b0'000'100'000, 21 | 22 | user_execute = 0b0'001'000'000, 23 | user_write = 0b0'010'000'000, 24 | user_read = 0b0'100'000'000, 25 | 26 | directory = 0b1'000'000'000 27 | }; 28 | IS_FLAG_ENUM(FilePermission); // Opting in making FilePermission enum as a flag enum 29 | } // namespace filesystem 30 | 31 | int main() { 32 | using namespace river::flags; // All operators are define in the river namespace 33 | using filesystem::FilePermission; 34 | 35 | // type of file_permission is std::uint16_t 36 | auto file_permission = FilePermission::all_execute | FilePermission::user_execute; 37 | 38 | assert(has(file_permission)); 39 | assert(has(file_permission)); 40 | 41 | // Add group_execute to file_permission 42 | file_permission |= FilePermission::group_execute; 43 | assert(has(file_permission)); 44 | 45 | // Toggle user_execute bit of file_permission 46 | file_permission ^= FilePermission::user_execute; 47 | assert(!has(file_permission)); 48 | file_permission ^= FilePermission::user_execute; 49 | assert(has(file_permission)); 50 | } 51 | ``` 52 | 53 | ## Usage 54 | This is a header only library, copy the file `flag.hpp` to your source code and start using it. 55 | 56 | Users can mark their scoped enums as flag enums by using the macro `IS_FLAG_ENUM` with the 57 | parameter as the name of the enum (qualified if used out of the scope that the enum is defined). 58 | -------------------------------------------------------------------------------- /tests/main.cpp: -------------------------------------------------------------------------------- 1 | #define CATCH_CONFIG_MAIN 2 | #include "catch.hpp" 3 | 4 | #include 5 | #include 6 | 7 | enum class Enum : std::uint8_t { _0 = 0b000, _1 = 0b001, _2 = 0b010, _3 = 0b011, _4 = 0b100 }; 8 | IS_FLAG_ENUM(Enum); 9 | 10 | TEST_CASE("Operations on an enum flag") { 11 | SECTION("Flip operators") { 12 | using river::flags::operator~; 13 | CHECK(std::is_same_v); 14 | CHECK(~Enum::_0 == 0b1111'1111); 15 | CHECK(~Enum::_1 == 0b1111'1110); 16 | CHECK(~Enum::_2 == 0b1111'1101); 17 | } 18 | 19 | SECTION("Binary operators") { 20 | using river::flags::operator|, river::flags::operator&, river::flags::operator^; 21 | CHECK(std::is_same_v); 22 | CHECK((Enum::_0 | Enum::_1) == 0b001); 23 | CHECK((Enum::_0 & Enum::_1) == 0b000); 24 | CHECK((Enum::_3 ^ Enum::_4) == 0b111); 25 | } 26 | 27 | SECTION("Chaining operations") { 28 | using river::flags::operator|, river::flags::operator&, river::flags::operator^; 29 | CHECK(std::is_same_v); 30 | CHECK((Enum::_1 & Enum::_2 & Enum::_3) == 0b000); 31 | CHECK((Enum::_1 | Enum::_2 | Enum::_4) == 0b111); 32 | CHECK((Enum::_1 ^ Enum::_2 ^ Enum::_4) == 0b111); 33 | } 34 | 35 | SECTION("Getting bits") { 36 | using river::flags::has; 37 | CHECK(has(0b1111)); 38 | CHECK(has(0b0100)); 39 | CHECK_FALSE(has(0b0)); 40 | CHECK_FALSE(has(0b1000)); 41 | } 42 | } 43 | 44 | TEST_CASE("Operations on enum and literals") { 45 | using river::flags::operator&; 46 | CHECK((Enum::_1 & 0b1111'1111) == 0b1); 47 | } 48 | 49 | TEST_CASE("Check has function") { 50 | using river::flags::has; 51 | std::uint8_t value = 0b1u; 52 | CHECK(has(value)); 53 | CHECK_FALSE(has(value)); 54 | } 55 | 56 | TEST_CASE("Update value") { 57 | using namespace river::flags; 58 | std::uint8_t value = 0u; 59 | value |= Enum::_1; 60 | CHECK(has(value)); 61 | CHECK_FALSE(has(value)); 62 | 63 | value |= Enum::_3; 64 | CHECK(has(value)); 65 | CHECK(has(value)); 66 | 67 | value &= Enum::_1; 68 | CHECK(has(value)); 69 | CHECK_FALSE(has(value)); 70 | 71 | value ^= Enum::_3; 72 | CHECK_FALSE(has(value)); 73 | CHECK(has(value)); 74 | CHECK_FALSE(has(value)); 75 | } 76 | 77 | namespace flag_test { 78 | enum class NSEnum : std::uint8_t { _0 = 0b000, _1 = 0b001, _2 = 0b010, _3 = 0b011, _4 = 0b100 }; 79 | IS_FLAG_ENUM(NSEnum); 80 | TEST_CASE("Enum outside of global and river namespace") { 81 | using namespace river::flags; 82 | static_assert(Flag); 83 | CHECK(~NSEnum::_0 == 0b1111'1111u); 84 | } 85 | } // namespace flag_test 86 | -------------------------------------------------------------------------------- /include/river/flag.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | 4 | Copyright (c) 2020 Uy Ha 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | 25 | #pragma once 26 | 27 | #include 28 | 29 | #define IS_FLAG_ENUM(enum_name) \ 30 | constexpr bool is_flag(enum_name) { \ 31 | return true; \ 32 | } 33 | 34 | namespace river::flags { 35 | template 36 | concept unsigned_enum = std::is_enum_v &&std::is_unsigned_v>; 37 | 38 | namespace details { 39 | template 40 | constexpr bool is_flag() { 41 | return is_flag(T{}); 42 | } 43 | } // namespace details 44 | 45 | template 46 | concept Flag = unsigned_enum &&details::is_flag(); 47 | 48 | template 49 | constexpr auto underlying_value(T enum_value) { 50 | return static_cast>(enum_value); 51 | } 52 | 53 | template 54 | constexpr auto operator~(T value) { 55 | return static_cast>(~underlying_value(value)); 56 | } 57 | 58 | template 59 | constexpr auto operator|(T lhs, T rhs) { 60 | return static_cast>(underlying_value(lhs) | underlying_value(rhs)); 61 | } 62 | template 63 | constexpr auto operator|(std::underlying_type_t lhs, T rhs) { 64 | return static_cast>(lhs | underlying_value(rhs)); 65 | } 66 | template 67 | constexpr auto operator|(T lhs, std::underlying_type_t rhs) { 68 | return static_cast>(underlying_value(lhs) | rhs); 69 | } 70 | template 71 | constexpr auto operator|=(std::underlying_type_t &value, T const flag) { 72 | return value = value | flag; 73 | } 74 | 75 | template 76 | constexpr auto operator&(T lhs, T rhs) { 77 | return static_cast>(underlying_value(lhs) & underlying_value(rhs)); 78 | } 79 | template 80 | constexpr auto operator&(std::underlying_type_t lhs, T rhs) { 81 | return static_cast>(lhs & underlying_value(rhs)); 82 | } 83 | template 84 | constexpr auto operator&(T lhs, std::underlying_type_t rhs) { 85 | return static_cast>(underlying_value(lhs) & rhs); 86 | } 87 | template 88 | constexpr auto operator&=(std::underlying_type_t &value, T const flag) { 89 | return value = value & flag; 90 | } 91 | 92 | template 93 | constexpr auto operator^(T lhs, T rhs) { 94 | return static_cast>(underlying_value(lhs) ^ underlying_value(rhs)); 95 | } 96 | template 97 | constexpr auto operator^(std::underlying_type_t lhs, T rhs) { 98 | return static_cast>(lhs ^ underlying_value(rhs)); 99 | } 100 | template 101 | constexpr auto operator^(T lhs, std::underlying_type_t rhs) { 102 | return static_cast>(underlying_value(lhs) ^ rhs); 103 | } 104 | template 105 | constexpr auto operator^=(std::underlying_type_t &value, T const flag) { 106 | return value = value ^ flag; 107 | } 108 | 109 | template 110 | constexpr bool has(std::underlying_type_t value) { 111 | return (value & mask) == underlying_value(mask); 112 | } 113 | } // namespace river::flags --------------------------------------------------------------------------------