Single-header header-only C++11 / C++14 / C++17 library for easily managing set of auto-generated type-safe flags.
#include <bitflags/bitflags.hpp>
BEGIN_BITFLAGS(Flags)
FLAG(none)
FLAG(flag_a)
FLAG(flag_b)
FLAG(flag_c)
END_BITFLAGS(Flags)
int main() {
Flags flags = Flags::flag_a | Flags::flag_b;
if (flags & Flags::flag_a) {
std::cout << "flag_a is set" << std::endl;
} else {
std::cout << "flag_a is not set" << std::endl;
}
flags.toggle(Flags::flag_a);
// ...
return 0;
}
- Motivation
- Getting Started
- Benchmark
- Building Tests
- Compiler Compatibility
- Contributing
- License
- Support
Inspiration for writing this bitflags
library I got from the homonymous Rust's crate that you may find here.
Some people may ask: Why not just use enum class
and assign binary literals?
Following example presents using enum class
with std::uint8_t
as underlying type (so that max number of flags is 9):
enum class Flags : std::uint8_t {
none = 0b0000,
flag_a = 0b0001,
flag_b = 0b0010,
flag_c = 0b0010
};
Nothing is wrong with above usage, right?
Oh... you might not have noticed that both Flags::flag_b
and Flags::flag_c
have the same value assigned! That's the exact same mistake that developers make in everyday work... This mistake happened with std::uint8_t
as underlying type but imagine how often would be mistakes if the underlying value is std::uint32_t
or even std::uint64_t
.
You got the point?
Assigning binary literals to flags is manual and error-prone process, especially if there is lots of flags and if developers change them over time.
So, why should I use bitflags
library?
bitflags
library provides you decently safe way of specifying your flags with 2 core features:
- auto - generated flag values
- auto - detected underlying type
bitflags
is a single-header header-only C++11 / C++14 / C++17 library for easily managing set of auto-generated type-safe flags.
bitflags
library provides you with 2 flag types:
- raw_flag without its string representation (sort of lightweight version)
- flag with its string representation
In order to declare a set of auto-generated flags, there are few helper macros that hide kind of "ugly" declaration syntax and provide auto-generated value for each flag.
Macros for creating set of raw flags, i.e. flags without string representation:
-
BEGIN_RAW_BITFLAGS(NAME)
NAME
- name of set of raw flags
-
RAW_FLAG(NAME)
NAME
- name of specific raw flag
-
END_RAW_BITFLAGS(NAME)
NAME
- name of set of raw flags
Macros for creating set of ordinary flags, i.e. flags with string representation:
-
BEGIN_BITFLAGS(NAME)
NAME
- name of set of flags
-
FLAG(NAME)
NAME
- name of specific flag
-
END_BITFLAGS(NAME)
NAME
- name of set of flags
Following snippet shows the use case of the above macros:
BEGIN_BITFLAGS(Flags)
FLAG(none)
FLAG(flag_a)
FLAG(flag_b)
FLAG(flag_c)
END_BITFLAGS()
and is translated into:
template <typename T>
struct FlagsImpl {
using flag = bf::internal::flag<FlagsImpl, T>
static constexpr flag none{ 0b0000, "none" };
static constexpr flag flag_a{ 0b0001, "flag_a" };
static constexpr flag flag_b{ 0b0010, "flag_b" };
static constexpr flag flag_c{ 0b0100, "flag_c" };
};
using Flags = bf::bitflags<
FlagsImpl<
bf::bitflags<
FlagsImpl<std::uint8_t>
>::underlying_type
>
>;
Usage is basically the same for raw_flag
s.
In case you are using C++11, you need to define your flags separately like:
DEFINE_FLAG(Flags, none)
DEFINE_FLAG(Flags, flag_a)
DEFINE_FLAG(Flags, flag_b)
DEFINE_FLAG(Flags, flag_c)
This is because C++11 requires static class members to have an out-of-class definition.
While bits are part of both raw_flag
and flag
, names are part of flag
type only.
Once the flags are specified, it is possible to get bits representing each flag as well as string representation of each flag:
BEGIN_RAW_BITFLAGS(RawFlags)
RAW_FLAG(none)
RAW_FLAG(flag_a)
RAW_FLAG(flag_b)
END_RAW_BITFLAGS(RawFlags)
std::cout << RawFlags::flag_a.bits << std::endl;
and
BEGIN_BITFLAGS(Flags)
FLAG(none)
FLAG(flag_a)
FLAG(flag_b)
END_BITFLAGS(Flags)
std::cout << Flags::flag_a.bits << " - " << Flags::flag_a.name << std::endl;
The following binary operators are implemented for the generated flags:
- NOT (
~
) operator - AND (
&
) operator - OR (
|
) operator - XOR (
^
) operator
In case we want to check whether specific flag is set or not, we have 2 options:
- use AND operator
BEGIN_BITFLAGS(Flags)
FLAG(none)
FLAG(flag_a)
FLAG(flag_b)
END_BITFLAGS(Flags)
Flags flags = Flags::flag_a;
std::cout << static_cast<bool>(flags & Flags::flag_a) << std::endl; // true
std::cout << static_cast<bool>(flags & Flags::flag_b) << std::endl; // false
- use
contains
member function
BEGIN_BITFLAGS(Flags)
FLAG(none)
FLAG(flag_a)
FLAG(flag_b)
FLAG(flag_c)
END_BITFLAGS(Flags)
Flags flags_1 = Flags::flag_a;
std::cout << flags_1.contains(Flags::flag_a) << std::endl; // true
std::cout << flags_1.contains(Flags::flag_b) << std::endl; // false
Flags flags_2 = Flags::flag_a | Flags::flag_b;
std::cout << flags_2.contains(Flags::flag_a, Flags::flag_b) << std::endl; // true
std::cout << flags_2.contains(Flags::flag_a, Flags::flag_c) << std::endl; // false
Following member functions are available for setting all the flags or setting no flag:
all
/is_all
BEGIN_BITFLAGS(Flags)
FLAG(none)
FLAG(flag_a)
FLAG(flag_b)
END_BITFLAGS(Flags)
Flags flags = Flags::all();
std::cout << flags.is_all() << std::endl; // true
std::cout << flags.contains(Flags::flag_a) << std::endl; // true
std::cout << flags.contains(Flags::flag_b) << std::endl; // true
std::cout << flags.is_empty() << std::endl; // false
empty
/is_empty
BEGIN_BITFLAGS(Flags)
FLAG(none)
FLAG(flag_a)
FLAG(flag_b)
END_BITFLAGS(Flags)
Flags flags = Flags::empty();
std::cout << flags.is_all() << std::endl; // false
std::cout << flags.contains(Flags::flag_a) << std::endl; // false
std::cout << flags.contains(Flags::flag_b) << std::endl; // false
std::cout << flags.is_empty() << std::endl; // true
Not only that one can set and remove specific flag by using bitwise operators, but there are also special member functions set
and remove
that have the same purpose.
BEGIN_BITFLAGS(Flags)
FLAG(none)
FLAG(flag_a)
FLAG(flag_b)
END_BITFLAGS(Flags)
Flags flags = Flags::empty();
std::cout << flags.contains(Flags::flag_a) << std::endl; // false
std::cout << flags.contains(Flags::flag_b) << std::endl; // false
flags.set(Flags::flag_a);
flags.set(Flags::flag_b);
std::cout << flags.contains(Flags::flag_a) << std::endl; // true
std::cout << flags.contains(Flags::flag_b) << std::endl; // true
flags.remove(Flags::flag_a);
std::cout << flags.contains(Flags::flag_a) << std::endl; // false
std::cout << flags.contains(Flags::flag_b) << std::endl; // true
It is possible to toggle specific flag, i.e. if the flag is not already set, it will be set. On the other hand, if the flag is already set, it will be unset.
BEGIN_BITFLAGS(Flags)
FLAG(none)
FLAG(flag_a)
FLAG(flag_b)
END_BITFLAGS(Flags)
Flags flags = Flags::flag_a;
std::cout << flags.contains(Flags::flag_a) << std::endl; // true
flags.toggle(Flags::flag_a);
std::cout << flags.contains(Flags::flag_a) << std::endl; // false
std::cout << flags.contains(Flags::flag_b) << std::endl; // false
flags.toggle(Flags::flag_b);
std::cout << flags.contains(Flags::flag_b) << std::endl; // true
In order to clear all the flags currently set, one can use clear
member function.
BEGIN_BITFLAGS(Flags)
FLAG(none)
FLAG(flag_a)
FLAG(flag_b)
FLAG(flag_c)
END_BITFLAGS(Flags)
Flags flags = Flags::flag_a | Flags::flag_b;
std::cout << flags.contains(Flags::flag_a) << std::endl; // true
std::cout << flags.contains(Flags::flag_b) << std::endl; // true
std::cout << flags.contains(Flags::flag_c) << std::endl; // false
flags.clear();
std::cout << flags.contains(Flags::flag_a) << std::endl; // false
std::cout << flags.contains(Flags::flag_b) << std::endl; // false
std::cout << flags.contains(Flags::flag_c) << std::endl; // false
As you can see from the following chart, using raw_flag
s is as fast as using std::bitset
. However, using ordinary flag
s (i.e. flags with string representation) is a bit slower (as it is expected because of additional feature of having string representation).
If you want to run the benchmark yourself, you can use plot.py
script like:
$ python3 plot.py --benchmarks-dir <benchmark-json-dir>
$ git clone https://github.com/m-peko/bitflags
$ cd bitflags
$ # create the build directory
$ mkdir build
$ cd build
$ # configure the project
$ cmake -DBITFLAGS_BUILD_TESTS=ON ..
$ # compile
$ make
$ # compile tests
$ make tests
$ # run tests
$ make test
- Clang/LLVM >= 5
- MSVC++ >= 14.11 / Visual Studio >= 2017
- GCC >= 7.3
There are no 3rd party dependencies.
Feel free to contribute.
If you find that any of the tests fail, please create a ticket in the issue tracker indicating the following information:
- platform
- architecture
- library version
- minimal reproducible example
The project is available under the MIT license.
If you like the work bitflags
library is doing, please consider supporting it: