├── CMakeLists.txt ├── example ├── CMakeLists.txt └── example.cpp ├── test ├── CMakeLists.txt └── tester.cpp ├── .travis.yml ├── doc └── Doxyfile ├── LICENSE ├── include ├── detail │ ├── meta17.hpp │ ├── facet17.hpp │ ├── meta14.hpp │ ├── facet14.hpp │ └── flags_storage.hpp └── typed_flags.hpp └── README.md /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # 2 | # MIT License 3 | # Copyright (c) 2017 Roman Orlov 4 | # See accompanying file LICENSE or copy at http://opensource.org/licenses/MIT 5 | # 6 | 7 | cmake_minimum_required(VERSION 2.8) 8 | 9 | project(typed_flags) 10 | 11 | enable_testing() 12 | add_subdirectory(example) 13 | add_subdirectory(test) 14 | 15 | -------------------------------------------------------------------------------- /example/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # 2 | # MIT License 3 | # Copyright (c) 2017 Roman Orlov 4 | # See accompanying file LICENSE or copy at http://opensource.org/licenses/MIT 5 | # 6 | 7 | cmake_minimum_required(VERSION 2.8) 8 | 9 | if(MSVC) 10 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /std:c++latest /W4") 11 | elseif(MINGW) 12 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++14 -pedantic -Wall -Wextra") 13 | else() 14 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++1z -pedantic -Wall -Wextra") 15 | endif() 16 | add_executable(example example.cpp) 17 | 18 | -------------------------------------------------------------------------------- /test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # 2 | # MIT License 3 | # Copyright (c) 2017 Roman Orlov 4 | # See accompanying file LICENSE or copy at http://opensource.org/licenses/MIT 5 | # 6 | 7 | cmake_minimum_required(VERSION 2.8) 8 | 9 | if(MSVC) 10 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /std:c++latest /W4") 11 | elseif(MINGW) 12 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++14 -pedantic -Wall -Wextra") 13 | else() 14 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++1z -pedantic -Wall -Wextra") 15 | set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fsanitize=undefined") 16 | endif() 17 | add_executable(tester tester.cpp) 18 | add_test(NAME typed_flags COMMAND tester) 19 | 20 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: c++ 2 | dist: trusty 3 | sudo: required 4 | 5 | git: 6 | depth: 1 7 | 8 | matrix: 9 | include: 10 | - os: linux 11 | addons: 12 | apt: 13 | packages: 14 | - g++-6 15 | sources: 16 | - ubuntu-toolchain-r-test 17 | env: COMPILER=gcc-6 COMPILERXX=g++-6 18 | - os: linux 19 | addons: 20 | apt: 21 | packages: 22 | - g++-6 23 | - clang-3.8 24 | sources: 25 | - ubuntu-toolchain-r-test 26 | env: COMPILER=clang-3.8 COMPILERXX=clang++-3.8 CXXFLAGS="-isystem /usr/include/c++/6" 27 | 28 | install: 29 | - export CC=$COMPILER 30 | - export CXX=$COMPILERXX 31 | 32 | script: 33 | - mkdir -p build && cd build && rm -rf * 34 | - cmake ../ -DCMAKE_BUILD_TYPE=Debug 35 | - make && ctest 36 | -------------------------------------------------------------------------------- /doc/Doxyfile: -------------------------------------------------------------------------------- 1 | DOXYFILE_ENCODING = UTF-8 2 | PROJECT_NAME = "Typed flags" 3 | PROJECT_BRIEF = "Type-safe and human-readable set of bool flags" 4 | INPUT = $(ROOT)/include/ 5 | RECURSIVE = YES 6 | FILE_PATTERNS = *.hpp 7 | OUTPUT_LANGUAGE = English 8 | HTML_OUTPUT = . 9 | GENERATE_LATEX = NO 10 | INHERIT_DOCS = YES 11 | TAB_SIZE = 4 12 | HAVE_DOT = NO 13 | CLASS_DIAGRAMS = NO 14 | CLASS_GRAPH = NO 15 | COLLABORATION_GRAPH = NO 16 | HIDE_UNDOC_MEMBERS = NO 17 | HIDE_UNDOC_CLASSES = YES 18 | BRIEF_MEMBER_DESC = NO 19 | ALPHABETICAL_INDEX = NO 20 | CLANG_ASSISTED_PARSING = YES 21 | SHOW_FILES = NO 22 | SOURCE_BROWSER = NO 23 | VERBATIM_HEADERS = NO 24 | SHOW_USED_FILES = NO 25 | PREDEFINED = DOXYGEN_WORKAROUND 26 | 27 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Roman Orlov 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 | -------------------------------------------------------------------------------- /include/detail/meta17.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // MIT License 3 | // Copyright (c) 2017 Roman Orlov 4 | // See accompanying file LICENSE or copy at http://opensource.org/licenses/MIT 5 | // 6 | 7 | #ifndef _TFL_META17_HPP_ 8 | #define _TFL_META17_HPP_ 9 | 10 | #include 11 | #include 12 | 13 | namespace tfl 14 | { 15 | namespace detail 16 | { 17 | 18 | template 19 | struct counter 20 | { 21 | static constexpr size_t value = (0 + ... + std::is_same::value); 22 | }; 23 | 24 | template 25 | struct is_unique 26 | { 27 | static constexpr bool value = (... && (counter::value == 1)); 28 | }; 29 | 30 | enum index: size_t {}; 31 | 32 | constexpr index operator << (index lhs, index rhs) 33 | { 34 | return lhs < rhs ? lhs : rhs; 35 | } 36 | 37 | template 38 | struct index_of_impl; 39 | 40 | template 41 | struct index_of_impl, Args...> 42 | { 43 | static constexpr size_t value = 44 | (index(-1) << ... << index(std::is_same::value ? I : -1)); 45 | }; 46 | 47 | template 48 | struct index_of 49 | { 50 | static constexpr size_t value = index_of_impl< 51 | T, std::index_sequence_for, Args...>::value; 52 | }; 53 | 54 | } // namespace detail 55 | } // namespace tfl 56 | 57 | #endif 58 | -------------------------------------------------------------------------------- /include/detail/facet17.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // MIT License 3 | // Copyright (c) 2017 Roman Orlov 4 | // See accompanying file LICENSE or copy at http://opensource.org/licenses/MIT 5 | // 6 | 7 | #ifndef _TFL_FACET17_HPP_ 8 | #define _TFL_FACET17_HPP_ 9 | 10 | namespace detail 11 | { 12 | 13 | template 14 | class typed_flags_facet 15 | { 16 | private: 17 | 18 | D& this_() noexcept { 19 | return static_cast(*this); 20 | } 21 | 22 | D const& this_() const noexcept { 23 | return static_cast(*this); 24 | } 25 | 26 | public: 27 | 28 | template 29 | bool none() const noexcept { 30 | return (... && (!this_().template test())); 31 | } 32 | 33 | template 34 | bool all() const noexcept { 35 | return (... && ( this_().template test())); 36 | } 37 | 38 | template 39 | void set(bool value = true) noexcept { 40 | (..., (this_().set_bit(D::template index(), value))); 41 | } 42 | 43 | template 44 | void set(flag... flags) noexcept { 45 | (..., (this_().set_bit(D::template index(), flags))); 46 | } 47 | 48 | template 49 | void get(flag&... flags) const noexcept { 50 | (..., (flags = this_().template test())); 51 | } 52 | 53 | template 54 | void reset() noexcept { 55 | (..., (this_().set_bit(D::template index(), false))); 56 | } 57 | 58 | template 59 | void flip() noexcept { 60 | (..., (this_().set_bit(D::template index(), !this_().template test()))); 61 | } 62 | }; 63 | 64 | } // namespace detail 65 | 66 | #endif 67 | -------------------------------------------------------------------------------- /include/detail/meta14.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // MIT License 3 | // Copyright (c) 2017 Roman Orlov 4 | // See accompanying file LICENSE or copy at http://opensource.org/licenses/MIT 5 | // 6 | 7 | #ifndef _TFL_META14_HPP_ 8 | #define _TFL_META14_HPP_ 9 | 10 | #include 11 | #include 12 | 13 | namespace tfl 14 | { 15 | namespace detail 16 | { 17 | 18 | struct empty; 19 | 20 | template 21 | struct counter 22 | { 23 | static constexpr size_t value = std::is_same::value 24 | + counter::value; 25 | }; 26 | 27 | template 28 | struct counter 29 | { 30 | static constexpr size_t value = 0; 31 | }; 32 | 33 | template 34 | struct is_unique 35 | { 36 | static constexpr bool value = counter::value == 0 37 | && is_unique::value; 38 | }; 39 | 40 | template<> 41 | struct is_unique 42 | { 43 | static constexpr bool value = true; 44 | }; 45 | 46 | template 47 | struct index_of_impl 48 | { 49 | static constexpr size_t value = index_of_impl::value; 50 | }; 51 | 52 | template 53 | struct index_of_impl 54 | { 55 | static constexpr size_t value = I; 56 | }; 57 | 58 | template 59 | struct index_of_impl 60 | { 61 | static constexpr size_t value = -1; 62 | }; 63 | 64 | template 65 | struct index_of 66 | { 67 | static constexpr size_t value = index_of_impl::value; 68 | }; 69 | 70 | } // namespace detail 71 | } // namespace tfl 72 | 73 | #endif 74 | -------------------------------------------------------------------------------- /include/detail/facet14.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // MIT License 3 | // Copyright (c) 2017 Roman Orlov 4 | // See accompanying file LICENSE or copy at http://opensource.org/licenses/MIT 5 | // 6 | 7 | #ifndef _TFL_FACET14_HPP_ 8 | #define _TFL_FACET14_HPP_ 9 | 10 | namespace detail 11 | { 12 | 13 | template 14 | class typed_flags_facet 15 | { 16 | private: 17 | 18 | D& this_() noexcept { 19 | return static_cast(*this); 20 | } 21 | 22 | D const& this_() const noexcept { 23 | return static_cast(*this); 24 | } 25 | 26 | public: 27 | 28 | template 29 | bool none() const noexcept { 30 | bool r = true; 31 | auto _ = {0, (r = r && !this_().template test(), 0)...}; 32 | (void)_; 33 | return r; 34 | } 35 | 36 | template 37 | bool all() const noexcept { 38 | bool r = true; 39 | auto _ = {0, (r = r && this_().template test(), 0)...}; 40 | (void)_; 41 | return r; 42 | } 43 | 44 | template 45 | void set(bool value = true) noexcept { 46 | auto _ = {0, (this_().set_bit(D::template index(), value), 0)...}; 47 | (void)_; 48 | } 49 | 50 | template 51 | void set(flag... flags) noexcept { 52 | auto _ = {0, (this_().set_bit(D::template index(), flags), 0)...}; 53 | (void)_; 54 | } 55 | 56 | template 57 | void get(flag&... flags) const noexcept { 58 | auto _ = {0, (flags = this_().template test(), 0)...}; 59 | (void)_; 60 | } 61 | 62 | template 63 | void reset() noexcept { 64 | auto _ = {0, (this_().set_bit(D::template index(), false), 0)...}; 65 | (void)_; 66 | } 67 | 68 | template 69 | void flip() noexcept { 70 | auto _ = {0, (this_().set_bit(D::template index(), !this_().template test()), 0)...}; 71 | (void)_; 72 | } 73 | }; 74 | 75 | } // namespace detail 76 | 77 | #endif 78 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Typed flags 2 | [![Build Status](https://travis-ci.org/compmaniak/typed_flags.svg?branch=master)](https://travis-ci.org/compmaniak/typed_flags) 3 | [![Build status](https://ci.appveyor.com/api/projects/status/kyrm1lmn2ciaxbel?svg=true)](https://ci.appveyor.com/project/compmaniak/typed-flags) 4 | [![Try online](https://img.shields.io/badge/Try-online-4DB6AC.svg)](http://melpon.org/wandbox/permlink/55g1czUjwSFO8LS1) 5 | 6 | Type-safe and human-readable sets of bool flags. 7 | 8 | ## Quick start 9 | 10 | Start by declaring some types 11 | ```cpp 12 | class eats_meat; 13 | class eats_grass; 14 | class has_tail; 15 | ``` 16 | Then bind these types to flag identifiers 17 | ```cpp 18 | typedef typed_flags animal; 19 | ``` 20 | Create flags from scratch 21 | ```cpp 22 | animal wolf; 23 | wolf.set(false); 24 | wolf.set(); 25 | wolf.set(flag{1}, flag{1}); 26 | ``` 27 | Create flags with a flexible human-readable constructor 28 | ```cpp 29 | wolf = animal{flag{1}, flag{1}, flag{0}}; 30 | ``` 31 | Test each flag separately 32 | ```cpp 33 | assert( (wolf.test() && wolf.test()) ); 34 | ``` 35 | Test a group of flags in one call 36 | ```cpp 37 | assert( (wolf.all()) ); 38 | assert( (wolf.any()) ); 39 | assert( (wolf.none()) ); 40 | ``` 41 | Extract flag values 42 | ```cpp 43 | flag f1; 44 | flag f2; 45 | wolf.get(f1, f2); 46 | assert( f1 && f2 ); 47 | ``` 48 | Create flags from integers or strings and convert back and forth - like std::bitset 49 | ```cpp 50 | auto a1 = animal{3}; 51 | auto a2 = animal{"101"}; 52 | assert( a1.to_integral() == 3 ); 53 | assert( a2.to_string() == "101" ); 54 | ``` 55 | 56 | ## Documentation 57 | 58 | You can find more detailed info [here](https://compmaniak.github.io/typed_flags/classtyped__flags.html). 59 | 60 | ## Requirements 61 | 62 | To build tests and examples you need 63 | * cmake 64 | * compiler supporting fold-expressions from C++17 (GCC 6, Clang 3.8+) 65 | 66 | -------------------------------------------------------------------------------- /example/example.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // MIT License 3 | // Copyright (c) 2017 Roman Orlov 4 | // See accompanying file LICENSE or copy at http://opensource.org/licenses/MIT 5 | // 6 | 7 | #include "../include/typed_flags.hpp" 8 | #include 9 | 10 | using namespace tfl; 11 | 12 | // at first declare some types 13 | class eats_meat; 14 | class eats_grass; 15 | class has_tail; 16 | class build_spaceships; 17 | 18 | // then bind types to flag identifiers 19 | typedef typed_flags animal; 20 | typedef typed_flags engineer; 21 | 22 | int main() 23 | { 24 | // unlike std::bitset typed_flags allocates less memory 25 | assert( sizeof(animal) == 1 ); 26 | 27 | // create flags from scratch 28 | animal wolf; 29 | wolf.set(false); 30 | wolf.set(); 31 | wolf.set(flag{1}, flag{1}); 32 | 33 | // create flags with flexible human-readable constructor 34 | wolf = animal{flag{1}, flag{1}, flag{0}}; 35 | 36 | // test each flag separately 37 | assert( (wolf.test() && wolf.test()) ); 38 | 39 | // test group off flags in one call 40 | assert( (wolf.all()) ); 41 | assert( (wolf.any()) ); 42 | assert( (wolf.none()) ); 43 | 44 | // extract flag values 45 | flag f1; 46 | flag f2; 47 | flag f3; 48 | wolf.get(f1, f2); 49 | wolf.get(f3); 50 | assert( f1 && !f2 && f3 ); 51 | 52 | // like std::bitset create flags from integers or strings 53 | // and convert vice versa 54 | auto a1 = animal{3}; 55 | auto a2 = animal{"101"}; 56 | assert( a1.to_integral() == 3 ); 57 | assert( a2.to_string() == "101" ); 58 | 59 | // there are bitwise member and non-member functions 60 | auto a3 = wolf; 61 | a3 &= animal{"001"}; 62 | assert( a3 == animal{flag{1}} ); 63 | assert( (a3 | animal{2}) == animal{3} ); 64 | 65 | //wolf.set(); // compile error! 66 | //wolf = engineer{1}; // compile error! 67 | } 68 | -------------------------------------------------------------------------------- /include/detail/flags_storage.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // MIT License 3 | // Copyright (c) 2017 Roman Orlov 4 | // See accompanying file LICENSE or copy at http://opensource.org/licenses/MIT 5 | // 6 | 7 | #ifndef _TFL_FLAGS_STORAGE_HPP_ 8 | #define _TFL_FLAGS_STORAGE_HPP_ 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | namespace tfl 18 | { 19 | namespace detail 20 | { 21 | 22 | // 23 | // Class storing bits in continuous array similar to std::bitset. 24 | // Unlike std::bitset allocates less memory. 25 | // 26 | template 27 | class flags_storage 28 | { 29 | // Storage array element type 30 | typedef uint8_t bank_type; 31 | 32 | // Size of bank in bits 33 | static constexpr size_t bank_bits = sizeof(bank_type) * 8; 34 | 35 | // Number of elements in storage array 36 | static constexpr size_t bank_count = N / bank_bits + (N % bank_bits != 0); 37 | 38 | // Bit mask for last bank 39 | static constexpr bank_type bank_mask = N % bank_bits != 0 40 | ? bank_type(~(size_t(-1) << (N % bank_bits))) 41 | : bank_type(-1); 42 | 43 | template 44 | std::enable_if_t init(T /*data*/) noexcept 45 | {} 46 | 47 | template 48 | std::enable_if_t<0 < S && S <= sizeof(T)> init(T data) noexcept 49 | { 50 | memcpy(m_data.data(), &data, S); 51 | m_data.back() &= bank_mask; 52 | } 53 | 54 | template 55 | std::enable_if_t init(T data) noexcept 56 | { 57 | reset(); 58 | memcpy(m_data.data(), &data, sizeof(T)); 59 | } 60 | 61 | template 62 | struct is_equal_to 63 | { 64 | bool operator()(bank_type v) const noexcept { 65 | return v == bank_type(I); 66 | } 67 | }; 68 | 69 | public: 70 | 71 | flags_storage() noexcept 72 | { 73 | reset(); 74 | } 75 | 76 | explicit flags_storage(unsigned long long data) noexcept 77 | { 78 | init(data); 79 | } 80 | 81 | template 82 | explicit flags_storage(CharT const* src, size_t n, CharT zero, CharT one) 83 | { 84 | reset(); 85 | auto it = src + n; 86 | for (size_t k = 0; k < n && k < N; ++k) { 87 | CharT const ch = *--it; 88 | if (ch == one) 89 | set_bit(k, true); 90 | else if (ch != zero) 91 | throw std::invalid_argument("Char is not zero or one"); 92 | } 93 | } 94 | 95 | // 96 | // Modifiers 97 | // 98 | void set_bit(size_t n, bool value) noexcept 99 | { 100 | auto const mask = bank_type(1) << (n % bank_bits); 101 | if (value) 102 | m_data[n / bank_bits] |= mask; 103 | else 104 | m_data[n / bank_bits] &= ~mask; 105 | } 106 | 107 | void set() noexcept 108 | { 109 | std::fill(m_data.begin(), m_data.end(), -1); 110 | m_data.back() &= bank_mask; 111 | } 112 | 113 | void reset() noexcept 114 | { 115 | std::fill(m_data.begin(), m_data.end(), 0); 116 | } 117 | 118 | void flip() noexcept 119 | { 120 | for (auto& bank : m_data) 121 | bank = ~bank; 122 | m_data.back() &= bank_mask; 123 | } 124 | 125 | // 126 | // Element access 127 | // 128 | bool get_bit(size_t n) const noexcept 129 | { 130 | auto const mask = bank_type(1) << (n % bank_bits); 131 | return (m_data[n / bank_bits] & mask) > 0; 132 | } 133 | 134 | bool none() const noexcept 135 | { 136 | return std::all_of(m_data.begin(), m_data.end(), is_equal_to<0>{}); 137 | } 138 | 139 | bool any() const noexcept 140 | { 141 | return !none(); 142 | } 143 | 144 | bool all() const noexcept 145 | { 146 | // TODO use 'constexpr if' after some time 147 | if (m_data.empty()) 148 | return false; 149 | if (!std::all_of(m_data.begin(), m_data.end() - 1, is_equal_to<-1>{})) 150 | return false; 151 | return m_data.back() == bank_mask; 152 | } 153 | 154 | // 155 | // Conversions 156 | // 157 | template 158 | T to_integral() const noexcept 159 | { 160 | static_assert(std::is_integral::value, "T is not an intergal type"); 161 | static_assert(sizeof(T) * 8 >= N, "T can't hold all flags"); 162 | T res = 0; 163 | // TODO use 'constexpr if' after some time 164 | if (!m_data.empty()) 165 | memcpy(&res, m_data.data(), m_data.size() * sizeof(bank_type)); 166 | return res; 167 | } 168 | 169 | template, 171 | class Allocator = std::allocator> 172 | auto to_string(CharT zero = CharT('0'), CharT one = CharT('1')) const 173 | { 174 | std::basic_string res; 175 | res.reserve(N); 176 | for (size_t i = N; i > 0;) 177 | res += get_bit(--i) ? one : zero; 178 | return res; 179 | } 180 | 181 | // 182 | // Operators' implementation 183 | // 184 | bool is_equal(flags_storage const& other) const noexcept 185 | { 186 | if (this == &other) 187 | return true; 188 | return m_data == other.m_data; 189 | } 190 | 191 | template 192 | void bitwise(flags_storage const& other, BinFn&& fn) noexcept 193 | { 194 | std::transform(m_data.begin(), m_data.end(), other.m_data.begin(), 195 | m_data.begin(), fn); 196 | } 197 | 198 | private: 199 | 200 | std::array m_data; 201 | }; 202 | 203 | } // namespace detail 204 | } // namespace tfl 205 | 206 | #endif 207 | -------------------------------------------------------------------------------- /test/tester.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // MIT License 3 | // Copyright (c) 2017 Roman Orlov 4 | // See accompanying file LICENSE or copy at http://opensource.org/licenses/MIT 5 | // 6 | 7 | #include "../include/typed_flags.hpp" 8 | #include 9 | 10 | using namespace tfl; 11 | 12 | class has_tail; 13 | class eats_meat; 14 | class eats_grass; 15 | class builds_spaceships; 16 | 17 | typedef typed_flags animal; 18 | typedef typed_flags human; 19 | 20 | int main() 21 | { 22 | //typed_flags ill_formed; // compilation error! 23 | 24 | typed_flags<> empty; 25 | assert( empty.size() == 0 ); 26 | assert( sizeof(empty) == 1 ); 27 | assert( empty.to_integral() == 0 ); 28 | assert( empty.none<>() ); 29 | assert( !empty.any<>() ); 30 | assert( empty.all<>() ); 31 | empty = typed_flags<>(1); 32 | assert( empty.to_integral() == 0 ); 33 | 34 | assert( animal::index() == 0 ); 35 | assert( animal::index() == 1 ); 36 | assert( animal::index() == 2 ); 37 | //animal::index(); // compilation error! 38 | 39 | animal wolf; 40 | assert( wolf.size() == 3 ); 41 | assert( sizeof(wolf) == 1 ); 42 | assert( !wolf.test() ); 43 | assert( !wolf.test() ); 44 | assert( !wolf.test() ); 45 | assert( wolf.to_string() == "000" ); 46 | wolf.set(); 47 | assert( wolf.test() ); 48 | assert( wolf.to_string() == "001" ); 49 | wolf.set(true); 50 | assert( wolf.test() ); 51 | assert( wolf.to_string('-') == "-11" ); 52 | wolf.set(false); 53 | assert( !wolf.test() ); 54 | wolf.set(); 55 | assert( wolf.test() ); 56 | assert( wolf.to_integral() == 5 ); 57 | assert( wolf.to_string('-', '+') == "+-+" ); 58 | 59 | animal rabbit; 60 | rabbit.set(); 61 | assert( !rabbit.test() ); 62 | assert( rabbit.test() ); 63 | assert( rabbit.test() ); 64 | assert( rabbit.to_integral() == 6 ); 65 | 66 | animal unknown; 67 | assert( unknown.to_integral() == 0 ); 68 | assert( unknown.none() ); 69 | assert( !unknown.any() ); 70 | assert( !unknown.all() ); 71 | unknown.set(); 72 | assert( unknown.to_integral() == 7 ); 73 | assert( !unknown.none() ); 74 | assert( unknown.any() ); 75 | assert( unknown.all() ); 76 | animal tmp = unknown; 77 | tmp.reset(); 78 | assert( tmp != unknown ); 79 | unknown.reset(); 80 | assert( tmp == unknown ); 81 | assert( unknown.to_integral() == 0 ); 82 | assert( unknown.none() ); 83 | assert( !unknown.any() ); 84 | assert( !unknown.all() ); 85 | assert( unknown != wolf ); 86 | unknown = wolf; 87 | assert( unknown == wolf ); 88 | assert( unknown.to_integral() == wolf.to_integral() ); 89 | 90 | human engineer; 91 | assert( (engineer.none()) ); 92 | assert( (engineer.none()) ); 93 | assert( (engineer.none()) ); 94 | assert( (!engineer.any()) ); 95 | assert( (!engineer.any()) ); 96 | assert( (!engineer.any()) ); 97 | assert( (!engineer.all()) ); 98 | assert( (!engineer.all()) ); 99 | assert( (!engineer.all()) ); 100 | engineer.set(); 101 | assert( engineer.to_integral() == 4 ); 102 | assert( (engineer.none()) ); 103 | assert( (engineer.none()) ); 104 | assert( (!engineer.none()) ); 105 | assert( (!engineer.any()) ); 106 | assert( (!engineer.any()) ); 107 | assert( (engineer.any()) ); 108 | assert( (!engineer.all()) ); 109 | assert( (!engineer.all()) ); 110 | assert( (!engineer.all()) ); 111 | engineer.flip(); 112 | assert( (!engineer.none()) ); 113 | assert( (!engineer.none()) ); 114 | assert( (!engineer.none()) ); 115 | assert( (engineer.any()) ); 116 | assert( (engineer.any()) ); 117 | assert( (engineer.any()) ); 118 | assert( (engineer.all()) ); 119 | assert( (engineer.all()) ); 120 | assert( (!engineer.all()) ); 121 | engineer.flip(); 122 | assert( !engineer.test() ); 123 | assert( !engineer.test() ); 124 | assert( engineer.test() ); 125 | assert( engineer.to_integral() == 4 ); 126 | 127 | //engineer = wolf; // compilation error! 128 | 129 | unknown = animal{65535}; 130 | assert( unknown.to_integral() == 7 ); 131 | unknown = ~unknown; 132 | assert( unknown.to_integral() == 0 ); 133 | unknown |= animal{2}; 134 | assert( unknown.to_integral() == 2 ); 135 | unknown ^= animal{7}; 136 | assert( unknown.to_integral() == 5 ); 137 | unknown &= animal{4}; 138 | assert( unknown.to_integral() == 4 ); 139 | 140 | constexpr flag const_int_flag_0; 141 | constexpr flag const_int_flag_1{1}; 142 | flag int_flag; 143 | assert( !int_flag && int_flag == const_int_flag_0 ); 144 | int_flag = true; 145 | assert( int_flag && int_flag == const_int_flag_1 ); 146 | int_flag = false; 147 | assert( !int_flag ); 148 | 149 | unknown = animal{flag{1}, flag{1}}; 150 | assert( unknown == rabbit ); 151 | unknown.set(flag{1}); 152 | assert( unknown != rabbit ); 153 | unknown.set(flag{0}, flag{1}); 154 | assert( unknown == rabbit ); 155 | flag f1; 156 | flag f2; 157 | flag f3; 158 | unknown.get(f1, f2, f3); 159 | assert( f2 && f3 ); 160 | assert( !f1 ); 161 | 162 | auto a1 = animal{"111"} & animal{"010"}; 163 | assert( a1.to_string() == "010" ); 164 | auto a2 = animal{"11100"} | animal{"11001"}; 165 | assert( a2.to_string() == "101" ); 166 | auto a3 = animal{"10"} ^ animal{"11"}; 167 | assert( a3.to_string() == "001" ); 168 | try 169 | { 170 | animal{"X"}; 171 | assert( false ); 172 | } 173 | catch (std::invalid_argument const&) 174 | { 175 | } 176 | 177 | typed_flags 180 | flags_8; 181 | assert( flags_8.size() == 8 ); 182 | assert( sizeof(flags_8) == 1 ); 183 | flags_8.set(); 184 | assert( flags_8.to_integral() == 128 ); 185 | assert( flags_8.to_string() == "10000000" ); 186 | 187 | typed_flags 190 | flags_9; 191 | assert( flags_9.size() == 9 ); 192 | assert( sizeof(flags_9) == 2 ); 193 | //flags_9.to_integral(); // compilation error! 194 | flags_9.set(); 195 | assert( flags_9.to_integral() == 257 ); 196 | assert( flags_9.to_string() == "100000001" ); 197 | 198 | return 0; 199 | } 200 | -------------------------------------------------------------------------------- /include/typed_flags.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // MIT License 3 | // Copyright (c) 2017 Roman Orlov 4 | // See accompanying file LICENSE or copy at http://opensource.org/licenses/MIT 5 | // 6 | 7 | #ifndef _TFL_TYPED_FLAGS_HPP_ 8 | #define _TFL_TYPED_FLAGS_HPP_ 9 | 10 | #include "detail/flags_storage.hpp" 11 | #if __cplusplus > 201402L 12 | #include "detail/meta17.hpp" 13 | #else 14 | #include "detail/meta14.hpp" 15 | #endif 16 | #include 17 | 18 | namespace tfl 19 | { 20 | 21 | //! 22 | //! @brief Single flag container. 23 | //! 24 | //! Simple typed wrapper around bool variable. Can be converted to/from bool. 25 | //! @param T flag type. 26 | //! 27 | template 28 | class flag 29 | { 30 | bool value; 31 | 32 | public: 33 | 34 | //! @name Creation 35 | //! @{ 36 | 37 | constexpr flag(): value(false) 38 | {} 39 | 40 | constexpr flag(bool v): value(v) 41 | {} 42 | 43 | //! @} 44 | //! @name Conversions 45 | //! @{ 46 | 47 | constexpr operator bool () const noexcept 48 | { 49 | return value; 50 | } 51 | 52 | flag& operator = (bool v) noexcept 53 | { 54 | value = v; 55 | return *this; 56 | } 57 | 58 | //! @} 59 | }; 60 | 61 | #if __cplusplus > 201402L 62 | #include "detail/facet17.hpp" 63 | #else 64 | #include "detail/facet14.hpp" 65 | #endif 66 | 67 | //! 68 | //! @brief Type-safe flag container. 69 | //! 70 | //! Templated frontend to raw bit storage. Allows type safe bit manipulations 71 | //! translating user defined types to corresponding indexes. 72 | //! @param Args... user defined types. 73 | //! 74 | //! @note Types can be incomplete. 75 | //! 76 | template 77 | class typed_flags: 78 | private detail::flags_storage, 79 | private detail::typed_flags_facet> 80 | { 81 | typedef typed_flags this_type; 82 | typedef detail::flags_storage parent_type; 83 | typedef detail::typed_flags_facet facet_type; 84 | 85 | friend class detail::typed_flags_facet; 86 | 87 | static_assert(detail::is_unique::value, "Flag types are not unique."); 88 | 89 | public: 90 | 91 | using parent_type::set; 92 | using parent_type::reset; 93 | using parent_type::flip; 94 | using parent_type::none; 95 | using parent_type::any; 96 | using parent_type::all; 97 | using parent_type::to_integral; 98 | using parent_type::to_string; 99 | 100 | using facet_type::none; 101 | using facet_type::all; 102 | using facet_type::set; 103 | using facet_type::get; 104 | using facet_type::reset; 105 | using facet_type::flip; 106 | 107 | //! @name Creation 108 | //! @{ 109 | 110 | //! 111 | //! Sets all flags to zero. 112 | //! 113 | typed_flags() {}; 114 | 115 | //! 116 | //! Sets concrete flags to corresponding values. 117 | //! @param flag... flag values. 118 | //! 119 | template 120 | explicit typed_flags(flag... flags) noexcept 121 | { 122 | set(flags...); 123 | } 124 | 125 | //! 126 | //! Loads flag values from integral number.
Least significant bit 127 | //! corresponds to the first parameter of the class template. 128 | //! Remaining flags are initialized to zeros. 129 | //! @param data source integral number. 130 | //! 131 | explicit typed_flags(unsigned long long data) noexcept 132 | : parent_type(data) 133 | {} 134 | 135 | //! 136 | //! Loads flag values from characters.
Rightmost character corresponds to 137 | //! the first parameter of the class template. Remaining flags are initialized to zeros. 138 | //! @param str string used to initialize flags. 139 | //! @param n number of characters to read from string (optional). 140 | //! @param zero character representing unset flag (optional). 141 | //! @param one character representing set flag (optional). 142 | //! @throws std::invalid_argument if character is neither zero nor one. 143 | //! 144 | template> 145 | explicit typed_flags(const CharT* str, size_t n = -1, 146 | CharT zero = CharT('0'), 147 | CharT one = CharT('1')) 148 | : parent_type(str, (n == size_t(-1) ? Traits::length(str) : n), zero, one) 149 | {} 150 | 151 | //! @} 152 | //! @name Element access 153 | //! @{ 154 | 155 | //! 156 | //! Returns the value of the specified flag. 157 | //! @param T flag type. 158 | //! @returns true if the flag is set, false otherwise. 159 | //! 160 | template 161 | bool test() const noexcept 162 | { 163 | return this->get_bit(index()); 164 | } 165 | 166 | //! 167 | //! Checks that every specified flag is unset. 168 | //! @param T... flag types. 169 | //! @returns true if every specified flag is unset, false otherwise. 170 | //! @note Invoking none() without template parameters checks all flags are equal to zero. 171 | //! 172 | #ifdef DOXYGEN_WORKAROUND 173 | template 174 | bool none() const noexcept; 175 | #endif 176 | 177 | //! 178 | //! Checks that at least one of specified flags is set. 179 | //! @param T... flag types. 180 | //! @returns true if one of specified flags is set, false otherwise. 181 | //! @note Invoking any() without template parameters checks at least one of all flags is set. 182 | //! 183 | template 184 | bool any() const noexcept 185 | { 186 | return !this->template none(); 187 | } 188 | 189 | //! 190 | //! Checks that every specified flag is set. 191 | //! @param T... flag types. 192 | //! @returns true if every specified flag is set, false otherwise. 193 | //! @note Invoking all() without template parameters checks all flags are equal to one. 194 | //! 195 | #ifdef DOXYGEN_WORKAROUND 196 | template 197 | bool all() const noexcept; 198 | #endif 199 | 200 | //! 201 | //! Extracts values of specified flags to variables. 202 | //! @param flag&... flag variables to store result. 203 | //! 204 | #ifdef DOXYGEN_WORKAROUND 205 | template 206 | void get(flag&... flags) const noexcept; 207 | #endif 208 | 209 | //! @} 210 | //! @name Capacity 211 | //! @{ 212 | 213 | //! 214 | //! Get the number of flags. 215 | //! @returns typed_flags::length 216 | //! 217 | static constexpr size_t size() noexcept 218 | { 219 | return sizeof...(Args); 220 | } 221 | 222 | //! 223 | //! Get index of specified flag. 224 | //! @param T flag type. 225 | //! @returns size_t 226 | //! 227 | template 228 | static constexpr size_t index() noexcept 229 | { 230 | using getter = detail::index_of; 231 | static_assert(getter::value < size(), "Index is not defined"); 232 | return getter::value; 233 | } 234 | 235 | //! @} 236 | //! @name Modifiers 237 | //! @{ 238 | 239 | //! 240 | //! Sets specified flags from flag variables. 241 | //! @param flag... flag variables to load from. 242 | //! 243 | #ifdef DOXYGEN_WORKAROUND 244 | template 245 | void set(flag... flags) noexcept; 246 | #endif 247 | 248 | //! 249 | //! Changes specified flags. 250 | //! @param T... flag types. 251 | //! @param value sets flags to this value. 252 | //! 253 | #ifdef DOXYGEN_WORKAROUND 254 | template 255 | void set(bool value = true) noexcept; 256 | #endif 257 | 258 | //! 259 | //! Unsets specified flags. 260 | //! @param T... flag types. 261 | //! 262 | #ifdef DOXYGEN_WORKAROUND 263 | template 264 | void reset() noexcept; 265 | #endif 266 | 267 | //! 268 | //! Reverts specified flags i.e. zeros becomes ones and vice versa. 269 | //! @param T... flag types. 270 | //! 271 | #ifdef DOXYGEN_WORKAROUND 272 | template 273 | void flip() noexcept; 274 | #endif 275 | 276 | //! @} 277 | //! @name Conversions 278 | //! @{ 279 | 280 | //! 281 | //! Converts flags to integral number.
282 | //! Least significant bit corresponds to the first parameter of the class template. 283 | //! @param T target integral type. 284 | //! @returns integral number of type T. 285 | //! @note If type T can't hold all flags static assertion fails. 286 | //! 287 | #ifdef DOXYGEN_WORKAROUND 288 | template 289 | T to_integral() const noexcept; 290 | #endif 291 | 292 | //! 293 | //! Converts flags to standard string.
294 | //! Rightmost character corresponds to the first parameter of the class template. 295 | //! @param zero character representing unset flag (optional). 296 | //! @param one character representing set flag (optional). 297 | //! @returns std::basic_string 298 | //! 299 | #ifdef DOXYGEN_WORKAROUND 300 | template, 302 | typename Allocator = std::allocator> 303 | auto to_string(CharT zero = CharT('0'), CharT one = CharT('1')) const; 304 | #endif 305 | 306 | //! @} 307 | //! @name Logical member operators 308 | //! @{ 309 | 310 | bool operator == (this_type const& other) const noexcept 311 | { 312 | return this->is_equal(other); 313 | } 314 | 315 | bool operator != (this_type const& other) const noexcept 316 | { 317 | return !this->is_equal(other); 318 | } 319 | 320 | //! @} 321 | //! @name Bitwise member operators 322 | //! @{ 323 | 324 | this_type& operator &= (this_type const& other ) noexcept 325 | { 326 | this->bitwise(other, std::bit_and<>{}); 327 | return *this; 328 | } 329 | 330 | this_type& operator |= (this_type const& other ) noexcept 331 | { 332 | this->bitwise(other, std::bit_or<>{}); 333 | return *this; 334 | } 335 | 336 | this_type& operator ^= (this_type const& other ) noexcept 337 | { 338 | this->bitwise(other, std::bit_xor<>{}); 339 | return *this; 340 | } 341 | 342 | this_type operator ~ () const noexcept 343 | { 344 | this_type tmp(*this); 345 | tmp.flip(); 346 | return tmp; 347 | } 348 | 349 | //! @} 350 | }; 351 | 352 | //! @name Bitwise non-member operators 353 | //! @relates typed_flags 354 | //! @{ 355 | 356 | template 357 | typed_flags operator & ( typed_flags const& lhs, typed_flags const& rhs ) 358 | { 359 | typed_flags res = lhs; 360 | return res &= rhs; 361 | } 362 | 363 | template 364 | typed_flags operator | ( typed_flags const& lhs, typed_flags const& rhs ) 365 | { 366 | typed_flags res = lhs; 367 | return res |= rhs; 368 | } 369 | 370 | template 371 | typed_flags operator ^ ( typed_flags const& lhs, typed_flags const& rhs ) 372 | { 373 | typed_flags res = lhs; 374 | return res ^= rhs; 375 | } 376 | 377 | //! @} 378 | 379 | } // namespace tfl 380 | 381 | #endif 382 | --------------------------------------------------------------------------------