A modern C++ library for type-safe flag operations on enum types.
- Type-safe flag operations: Perform bitwise operations on enum types without losing type safety
- Modern C++ support: C++17 baseline; C++20-era features are enabled when the compiler and standard library expose the matching feature macros
- Minimal overhead: Zero-cost abstractions with compile-time optimizations
- Comprehensive API: Complete set of flag manipulation functions and operators
To enable flag operations for an enum type, add a declaration:
enum class MyFlags {
None = 0,
Flag1 = 1 << 0,
Flag2 = 1 << 1,
Flag3 = 1 << 2
};
// Enable flag operations for MyFlags
enum_flags::enable_flag_ops flag_enable(MyFlags);#include "enum_flags.hpp"
enum class Permissions {
None = 0,
Read = 1 << 0,
Write = 1 << 1,
Execute = 1 << 2
};
enum_flags::enable_flag_ops flag_enable(Permissions);
int main() {
// Combine flags using bitwise OR
auto perms = Permissions::Read | Permissions::Write;
// Test if a flag is set
bool has_read = enum_flags::test(perms, Permissions::Read);
// Set a flag
perms = enum_flags::set_flag(perms, Permissions::Execute);
// Clear a flag
perms = enum_flags::clear_flag(perms, Permissions::Write);
// Toggle a flag
perms = enum_flags::toggle_flag(perms, Permissions::Read);
return 0;
}set_flag(value, flag, on = true)- Set or clear a flagclear_flag(value, flag)- Clear a flagtoggle_flag(value, flag)- Toggle a flagset_assign(value, flag, on = true)- Assignment version of set_flagclear_assign(value, flag)- Assignment version of clear_flagtoggle_assign(value, flag)- Assignment version of toggle_flag
any(value)- Check if any flags are setnone(value)- Check if no flags are settest(value, mask)- True if any bit inmaskis set invalue(i.e.(value & mask) != 0);maskmay combine several flagsall(value, mask)- True if every bit inmaskis set invaluecount(value)- Count the number of set flags
When flag operations are enabled for an enum type, the following operators are automatically available:
operator|- Bitwise OR (combine flags)operator&- Bitwise AND (intersection)operator^- Bitwise XOR (difference)operator~- Bitwise NOT on the full underlying type (may set bits that are not named enumerators)operator|=- Compound OR assignmentoperator&=- Compound AND assignmentoperator^=- Compound XOR assignment
- C++17 or later is required for the library itself.
- Optional capabilities are detected independently (you can have one without the other, depending on the toolchain):
- When
__cpp_concepts >= 201907L, the library uses C++20 concepts (FlagEnum) and concept-based constraints on the bitwise operators. - When
__cpp_lib_int_pow2 >= 202002L,<bit>is available andenum_flags::countusesstd::popcount; otherwise it uses a portable popcount loop.
- When
Most C++20 standard libraries provide both; C++17 mode uses neither.
Vendoring: Copy enum_flags.hpp into your tree, add its directory to your include path, then:
#include "enum_flags.hpp"Adjust the include form ("enum_flags.hpp" vs <...>) to match how you expose headers.
CMake (this repository): After add_subdirectory(enum_flags) or equivalent, link the interface target:
target_link_libraries(your_target PRIVATE enum_flags::enum_flags)That adds the directory containing enum_flags.hpp as a usage requirement. You can also install the header and CMake package (see INSTALL.md) and use find_package(enum_flags CONFIG).
From the repository root, with a C++17-capable compiler:
cmake -S . -B build -DENUM_FLAGS_BUILD_TESTS=ON
cmake --build build
ctest --test-dir build --output-on-failureTo compile the bundled tests as C++20 (e.g. to exercise concepts in your toolchain), set -DENUM_FLAGS_TEST_CXX_STANDARD=20 when configuring.
To use this project as a subdirectory without building its tests: -DENUM_FLAGS_BUILD_TESTS=OFF.
This library follows several key principles:
- Type Safety: All operations maintain enum type safety and prevent accidental mixing of different enum types
- Zero Cost: Operations are optimized away at compile time when possible
- Explicit Opt-in: Flag operations must be explicitly enabled for each enum type
- Modern C++: Uses newer language and library features when feature macros indicate they are available, without raising the baseline above C++17
enum class FileMode {
None = 0,
Read = 1 << 0,
Write = 1 << 1,
Execute = 1 << 2,
Append = 1 << 3
};
enum_flags::enable_flag_ops flag_enable(FileMode);
void set_file_mode(FileMode& mode, FileMode flags) {
mode |= flags;
}
bool has_permission(FileMode mode, FileMode permission) {
return enum_flags::test(mode, permission);
}enum class WidgetState {
None = 0,
Enabled = 1 << 0,
Visible = 1 << 1,
Focused = 1 << 2,
Hovered = 1 << 3
};
enum_flags::enable_flag_ops flag_enable(WidgetState);
class Widget {
WidgetState state_ = WidgetState::None;
public:
void set_enabled(bool enabled) {
enum_flags::set_assign(state_, WidgetState::Enabled, enabled);
}
bool is_enabled() const {
return enum_flags::test(state_, WidgetState::Enabled);
}
void set_visible(bool visible) {
enum_flags::set_assign(state_, WidgetState::Visible, visible);
}
bool is_visible() const {
return enum_flags::test(state_, WidgetState::Visible);
}
};This project is licensed under the MIT License - see the LICENSE file for details.