Type-safe bitwise operations - TL;DR
How to use Boost.Flags?
-
include the header
<boost/flags.hpp>
-
opt-in for an enumeration
E
by defining
BOOST_FLAGS_ENABLE(E)
in the same namespace as E
.
Example: on Godbolt compiler explorer
#include <boost/flags.hpp>
enum class pizza_toppings : unsigned int {
tomato = boost::flags::nth_bit(0), // == 0x01
cheese = boost::flags::nth_bit(1), // == 0x02
salami = boost::flags::nth_bit(2), // == 0x04
olives = boost::flags::nth_bit(3), // == 0x08
garlic = boost::flags::nth_bit(4), // == 0x10
all_toppings = tomato | cheese | salami | olives | garlic,
};
// enable Boost.Flags for pizza_toppings
BOOST_FLAGS_ENABLE(pizza_toppings)
enum class ice_cream_flavours : unsigned int {
vanilla = boost::flags::nth_bit(0), // == 0x01
chocolate = boost::flags::nth_bit(1), // == 0x02
strawberry = boost::flags::nth_bit(2), // == 0x04
};
// enable Boost.Flags for ice_cream_flavours
BOOST_FLAGS_ENABLE(ice_cream_flavours)
// order selected pizza
void order_pizza(pizza_toppings toppings) {
std::cout << "Pizza ordered with\n";
if (boost::flags::any(toppings & pizza_toppings::tomato)) {
std::cout << "- tomato\n";
}
if (boost::flags::any(toppings & pizza_toppings::cheese)) {
std::cout << "- cheese\n";
}
if (boost::flags::any(toppings & pizza_toppings::salami)) {
std::cout << "- salami\n";
}
if (boost::flags::any(toppings & pizza_toppings::olives)) {
std::cout << "- olives\n";
}
if (boost::flags::any(toppings & pizza_toppings::garlic)) {
std::cout << "- garlic\n";
}
std::cout << "\n";
}
// order selected dessert
void order_ice_cream(ice_cream_flavours flavours) {
std::cout << "Ice cream ordered with flavours\n";
if (boost::flags::any(flavours & ice_cream_flavours::vanilla)) {
std::cout << "- vanilla\n";
}
if (boost::flags::any(flavours & ice_cream_flavours::chocolate)) {
std::cout << "- chocolate\n";
}
if (boost::flags::any(flavours & ice_cream_flavours::strawberry)) {
std::cout << "- strawberry\n";
}
std::cout << "\n";
}
int main() {
pizza_toppings toppings = pizza_toppings::tomato | pizza_toppings::cheese; // a decent start
toppings |= pizza_toppings::salami | pizza_toppings::olives; // even better
order_pizza(toppings); // order main course
order_pizza(toppings & ~pizza_toppings::salami); // order a vegetarian pizza
order_ice_cream(ice_cream_flavours::vanilla); // order desert
// Guest: "Pizza without olives!"
// Waiter: "Ok, no olives. But what else to put on it?"
// error: negative mask is not a pizza topping
order_pizza(~pizza_toppings::olives);
// Guest: "Pizza with all toppings but olives!"
// Waiter: "Ok, got it!"
// Waiter takes note: Pizza with tomato, cheese, salami, garlic.
order_pizza(pizza_toppings::all_toppings & ~pizza_toppings::olives);
// error: mixing different enumerations
toppings |= ice_cream_flavours::strawberry;
// error: called with wrong enumeration
order_ice_cream(toppings);
}
Why use Boost.Flags?
-
provides type-safe bitwise operations for flag-like scoped / unscoped enumerations
-
turns undetected logical errors into type errors
Characteristics of Boost.Flags
-
is non-intrusive - only a function overload or a template specializations required to opt-in
-
single header-file library
-
no dependencies (only standard-library includes)
-
everything is
constexpr
-
zero-overhead in optimized builds
-
operator are found by ADL
-
requires at least C++11, uses newer features if available
Opt-in
To opt-in to Boost.Flags for an enumeration E
the easiest and most comfortable way is to use the macro BOOST_FLAGS_ENABLE
for E
in the same namespace as E
BOOST_FLAGS_ENABLE(E)
Best practice would be writing BOOST_FLAGS_ENABLE(E)
right after the enumeration in the same namespace.
In case the enumeration E
is defined inside a class or class template, the macro BOOST_FLAGS_ENABLE_LOCAL
has to be used for enabling:
class my_class {
// ...
enum class E:unsigned int { ... };
BOOST_FLAGS_ENABLE_LOCAL(E)
// ...
};
In case you want to ensure that an enumeration is enabled for Boost.Flags, you can test it e.g. with
static_assert(boost::flags::enable<E>::value, "Please check if E is enabled correctly and boost/flags.hpp is included.");
scoped or unscoped enumerations?
The usage of scoped enumeration is strongly recommended, as they provide more type safety than unscoped by prohibiting implicit conversion to the underlying integer type.
Nevertheless, for both kinds of enumerations Boost.Flags will detect semantical errors when using bitwise operations.
For unscoped enumerations Boost.Flags will delete binary operators &
, |
, ^
, &&
, ||
, ==
and !=
if
-
at least one is enabled for Boost.Flags and
-
both arguments are implicitly convertible to an integer type and
-
both arguments are not compatible (they are not the same enumeration or its complement)
i.e. Boost.Flags will prohibit that in those cases the built-in version will be used.
(For scoped enumerations this is not necessary, as those cases cannot occur.)
What about the underlying type?
-
For language standards before C++20 the underlying type is required as an
unsigned
type, for both scoped and unscoped enumerations! -
For unscoped enumerations with the
disable_complement
option the underlying type is always required!
(For details see below.)
The long story
Why do we use the bits of integer types for Boolean options?
-
easy to define, e.g. as macros, integer constants or enumerators
-
language support for setting and querying through bitwise operators
-
compact representation in memory
-
simple bulk transfer in interfaces (on many platforms an
int
can hold up to 32 different Boolean options) -
enforces the usage of names, especially to prevent
bool
parameters in interfaces:Boolean arguments loudly declare that the function does more than one thing. They are confusing and should be eliminated.
— C. Martin (Uncle Bob)
Clean Code
C++ has everything needed to work with flags, so why do we need Boost.Flags?
Because we can do too much with them!
Type-Safety
For flags based on integer-types or enumerations all values are implicitly convertible to int
. The compiler can’t help, when we accidentally apply binary operators to unrelated flags.
Here’s an example from the Win32-API:
In WinUser.h
we find the listbox-style
#define LBS_NOTIFY 0x0001L
and the button-style
#define BS_NOTIFY 0x00004000L
If we forget the leading L
of LBS_NOTIFY
and write
if (listbox_styles & BS_NOTIFY) { ... }
instead, we produced syntactically correct code but semantical nonsense.
Switching to enumerations makes it even worse
If the Windows team had decided to use unscoped enumerations instead of macros it wouldn’t have helped: the arguments of built-in bitwise operators are subject to integral promotion, i.e. they forget about the enumeration’s semantic overhead. Even worse:
-
The built-in operators
&
,|
,^
and~
return integer types. Assigning their results to enumeration-type variables require explicit casts, which is another source for errors undetectable by the compiler. -
The built-in operators
&=
,|=
,^=
require the left-hand type to be arithmetic, which excludes enumerations. -
Scoped enumerations (which didn’t exist when
WinUser.h
was created) prohibit implicit integral promotion and therefore don’t work with built-in bitwise operators at all.
But enumerations provide type-safety
On the other hand, enumerations, and especially scoped ones, improve the type-safety of our code:
-
variables and arguments of enumeration type can only by assigned (resp. invoked) with a value of the same type
-
no implicit integral promotion for scoped enumerations
With Boost.Flags you can get both!
With Boost.Flags we get support for all bitwise operators and type-safety. E.g. the following code
enum class E : unsigned int { a=1, b=2, c=4, d=8 };
BOOST_FLAGS_ENABLE(E)
-
enables all the operators
~
,&
,|
,^
,&=
,|=
,^=
forE
andcomplement<E>
(see below) -
the binary operators require both arguments to be from the enumeration or its
complement
-
the bitwise negation
~
changes the type fromE
tocomplement<E>
and vice-versa
The underlying type
Is the the specification of the underlying type required?
First, here the standard conforming decision diagram:
Ok, the unsigned
is more a theoretical issue, since all C++ compilers already used two’s complement for signed integers, even before it got mandatory with C++20 (https://en.cppreference.com/w/cpp/language/types).
Furthermore, scoped enumerations always have a fixed underlying type, which is int
if not specified (https://eel.is/c++draft/dcl.enum#5).
So, in case your compiler uses signed two’s complement, you can use the following dicision diagram:
It may seem pedantic, but defining the underlying type in the unscoped and disable_complement
case is crucial, as otherwise the operator ~
will invoke UB.
Compilers can track this down, e.g. when evaluating constant expressions (cf. https://eel.is/c++draft/expr.static.cast#10).
Starting with Clang 16 this is diagnosed as a hard error.
Note
|
For unscoped enums with unspecified underlying type, the compiler infers two types:
This hypothetical integer value type is not deducible from the type of the enumeration. (It requires to know the values of all enumerators.) Furthermore, it is in general not the same as the underlying type. E.g.
has underlying type |
Complementing the complement
Before going into details, let me tell you a little tale
Once, there was a kingdom, where the Queen of Hearts reigned with iron fist.
She had her own newspaper for proclamations, where they used a set of flags for font-styles
enum font_styles : unsigned int {
bold = 1,
italic = 2,
underline = 4
};
// special style for the Queen's proclamations
static constexpr auto queen_of_hearts_name = italic | underline;
One of the Queen's proclamations had been:
All my proclamations shall be encoded in C++, the Queen!
And there were loads of them every day.
A programmer for the newspaper, who was tired of typing queen_of_hearts_name
all the time, figured out that typing ~bold
instead also did the job. This saved him a lot of work.
One day, the troublemaker Alice came to the kingdom and the Queen ordered the creation of a new font-style
strikeout = 8
which should be used for Alice's name. So it got added to the `font_styles` enumeration.
The next day the following proclamation appeared in the newspaper:
All persons whose names are striken out are enemies of the kingdom, the Queen!
The last thing the programmer heard, as he ran away from the kingdom, was the queen shouting "Off with his head!".
There are two morals of this tale:
-
a syntactical: the negation flips all bits of the underlying integer. Even though
~bold
did the job in the beginning, it is different fromqueen_of_hearts_name
. -
a semantical:
~bold
isn’t a set of font-modifications. Its purpose is to exclude boldness from a given set of flags.
In a nutshell: the following two operations on sets of flags return another set of flags
-
conjunction (
operator&
): taking the flags that appear in all sets -
disjunction (
operator|
): taking the flags that appear in any of the sets
but negation (operator~
) in general does not.
Sets of flags resemble the semantics of mathematical lattices rather than the semantics of a Boolean algebra.
Mathematical justification for complement
This section provides a mathematical explanation, why complement
is required. It can easily be skipped.
-
The underlying type
U
with the bitwise operations~, &, |
and the constants0
and-1
(all bits set) form a (bitcount of U)-dimensional Boolean algebra (U, 0, -1, ∼, &, ∣) -
The defined flags (e.g.
bold
,italic
etc.) with the bitwise operations&, |
form a substructure (F, &, ∣) of U (cf. mathematical lattices) which in general is not closed under bitwise negation~
. -
Building the closure F of F wrt. ∼ generates a Boolean algebra which is a Boolean subalgebra of U.
Semantically the elements of F∖ F are not combinations of flags but negative flag-masks. The distinction of E
and complement<E>
keeps them apart on the type-level.
Finally, for the binary operators we have
-
operator&
-
operator&(E, E) -> E
-
operator&(complement<E>, E) -> E
-
operator&(E, complement<E>) -> E
-
operator&(complement<E>, complement<E>) -> complement<E>
-
-
operator|
-
operator|(E, E) -> E
-
operator|(complement<E>, E) -> complement<E>
-
operator|(E, complement<E>) -> complement<E>
-
operator|(complement<E>, complement<E>) -> complement<E>
-
-
operator^
-
operator^(E, E) -> E
-
operator^(complement<E>, E) -> complement<E>
-
operator^(E, complement<E>) -> complement<E>
-
operator^(complement<E>, complement<E>) -> E
-
Which means, on the meta-level the typeset {E, complement<E>} and the operation ∼, & and ∣ form a two-element Boolean algebra.
For the rare case where
-
the set of flags semantically forms a Boolean algebra and additionally
-
all bits of the underlying type correspond to flags
there exists the option disable_complement
which disables the usage of complement
and sets operator~(E) -> E
.
From flags to bool
Since scoped enumerations prevent implicit conversion to bool
, Boost.Flags provides the following functions (see also operator!
):
-
any(e) -> bool
: equivalent toe != E{}
-
none(e) -> bool
: equivalent toe == E{}
Furthermore, to test for intersection and entailment of flag-sets:
-
intersect(e1, e2) -> bool
: equivalent toe1 & e2 != E{}
-
disjoint(e1, e2) -> bool
: equivalent toe1 & e2 == E{}
-
subseteq(e1, e2) -> bool
: equivalent toe1 & e2 == e1
-
subset(e1, e2) -> bool
: equivalent tosubseteq(e1, e2) && e1 != e2
not
, not not
, why not?
Instead of calling any
and none
we can use operator!
-
!e
: equivalent tonone(e)
-
!!e
: equivalent toany(e)
Everything in order?
Let’s take a look at the relational operators <
, <=
, >
and >=
.
For enumerations (scoped and unscoped) C++ provides built-in relational operators which apply the operator to the values of the underlying type. This order we usually want to use as Compare predicate for sorted containers and sorting algorithms.
This total order, is a linearization of a partial order which naturally arises, when we abstract away from the underlying type (which is what enumerations are usually about). It is the order induced by flag entailment:
e1 < e2
if and only if subset(e1, e2) == true
But, if there is more than one flag, then this order is inherently partial and should never be used as Compare predicate for containers or algorithms.
As there are applications for two different orders on the same type, the best would be different sets of relational operators, but C++ offers only one such.
One approach would be, deleting all relational operators for flags and forcing the user to call a function / pass a Compare type as predicate.
But here comes the next obstacle from the language:
Note
|
C++ allows overloading operators when at least one of its arguments is of
This also works for operator templates, but there is one exception:
There is a Defect Report pending https://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#2730 which would make the latter also call the user defined operator template. |
Currently, we simply cannot overload relational operators for Boost.Flags enabled enumerations with a function template - it requires the definition of a function. Thus Boost.Flags provides the following macros
-
BOOST_FLAGS_REL_OPS_DELETE(E)
: deletes all relational operators for enumerationE
-
BOOST_FLAGS_REL_OPS_PARTIAL_ORDER(E)
: defines all relational operators to reflect the partial order induced bysubseteq
(see below)
Additionally, there exist the following Compare structs and objects:
-
totally ordered:
struct boost::flags::total_order_t
and objectboost::flags::total_order
-
partially ordered:
struct boost::flags::partial_order_t
and objectboost::flags::partial_order
As C++ allows to specialize std::less
for user-defined types, we can provide a macro
-
BOOST_FLAGS_SPECIALIZE_STD_LESS(E)
: specializesstd::less
to useboost::flags::total_order
which ensures always total ordering for std::less
based compare in containers and algorithms.
But for range-algorithms the language strikes back again: range compare uses
std::ranges::less
which is a struct and thus cannot be specialized. Internally it will do some checks and call operator <
if available.
This means, when we define BOOST_FLAGS_REL_OPS_PARTIAL_ORDER(E)
then for any range algorithm that compares values of type E
we must provide
boost::flags::total_order
(or another total ordering) as Compare. Otherwise, we’re in UB land.
So, our recommendation (and the currently most comfortable way without loosing any safety) would be
defining both BOOST_FLAGS_REL_OPS_DELETE(E)
and BOOST_FLAGS_SPECIALIZE_STD_LESS(E)
.
Note
|
For the mathematically inclined programmer:
|
Flags and
Flags
Instead of disjoint
and intersect
we can write:
-
!(e1 & e2)
: equivalent todisjoint(e1, e2)
-
!!(e1 & e2)
: equivalent tointersect(e1, e2)
If we update existing code with Boost.Flags, then expressions like e1 & e2
in Boolean contexts would have to be replaced by !!(e1 & e2)
. This is tedious and requires adding parenthesis.
Therefore, Boost.Flags provides a pseudo operator BOOST_FLAGS_AND
with the same precedence and associativity as &
but returning a bool
:
-
e1 BOOST_FLAGS_AND e2
: equivalent to!!(e1 & e2)
Alternatively, there is the options logical_and
, which enables operator&&
, see next section.
Note
|
|
Logically and
and or
The logical operator &&
and operator ||
have to handled with care, as they are the only ones to support short-circuit evaluation for the built-in versions.
Let’s first take a look at the semantics of the built-in operator &&
:
-
it converts its first arguments to
bool
, if itsfalse
then returnsfalse
(short-circuit evaluation) -
otherwise converts the second arguments to
bool
and returns it
This is quite different from the semantics of the built-in bitwise &
operator in a Boolean context:
-
it performs intergral promotion on both arguments and compute their bitwise AND
-
returns
false
if the outcome is equal to0
otherwisetrue
E.g. in Boolean contexts (1 && 2)
evaluates to true
, while (1 & 2)
evaluates to false
.
Though, for operator ||
and operator |
and values v1
and v2
, in Boolean contexts (v1 || v2)
and (v1 | v2)
always evaluate to the same value, there is still the difference, that operator ||
uses short-circuit evaluation and operator |
does not.
Furthermore, operator &&
and operator ||
have lower precedences than operator &
and operator |
(cf. C++ Operator Precedence), which is another source for errors.
Since logical operators &&
and ||
can easily get confused with their bitwise counterparts &
and |
, by default Boost.Flags disables operator&&
and operator||
, when both arguments are implicitly convertible to integral types, to prevent accidentally calling the built-in versions.
Nevertheless, sometimes operator &&
comes in handy or is even required, e.g. when Boost.Flags is used together with other generic libraries. When for an enabled enumeration E
BOOST_FLAGS_ENABLE_LOGICAL_AND(E)
// resp. for a class local enum
BOOST_FLAGS_ENABLE_LOCAL_LOGICAL_AND(E)
// instead of
// BOOST_FLAGS_ENABLE(E)
// resp.
// BOOST_FLAGS_ENABLE_LOCAL(E)
is defined, then operator &&
is overloaded for E
with the semantics
operator&&(e1, e2) -> bool { return !!(e1 & e2); }
And once more, there is no short-circuit evaluation!
Warning
|
When applying Boost.Flags to existing code, please be cautious enabling
So, before enabling |
Note
|
To achieve short-circuit evaluation for expresions with flags, convert the flag-value arguments to |
Utilities
Functions
Boost.Flags provides the following functions for an enabled enumeration E
:
-
make_null(E) -> E { return E{}; }
-
make_if(E e, bool set) -> E { return set ? e : E{}; }
-
modify(E e, E mod, bool set) -> E { return set ? e | mod : e & ~mod; }
-
modify_inplace(E& e, E mod, bool set) -> E& { e = modify(e, mod, set); return e; }
-
add_if(E e, E mod, bool add) -> E { return add ? e | mod : e; }
-
add_if_inplace(E& e, E mod, bool add) -> E& { e = add_if(e, mod, add); return e; }
-
remove_if(E e, E mod, bool remove) -> E { return remove ? e & ~mod : e; }
-
remove_if_inplace(E& e, E mod, bool remove) -> E& { e = remove_if(e, mod, remove); return e; }
-
get_underlying(E e) -> typename underlying_type<E>::type { return static_cast<typename underlying_type<E>::type>(e); }
-
from_underlying(typename underlying_type<E>::type u) -> E { return static_cast<E>(u); }
All utility functions are defined in namespace boost::flags
.
The macro BOOST_FLAGS_USING_UTILITIES()
imports all utility functions, except from_underlying
, into the current naespace.
and for generating the values of enumerators:
-
template<typename T = int> nth_bit(unsigned int n) -> underlying_or_identity<T>::type { return static_cast<underlying_or_identity<T>::type>(1) << n; }
where typeT
can be either an enumeration or an integral type, andunderlying_or_identity
is a type-trait returning the underlying type of the enumeration or is the type-identity respectively -
template<typename T> next_bit(T v) -> T { return v << 1; }
Flags generator
Boost.Flags provides a flag_generator
class template to iterate over the bits of a flag-enum. The class template is usually not create directly but through a call the one of the following functions
// generates from first to last (incl.)
constexpr flag_generator<E> flags_from_to(E first, E last);
// generates from E(1) to last (incl.)
constexpr flag_generator<E> flags_to(E last);
// generates all bits of the underlying type of E, starting from E(1)
constexpr flag_generator<E> flags_all();
Example:
// using enum pizza_toppings from example above
auto base_toppings = pizza_toppings::tomato | pizza_toppings::cheese;
for (auto f : boost::flags::bits_to(pizza_toppings::garlic)) {
if (boost::flags::any(f & base_toppings)) {
std::cout << get_underlying(f) << "\n";
}
}
The template<typename E> flag_generator
provides an internal iterator and member functions begin()
and end()
returning the resp. iterators.
It is contructed with the lowest and highest flag that shall be iterated over.
If flag_generator
is constructor with flags not having exactly one bit set, then the behaviour is undefined.
Other flags-like enum implementations
There are several articles and libraries on flag-like enums available. The following is not exhaustive list found on the web in January 2024:
-
https://www.boost.org/doc/libs/1_84_0/boost/detail/bitmask.hpp (BOOST_BITMASK, BB)
-
https://www.sandordargo.com/blog/2022/06/22/bitwise-enums (SD)
-
https://m-peko.github.io/craft-cpp/posts/different-ways-to-define-binary-flags/ (MP)
-
https://learn.microsoft.com/en-us/previous-versions/dotnet/netframework-4.0/ms229062(v=vs.100) (.net, language built-in flags support - not usable with ISO-C++ !)
Boost.Flags | BB | SD | GR | MP | Qt | JM | .net | |
---|---|---|---|---|---|---|---|---|
non-intrusive |
+ |
+ |
+ |
- |
- |
- |
- |
+ |
zero-overhead |
+ |
+ |
+ |
+ |
- |
- |
+ |
+ |
distinguishes complement |
+ |
- |
- |
- |
- |
- |
+ |
- |
operators as free functions |
+ |
+ |
+ |
- |
- |
- |
- |
+ |
everything is constexpr |
+ |
- |
- |
- |
- |
- |
- |
N/A |
unscoped type safety (*) |
+ |
- |
- |
+ |
+ |
- |
+ |
+ |
library available |
+ |
+ |
- |
+ |
+ |
+ |
+ |
+ |
(*) unscoped type safety: The library prevents usage of built-in binary operators, when both arguments are implicitly convertible to an integer type and at least one of the arguments is an enabled enumeration type.
Design rationale
The design of Boost.Flags was driven by the following principles:
-
type-safety
-
the library shall enforce that binary operators can only be applied to flags or their complements from the same enumeration
-
flag values must be distinguished from their complements on the type-level
-
-
non-intrusive
-
as few as possible changes to the definition of existing flag-like enumeration shall be required
-
no new types for flags, except
complement
-
-
opt-in shall be done by
-
function overload or
-
template specialization
-
-
zero-overhead
-
for optimized builds operator-calls must produce the same assembly code as built-in calls on the underlying integers
-
-
no change of semantics for existing code
-
enabling Boost.Flags for an existing flag-like enumeration must not produce valid code with different semantics
-
The first four principles are met by the library. The last principle of course has a notable exception:
For a given enumeration E
values of type complement<E>
may invoke different overloads than values of E
.
Reference
The contents of the library are in namespace boost::flags.
All operators and functions in this library
-
are
constexpr
andnoexcept
-
use attribute
[[nodiscard]]
except for assignment operators&=
,|=
,^=
and functionsmodify_inplace
,add_inplace
,remove_inplace
Most of the function signatures for operators are written with trailing return type but the leading auto
is left out for readability.
Supported language versions
Boost.Flags requires at least C++11.
Concepts are used if available and not disabled using BOOST_FLAGS_HAS_CONCEPTS
.
Macros to opt-in
BOOST_FLAGS_ENABLE
For an enumeration E
at namespace scope the macro
BOOST_FLAGS_ENABLE(E)
enables all operators of Boost.Flags.
BOOST_FLAGS_ENABLE(E)
imports all operators of Boost.Flags into the current namespace (cf. BOOST_FLAGS_USING_OPERATORS()).
BOOST_FLAGS_ENABLE_DISABLE_COMPLEMENT
For an enumeration E
at namespace scope the macro
BOOST_FLAGS_ENABLE_DISABLE_COMPLEMENT(E)
enables all operators of Boost.Flags and disables the usage of complement<E>
(see also disable_complement
)
BOOST_FLAGS_ENABLE_DISABLE_COMPLEMENT(E)
imports all operators of Boost.Flags into the current namespace (cf. BOOST_FLAGS_USING_OPERATORS()).
BOOST_FLAGS_ENABLE_LOGICAL_AND
For an enumeration E
at namespace scope the macro
BOOST_FLAGS_ENABLE_LOGICAL_AND(E)
enables all operators of Boost.Flags and enables operator&&
for E
(see also logical_and
)
BOOST_FLAGS_ENABLE_LOGICAL_AND(E)
imports all operators of Boost.Flags into the current namespace (cf. BOOST_FLAGS_USING_OPERATORS()).
BOOST_FLAGS_ENABLE_DISABLE_COMPLEMENT_LOGICAL_AND
For an enumeration E
at namespace scope the macro
BOOST_FLAGS_ENABLE_DISABLE_COMPLEMENT_LOGICAL_AND(E)
enables all operators of Boost.Flags,disables the usage of complement<E>
(see also disable_complement
) and enables operator&&
for E
(see also logical_and
)
BOOST_FLAGS_ENABLE_DISABLE_COMPLEMENT_LOGICAL_AND(E)
imports all operators of Boost.Flags into the current namespace (cf. BOOST_FLAGS_USING_OPERATORS()).
BOOST_FLAGS_ENABLE_LOCAL
For an enumeration E
at class scope the macro
BOOST_FLAGS_ENABLE_LOCAL(E)
enables all operators of Boost.Flags.
BOOST_FLAGS_ENABLE_LOCAL(E)
creates friend functions for all operators of Boost.Flags for the enumeration E
(cf. BOOST_FLAGS_FORWARD_OPERATORS_LOCAL(E)).
BOOST_FLAGS_ENABLE_DISABLE_COMPLEMENT
For an enumeration E
at class scope the macro
BOOST_FLAGS_ENABLE_LOCAL_DISABLE_COMPLEMENT(E)
enables all operators of Boost.Flags and disables the usage of complement<E>
(see also disable_complement
)
BOOST_FLAGS_ENABLE_LOCAL_DISABLE_COMPLEMENT(E)
imports all operators of Boost.Flags into the current namespace (cf. BOOST_FLAGS_USING_OPERATORS()).
BOOST_FLAGS_ENABLE_LOCAL_LOGICAL_AND
For an enumeration E
at class scope the macro
BOOST_FLAGS_ENABLE_LOCAL_LOGICAL_AND(E)
enables all operators of Boost.Flags and enables operator&&
for E
(see also logical_and
)
BOOST_FLAGS_ENABLE_LOCAL_LOGICAL_AND(E)
imports all operators of Boost.Flags into the current namespace (cf. BOOST_FLAGS_USING_OPERATORS()).
BOOST_FLAGS_ENABLE_LOCAL_DISABLE_COMPLEMENT_LOGICAL_AND
For an enumeration E
at class scope the macro
BOOST_FLAGS_ENABLE_LOCAL_DISABLE_COMPLEMENT_LOGICAL_AND(E)
enables all operators of Boost.Flags,disables the usage of complement<E>
(see also disable_complement
) and enables operator&&
for E
(see also logical_and
)
BOOST_FLAGS_ENABLE_LOCAL_DISABLE_COMPLEMENT_LOGICAL_AND(E)
imports all operators of Boost.Flags into the current namespace (cf. BOOST_FLAGS_USING_OPERATORS()).
Enabling helper functions, types and macros
Note
|
The functions, types and macros in this section are usually not used directly, but implicitly through one of the |
BOOST_FLAGS_USING_OPERATORS
The macro BOOST_FLAGS_USING_OPERATORS()
imports all operators from namespace boost::flags
into the current namespace.
This will enable ADL for Boost.Flags operators for enumerations in the current namespace.
BOOST_FLAGS_USING_UTILITIES
The macro BOOST_FLAGS_USING_UTILITIES()
imports all utility functions from namespace boost::flags
into the current namespace.
This will enable ADL for Boost.Flags utility functions for enumerations in the current namespace.
Note
|
To import the utility functions into the current namespace the macro |
BOOST_FLAGS_USING_ALL
The macro BOOST_FLAGS_USING_ALL()
is a shorthand for
BOOST_FLAGS_USING_OPERATORS()
BOOST_FLAGS_USING_UTILITIES()
BOOST_FLAGS_FORWARD_OPERATORS
For an enumeration E
the macro BOOST_FLAGS_FORWARD_OPERATORS(E)
creates forwarding functions for all Boost.Flags operators for the enumeration E
in the current namespace.
Instead of BOOST_FLAGS_ENABLE(E)
, the sequence
constexpr bool boost_flags_enable(E) { return true; }
BOOST_FLAGS_FORWARD_OPERATORS(E)
can be used, which creates forwarding functions instead of importing the operators.
BOOST_FLAGS_FORWARD_OPERATORS_LOCAL
For an enumeration E
the macro BOOST_FLAGS_FORWARD_OPERATORS_LOCAL(E)
creates forwarding friend functions for all Boost.Flags operators for the enumeration E
in the current class / class template.
Note
|
Since the language does not allow |
boost_flags_enable
The function boost_flags_enable
can be overloaded for an enumeration E
either
-
with return-type
bool
:constexpr bool boost_flags_enable(E) { return true; }
enables Boost.Flags for an enumeration
E
. All optional features are disabled. -
or with return-type
boost::flags::options
: The flag-enumerationboost::flags::options
has the options-
enable
: enablesE
-
disable_complement
: disables the usage ofcomplement<E>
(see alsodisable_complement
) -
logical_and
: enablesoperator&&
forE
(see alsological_and
) e.g.constexpr boost::flags::options boost_flags_enable(E) { return boost::flags::options::enable // enable E (required) | boost::flags::options::disable_complement // disable the usage of the complement | boost::flags::options::logical_and // enable operator&& ; }
-
The function boost_flags_enable
is looked up using ADL.
In case E
is defined inside a class, a friend
function can be used for enabling. E.g.
class my_class {
// ...
enum class E:unsigned int { ... };
friend constexpr bool boost_flags_enable(E) { return true; }
// ...
};
A Boost.Flags enabled (scoped/unscoped) enumeration will also be called (scoped/unscoped) flags.
Alternatively to using boost_flags_enable
, the class template boost::flags::enable
can be specialized.
enable<T>
To enable the Boost.Flags functionality specialize the template for an enumeration
template<typename T> struct enable : std::false_type {};
and inherit from std::true_type
.
Example:
enum class my_flags : unsigned int {
option_a = 0x1,
option_b = 0x2,
};
template<> struct boost::flags::enable<my_flags> : std::true_type {};
A Boost.Flags enabled (scoped/unscoped) enumeration will also be called (scoped/unscoped) flags.
When boost::flags::enable
is specialized for E
any definition of boost_flags_enable
for E
are ignored
(cf. boost_flags_enable
).
disable_complement
If the specialization of enable
additionally inherits from disable_complement
then the usage of the complement
template (cf. operator~
) is disabled.
When disable_complement
is used, then the used enumeration must either be scoped, or has an explicit specified underlying type, otherwise the operator~
may invoke undefined behaviour. For more information, see the following note.
Example:
template<> struct boost::flags::enable<E>
: std::true_type
, boost::flags::disable_complement
{};
Note
|
For unscoped enumerations with unspecified underlying type, the compiler infers two types:
The legal values for the enumeration are those of the hypothetical integer value type. But, this hypothetical integer value type is not deducible from the type of the enumeration. (It requires to know the values of all enumerators.) Furthermore, it is in general not the same as the underlying type. E.g.
has underlying type |
logical_and
If the specialization of enable
additionally inherits from logical_and
then operator&&
is enabled.
Example:
template<> struct boost::flags::enable<E>
: std::true_type
, boost::flags::logical_and
{};
Types
complement<T>
The template complement
indicates that a value is the bitwise negation of a flag-value.
It is used to distinguish flag-values from negative masks.
template<typename T> struct complement;
By default the usage of complement
is enabled but can be disabled using disable_complement
.
total_order_t
The type boost::flags::total_order_t
defines a call-operator which accepts two compatible flag-arguments and compares their underlying values.
(cf. BOOST_FLAGS_SPECIALIZE_STD_LESS).
There is also a functions object
static constexpr boost::flags::total_order_t total_order;
partial_order_t
The type boost::flags::partial_order_t
defines a call-operator which accepts two compatible flag-arguments and compares them based on flag entailment.
(cf. BOOST_FLAGS_REL_OPS_PARTIAL_ORDER).
There is also a functions object
static constexpr boost::flags::partial_order_t partial_order;
Operators
operator~
Reverses all bits of the underlying integer representation of its argument.
The signature of operator~
depends on whether complement
is enabled (cf. disable_complement).
When complement
is enabled for flags E
-
operator~(E) -> complement<E>
-
operator~(complement<E>) -> E
otherwise
-
operator~(E) -> E
operator&
Applies a bitwise AND operation on the underlying integer representations of its arguments.
The signature of operator&
depends on whether complement
is enabled (cf. disable_complement).
When complement
is enabled for flags E
-
operator&(E, E) -> E
-
operator&(complement<E>, E) -> E
-
operator&(E, complement<E>) -> E
-
operator&(complement<E>, complement<E>) -> complement<E>
otherwise
-
operator&(E, E) -> E
All other operator&
where at least one of the arguments is enabled and both are implicitly convertible to an integer type are deleted.
operator|
Applies a bitwise AND operation on the underlying integer representations of its arguments.
The signature of operator|
depends on whether complement
is enabled (cf. disable_complement).
When complement
is enabled for flags E
-
operator|(E, E) -> E
-
operator|(complement<E>, E) -> complement<E>
-
operator|(E, complement<E>) -> complement<E>
-
operator|(complement<E>, complement<E>) -> complement<E>
otherwise
-
operator|(E, E) -> E
All other operator|
where at least one of the arguments is enabled and both are implicitly convertible to an integer type are deleted.
operator^
Applies a bitwise XOR operation on the underlying integer representations of its arguments.
The signature of operator^
depends on whether complement
is enabled (cf. disable_complement).
When complement
is enabled for flags E
-
operator^(E, E) -> E
-
operator^(complement<E>, E) -> complement<E>
-
operator^(E, complement<E>) -> complement<E>
-
operator^(complement<E>, complement<E>) -> E
otherwise
-
operator^(E, E) -> E
All other operator^
where at least one of the arguments is enabled and both are implicitly convertible to an integer type are deleted.
operator&=
Performs a bitwise AND assignment on the underlying integer representations of its arguments.
The signature of operator&=
depends on whether complement
is enabled (cf. disable_complement).
When complement
is enabled for flags E
-
operator&=(E&, E) -> E&
-
operator&=(E&, complement<E>) -> E&
-
operator&=(complement<E>&, complement<E>) -> complement<E>&
Note
|
The assignment |
otherwise
-
operator&=(E&, E) -> E&
operator|=
Performs a bitwise OR assignment on the underlying integer representations of its arguments.
The signature of operator|=
depends on whether complement
is enabled (cf. disable_complement).
When complement
is enabled for flags E
-
operator|=(E&,E) -> E&
-
operator|=(complement<E>&,E) -> complement<E>&
-
operator|=(complement<E>&,complement<E>) -> complement<E>&
Note
|
The assignment |
otherwise
-
operator|=(E&,E) -> E&
operator^=
Performs a bitwise XOR assignment on the underlying integer representations of its arguments.
The signature of operator^=
depends on whether complement
is enabled (cf. disable_complement).
When complement
is enabled for flags E
-
operator^=(E&,E) -> E&
-
operator^=(complement<E>&,E) -> complement<E>&
Note
|
The assignment |
otherwise
-
operator^=(E&,E) -> E&
operator!
Tests if a value is empty.
// pseudo code
[[nodiscard]] constexpr bool operator!(E e) noexcept { return e == E{}; }
Pseudo operator BOOST_FLAGS_AND
Takes the bitwise AND of its arguments and converts the result to bool
.
// pseudo code
[[nodiscard]] constexpr bool operator BOOST_FLAGS_AND(E e1, E e2) noexcept { return !!(e1 & e2); }
BOOST_FLAGS_AND
has same precedence and associativity as operator&
. It is a macro defined as
#define BOOST_FLAGS_AND & boost::flags::impl::pseudo_and_op_tag{} &
operator== , operator!=
The description is only given for operator==
. Calls with reversed arguments and to operator!=
will constructed by rewrite rules.
-
operator==(E, E) -> bool
-
operator==(complement<E>, complement<E>) -> bool
equality is defined as usual by applying the operator to the underlying integer.
Furthermore the following overloads are defined
-
operator==(E, std::nullptr_t) -> bool
-
operator==(E, boost::flags::null_tag) -> bool
Both test for equality with an underlying value of 0
. The macro BOOST_FLAGS_NULL
defines an instance of boost::flags::null_tag
.
All other operator==
and operator!=
where at least one of the arguments is enabled and both are implicitly convertible to an integer type are deleted.
Note
|
If
compiles and returns the expected result. when |
Relational operators <, <=, > and >=
The relational operators for enumerations (scoped and unscoped) are provided by the language (by applying the resp. operator to the underlying value). Furthermore, the current wording doesn’t allow overloading or deleting those operators by a templated operator (there is a pending Defect Report on this topic: https://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#2730).
There are macros BOOST_FLAGS_REL_OPS_PARTIAL_ORDER to overload and BOOST_FLAGS_REL_OPS_DELETE to delete relational operators.
Warning
|
It is recommended to define either This prohibits the accidental usage of relational operators with flag values ( The built-in semantics for relational operators compare the underlying numerical values and do not coincide with flag entailment!). When flags have to be stored in ordered container or sorted, please either define Range algorithms require the specification of |
Boolean predicates
any
Tests if a value is not empty.
// pseudo code
[[nodiscard]] constexpr bool any(E e) noexcept { return e != E{}; }
none
Tests if a value is empty.
// pseudo code
[[nodiscard]] constexpr bool none(E e) noexcept { return e == E{}; }
intersect
Tests if two values have common bits set.
// pseudo code
[[nodiscard]] constexpr bool intersect(E e1, E e2) noexcept { return e1 & e2 != E{}; }
disjoint
Tests if two values do not have a common bit set.
// pseudo code
[[nodiscard]] constexpr bool disjoint(E e1, E e2) noexcept { return e1 & e2 == E{}; }
subseteq
Tests if all bits set in the first argument are also set in the second argument.
// pseudo code
[[nodiscard]] constexpr bool subseteq(E e1, E e2) noexcept { return e1 & e2 == e1; }
subset
Tests if the bits set in the first argument are a proper subset of the bits in the second argument.
// pseudo code
[[nodiscard]] constexpr bool subset(E e1, E e2) noexcept { return subseteq(e1, e2) && (e1 != e2); }
Utility functions
make_null
// pseudo code
[[nodiscard]] constexpr E make_null(E) noexcept { return E{}; }
For flags E
returns an empty instance of type E
, i.e. with underlying value of 0
.
make_if
// pseudo code
[[nodiscard]] constexpr E make_if(E e, bool set) noexcept { return set ? e : E{}; }
Depending on set
returns either the first argument or empty an instance of type E
.
modify
// pseudo code
[[nodiscard]] constexpr E modify(E e, E mod, bool set) noexcept { return set ? e | mod : e & ~mod; }
Depending on set
either adds or removes all bits from mod
to e
and returns the result.
modify_inplace
// pseudo code
constexpr E& modify(E& e, E mod, bool set) noexcept { e = modify(e, mod, set); return e; }
Similar to modify
but applies the modification to e
and returns it as reference.
add_if
// pseudo code
[[nodiscard]] constexpr E add_if(E e, E mod, bool add) noexcept { return add ? e | mod : e; }
Depending on add
either adds all bits from mod
to e
or leaves e
unmodified and returns the result.
add_if_inplace
// pseudo code
constexpr E& add_if(E& e, E mod, bool add) noexcept { e = add_if(e, mod, add); return e; }
Similar to add_if
but applies the modification to e
and returns it as reference.
remove_if
// pseudo code
[[nodiscard]] constexpr E remove_if(E e, E mod, bool remove) noexcept { return remove ? e & ~mod : e; }
Depending on remove
either removes all bits from mod
from e
or leaves e
unmodified and returns the result.
remove_if_inplace
// pseudo code
constexpr E& remove_if(E& e, E mod, bool remove) noexcept { e = remove_if(e, mod, remove); return e; }
Similar to remove_if
but applies the modification to e
and returns it as reference.
get_underlying
Returns the underlying value.
Let U
be the underlying type of enabled enum E
// pseudo code
constexpr U get_underlying(E e) noexcept { return static_cast<U>(e); }
from_underlying
Casts an value from underlying value the an enabled enum.
Let U
be the underlying type of enabled enum E
// pseudo code
constexpr E from_underlying(U u) noexcept { return static_cast<E>(u); }
nth_bit
// pseudo code
template<typename T = int>
[[nodiscard]] constexpr underlying_or_identity<T>::type nth_bit(unsigned int n) noexcept {
return static_cast<underlying_or_identity<T>::type>(1) << n;
}
Returns a value of type T
with the n-th bit from the right set (zero-based) set.
The type T
can be either an enumeration or an integral type, and underlying_or_identity
is a type-trait returning the underlying type of the enumeration or it is the type-identity respectively.
next_bit
// pseudo code
template<typename T>
[[nodiscard]] constexpr T next_bit(T v) { return v << 1; }
Returns to a value v
of type T
the value with the next higher bit set (if available).
The behaviour is undefined if more than one bit is set in value v
.
Flags generator
flag_generator
The template<typename E> flag_generator
provides an internal iterator and member functions begin()
and end()
returning the resp. iterators.
It is contructed with the lowest and highest flag that shall be iterated over.
If flag_generator
is constructor with flags not having exactly one bit set, then the behaviour is undefined.
flags_from_to
The function
template<typename E>
[[nodiscard]] constexpr flag_generator<E> flags_from_to(E first, E last);
returns a flag_generator
instance iterating from first
to last
(incl.).
flags_to
The function
template<typename E>
[[nodiscard]] constexpr flag_generator<E> flags_to(E last);
returns a flag_generator
instance iterating from E(1)
to last
(incl.).
flags_all
The function
template<typename E>
[[nodiscard]] constexpr flag_generator<E> flags_all();
returns a flag_generator
instance iterating from E(1)
to the most significant bit of the underlying type of E
(incl.), i.e. all bits of the underlying type.
Macros
BOOST_FLAGS_NULL
The macro BOOST_FLAGS_NULL
can be used for (in-)equailty testing with a flag-value.
For any value e
of type flags E
the expression e == BOOST_FLAGS_NULL
is equivalent to e == E{}
. Similarly for e != BOOST_FLAGS_NULL
, BOOST_FLAGS_NULL == e
and BOOST_FLAGS_NULL != e
.
BOOST_FLAGS_REL_OPS_DELETE
The macro BOOST_FLAGS_REL_OPS_DELETE(E)
deletes all relational operators for a Boost.Flags enabled enumeration E
.
The macro BOOST_FLAGS_REL_OPS_DELETE(E)
has to be defined at global namespace.
BOOST_FLAGS_SPECIALIZE_STD_LESS
The macro BOOST_FLAGS_SPECIALIZE_STD_LESS(E)
specialize std::less for E and complement<E> with the total order based on the value of the underlying integer type (i.e. <
on the underlying integer type).
The macro BOOST_FLAGS_SPECIALIZE_STD_LESS(E)
has to be defined at global namespace.
The definition of BOOST_FLAGS_SPECIALIZE_STD_LESS(E)
will not enablerange algorithms to use that total order. Here it is required to explicitely specify boost::flags::total_order
as compare object.
BOOST_FLAGS_REL_OPS_PARTIAL_ORDER
The macro BOOST_FLAGS_REL_OPS_PARTIAL_ORDER(E)
defines all relational operators for a Boost.Flags enabled enumeration E
.
The following semantics apply
-
e1 <= e2
: equivalent tocontained(e1, e2)
-
e1 >= e2
: equivalent tocontained(e2, e1)
-
e1 < e2
: equivalent to(contained(e1, e2) && e1 != e2)
-
e1 > e2
: equivalent to(contained(e2, e1) && e1 != e2)
-
e1 <=> e2
: has typestd::partial_ordering
and is equivalent to
e1 == e2
? std::partial_ordering::equivalent
: contained(e1, e2)
? std::partial_ordering::less
: contained(e2, e1)
? std::partial_ordering::greater
: std::partial_ordering::unordered
The macro BOOST_FLAGS_REL_OPS_PARTIAL_ORDER(E)
has to be defined at global namespace.
BOOST_FLAGS_SPECIALIZE_STD_LESS
The macro BOOST_FLAGS_SPECIALIZE_STD_LESS(E)
specializes std::less
for E
to use boost::flags::total_order
.
The macro BOOST_FLAGS_SPECIALIZE_STD_LESS(E)
has to be defined at global namespace.
Configuration Macros
If not specified, all the following macros are deduced from the system, compiler, C++ version. (Boost.Flags is tested with all major compilers on linux (ubuntu), macos and windows.)
BOOST_FLAGS_HAS_THREE_WAY_COMPARISON
Specifies, if three way comparison (⇐>) is available.
Possible values: 0 or 1
BOOST_FLAGS_HAS_PARTIAL_ORDERING
Specifies, if std::partial_ordering
is available.
Possible values: 0 or 1
BOOST_FLAGS_HAS_CONCEPTS
Specifies, if the compiler supports concepts.
Possible values: 0 or 1
BOOST_FLAGS_HAS_IS_SCOPED_ENUM
Specifies, if std::is_scoped_enum
is available.
Possible values: 0 or 1
BOOST_FLAGS_HAS_LOGICAL_TRAITS
Specifies, if the logical traits std::conjunction
, std::disjunction
and std::negation
are available.
Possible values: 0 or 1
BOOST_FLAGS_HAS_INLINE_VARIABLES
Specifies, if the inline
variables are available (and work correctly: broken in msvc before version v142).
Possible values: 0 or 1
BOOST_FLAGS_DEFINE_PARTIAL_ORDERING_OBJECTS
Specifies, if the partial_ordering emulation is defined.
Only used, when std::partial_ordering
is not available.
Possible values: 0 or 1
BOOST_FLAGS_HAS_REWRITTEN_CANDIDATES
Specifies, if rewitten candidates are availabe.
Possible values: 0 or 1
BOOST_FLAGS_WEAK_SYMBOL
Text used to specify a symbol with weak linkage
-
__declspec(selectany)
for mscv and clang-cl, ICC on windows -
__attribute__((weak))
for g and clang, except for g on mingw: in that case please defineBOOST_FLAGS_DEFINE_PARTIAL_ORDERING_OBJECTS=1
in exactly one translation unit
BOOST_FLAGS_ATTRIBUTE_NODISCARD
Text used as nodiscard attribute (e.g. [[nodiscard]]
).
BOOST_FLAGS_ATTRIBUTE_NODISCARD_CTOR
Text used as nodiscard attribute for constructors (e.g. [[nodiscard]]
).
Appendix A: Copyright and License
This documentation is
-
Copyright 2024 Tobias Loew
-
Distributed under the Boost Software License, Version 1.0.