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(E)

in the same namespace as E.

Example on Godbolt compiler explorer

#include <boost/flags.hpp>

enum class pizza_toppings {
    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(pizza_toppings)

enum class ice_cream_flavours {
    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(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 logical errors into type errors

Characteristics of Boost.Flags

  • is non-intrusive - only a macro invocation is required to opt-in

  • single header-file library

  • no dependencies (only standard-library includes)

  • compile-time usable - everything is constexpr

  • zero-overhead (in optimized builds)

  • operators 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 for E in the same namespace as E

enum class E { ... };
BOOST_FLAGS(E)

Best practice would be writing BOOST_FLAGS(E) right after its definition / declaration.

In case the enumeration E is defined inside a class or class template, the macro BOOST_FLAGS_LOCAL has to be used instead:

class my_class {
    // ...
    enum class E { ... };
    BOOST_FLAGS_LOCAL(E)
    // ...
};
Note

Inside class definitions, constexpr constructions using Boost.Flags enabled operators may fail to compile:

class my_class {
    // ...
    enum class E {
        flag_0 = 0x1,
        flag_1 = 0x2,
    };
    BOOST_FLAGS_LOCAL(E)

    static constexpr E flag_0_or_flag_1 = E::flag_0 | E::flag_1;
};

The reason is, that BOOST_FLAGS_LOCAL(E) generates friend operators which are declared in the class but defined is after it, thus they are inaccessible for in-class constexpr constructions. To work around this limitation, please use the following code:

BOOST_FLAGS_USING_OPERATORS()

class my_class {
    // ...
    enum class E {
        flag_0 = 0x1,
        flag_1 = 0x2,
    };
    BOOST_FLAGS_LOCAL(E, BOOST_FLAGS_NO_FORWARDING)

    static constexpr E flag_0_or_flag_1 = E::flag_0 | E::flag_1;
};

In case you want to check whether an enumeration is enabled for Boost.Flags, you can test it using boost::flags::enable (resp. boost::flags::enable_v for C++14 or later), e.g.

static_assert(boost::flags::enable<E>::value, "Please check if boost/flags.hpp is included and E is enabled correctly.");

Scoped or Unscoped enumerations

The usage of scoped enumeration is strongly recommended, as they prohibit implicit conversion to the underlying integer type and thus provide more type safety. Further, by default scoped enumerations do not pollute the current namespace with the names of their enumerators.

Nevertheless, for both kinds of enumerations Boost.Flags will detect semantical errors when using bitwise operations.

For scoped enumerations the language does not provide built-in bitwise and logical operators, so Boost.Flags will provide them as requested.

For unscoped enumerations the language provides built-in bitwise and logical operators. Hence, Boost.Flags will delete binary operators &, |, ^, &&, ||, == and != if

  • at least one of its arguments is enabled for Boost.Flags

and

  • both arguments are implicitly convertible to an integer type

and

  • for enumeration E both arguments are not compatible

The underlying type

In most situations the usage of Boost.Flags does not depend on the enumeration’s underlying type. Only when Boost.Flags is used with an unscoped enumeration E in connection with option disable_complement (i.e. operator~(E) -> E) then the specification of the underlying type is required to prevent undefined behavior. 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 (e.g. on many platforms an unsigned int can hold up to 32 different Boolean options)

  • enforces the usage of names, especially to prevent bool parameters in interfaces

C++ has everything built-in to work with boolean flags, so why do we need Boost.Flags?

Because the language allows to do too much with them!

Type-Safety

For flags based on integer-types or unscoped 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.

Furthermore, 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 enumerations, improve the type-safety of our code:

  • variables and arguments of enumeration type can only by assigned (resp. invoked) with a values of the correct type

  • there is 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 { a=1, b=2, c=4, d=8 };
BOOST_FLAGS(E)
  • enables all the operators ~, &, |, ^, &=, |=, ^= for E and complement<E> (see below)

  • the bitwise negation ~ changes the type from E to complement<E> and vice-versa

  • the binary operators require both arguments to be from the enumeration resp. its complement

The underlying type

Here is a standard conforming decision diagram, whether the specification of the underlying type required:

underlying type decision diagram conforming

Ok, the unsigned requirement for pre C++20 compilers is more of 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).

So, in case your compiler uses signed two’s complement, you can use the following simplified decision diagram:

underlying type decision diagram practical

It may seem pedantic, but defining the underlying type in the unscoped and disable_complement case is crucial, as otherwise the operator~ will invoke undefined behavior, when its result is not within the value range of the enumeration.
Compilers can track this down, e.g. when evaluating constant expressions (cf. https://eel.is/c++draft/expr.static.cast#8).
Starting with Clang 16 this is diagnosed as a hard error.

Note

For unscoped enums with unspecified underlying type, the compiler infers two types:

  • the underlying type (https://eel.is/c++draft/dcl.enum#7), which can be queried using std::underlying_type

  • a hypothetical integer value type with minimal width such that all enumerators can be represented (https://eel.is/c++draft/dcl.enum#8)
    "The width of the smallest bit-field large enough to hold all the values of the enumeration type […​]."

The deduction of this hypothetical integer value type requires static reflection which is only available in C++26. (It requires to know the values of all enumerators.) Furthermore, it is in general not the same as the underlying type. E.g.

enum TriBool {
    false_ = 0,
    true_ = 1,
    undefined = 2
};

has underlying type int on all major compilers, but its valid values are just 0, 1, 2 and 3 as the hypothetical integer value type with minimal width is a 2-bit unsigned integer.

Complementing the complement

Before going into details, here is a little story:

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 stricken 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 from queen_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.

Even though the underlying type together with its operators &, | and ~ denotes a Boolean algebra, when using it as set of flags only the operator & and | are endo-functions: the operator ~ returns an element from a complemented set.

So, inside the Boolean algebra of the underlying type, there exist two sets of flags, where each of them resembles the semantics of mathematical lattices and both are entangled by the operator ~.

Hence, for a Boost.Flags enabled enumeration E (where complement is not disabled) the bitwise not operator switches between the types E and complement<E>

  • operator~(E) -> complement<E>

  • operator~(complement<E>) -> E

We will call arguments for binary operators compatible if the Boost.Flags provided operators are viable, i.e. each one is implicitly convertible to E or complement<E>. Additionally, for operator== and operator!= both have to be convertible to E or both have to be convertible to complement<E>. For assignment operators additional requirements apply (cf. Operators).

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 constants 0 and -1 (all bits set) form a (bitcount of U)-dimensional Boolean algebra (U, 0, -1, ∼, &, ∣)

  • The defined flags (usually given by the single-bit enumerators) 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 cases where

  • the set of flags semantically form 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:

  • any(e) -> bool : equivalent to e != E{}

  • none(e) -> bool : equivalent to e == E{}

Furthermore, to test for intersection and entailment of flag-sets:

  • intersect(e1, e2) -> bool : equivalent to e1 & e2 != E{}

  • disjoint(e1, e2) -> bool : equivalent to e1 & e2 == E{}

  • subseteq(e1, e2) -> bool : equivalent to e1 & e2 == e1

  • subset(e1, e2) -> bool : equivalent to subseteq(e1, e2) && e1 != e2

Instead of calling any and none we can use operator!

  • !e : equivalent to none(e)

  • !!e : equivalent to any(e)

Flags and Flags

Instead of disjoint and intersect we can write:

  • !(e1 & e2) : equivalent to disjoint(e1, e2)

  • !!(e1 & e2) : equivalent to intersect(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 option BOOST_FLAGS_LOGICAL_AND, which enables operator&&, see next section.

Note

BOOST_FLAGS_AND is a macro defined as & boost::flags::pseudo_and_op_tag{} &.
The first & stores its left-hand argument in an intermediate type, which the second & evaluates with its right-hand argument.

Logical and and or

The logical operator&& and operator|| are special among overloadable operators in C++: their built-in versions use short-circuit evaluation, but overloaded versions behave like regular functions and always evaluate both arguments.

Furthermore, for integer and (non Boost.Flags enabled) unscoped enumeration-typed expressions a and b, both (a) & (b) and (a) && (b) (resp. (a) | (b) and (a) || (b)) are all syntactically valid expressions but with different semantics

  • (a) & (b) (resp. (a) | (b)) always evaluates b

  • (a) & (b) and (a) && (b) (resp. (a) | (b) and (a) || (b)) have different result-types

Even in Boolean contexts (a) & (b) and (a) && (b) return different results (e.g. for arguments 1 and 2).

To be on the safe side, Boost.Flags by default deletes operator&& and operator|| for enabled enumerations. When applied to existing code, this will lead to compilation errors and enforces revisiting their uses.

Nevertheless, sometimes operator&& comes in handy or is even required, e.g. when Boost.Flags is used together with other generic libraries. If for an enumeration E

BOOST_FLAGS(E, BOOST_FLAGS_LOGICAL_AND)

// resp. for a class local enum
BOOST_FLAGS_LOCAL(E, BOOST_FLAGS_LOGICAL_AND)

is defined, then operator&& is overloaded for E with the semantics

    operator&&(e1, e2) -> bool { return !!(e1 & e2); }

and there is no short-circuit evaluation!

Warning

When applying Boost.Flags to existing code, please be cautious enabling operator&&

  • it evaluates differently than the built-in version and doesn’t provide short-circuit evaluation

  • the usage of operator&& may have been wrong and operator& was intended

So, before enabling operator&&, it is best to compile the code without it and check whether the uses of operator&& are correct.

Note

To achieve short-circuit evaluation for expressions with flags, first convert the flag-value arguments to bool by using e.g. !!(…​) && !!(…​) or any(…​) && any(…​).

Calls to order

Let’s take a look at the relational operators <, <=, >, >= and <=>.

For integer-based types, including scoped and unscoped enumerations, C++ provides built-in relational operators which apply the operator to the values (of the underlying type in case of enumerations). The induced order is a total: for any two values exactly one of the operators <, ==, > evaluates to true. These total orders are syntactically important, as we normally use them as Compare predicate for sorted containers and sorting algorithms.

But semantically, these orders do not carry much information: for the (introducing example), the meaning of

pizza_toppings::cheese | pizza_toppings::salami < pizza_toppings::olives

reduces to some information about, which bits of the underlying type might be used by the different options. But this is something we wanted to abstract away in the first place. So, there is a dilemma: on the one hand, we require a total order for sorted containers and sorting algorithms. But on the other hand, the use of this order in other places may indicate logical errors and should be prohibited.

To handle this Boost.Flags provides the following macros:

  • BOOST_FLAGS_DELETE_REL(E): deletes all relational operators for enumeration E

  • BOOST_FLAGS_SPECIALIZE_STD_LESS(E): specializes std::less for enumeration E to use the total order induced by the underlying values

The recommendation is defining both BOOST_FLAGS_DELETE_REL(E) and BOOST_FLAGS_SPECIALIZE_STD_LESS(E) as it allows standard sorting and sorted containers for E while disabling usage of relational operators in user code.

Note

In case BOOST_FLAGS_DELETE_REL(E) is defined, then for range-algorithms like std::ranges::sort the definition of BOOST_FLAGS_SPECIALIZE_STD_LESS(E) is unfortunately not sufficient. Range based algorithms by default use std::ranges::less as predicate, which eventually will call operator <, furthermore, std::ranges::less is not a template but a struct and cannot be specialized.

So, to use range-algorithms in this case we have to provide the sort-predicate explicitly, which can be either std::less<E>{} or the generic boost::flags::total_order.

Utilities

Functions

All following utility functions are defined in namespace boost::flags. For an enabled enumeration E Boost.Flags provides the following functions:

Name

Type

Definition

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);

The macro BOOST_FLAGS_USING_UTILITIES() imports all utility functions, except from_underlying, through using-directives into the current namespace.

For generating the values of enumerators Boost.Flags provides furthermore:

  • template<typename T = int> nth_bit(unsigned int n) -> underlying_or_identity<T>::type
    returning the n-th power of 2

  • template<typename T> next_bit(T v) -> underlying_or_identity<T>::type
    returning the next power of 2 (if applied to a power of 2)

where 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 is the type-identity respectively

Other flags-like enum implementations

There are several articles and libraries on flag-like enums available. The following list is not exhaustive:

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 (*)

+

-

-

+

+

-

+

+

available as library

+

+

-

+

+

+

+

+

(*) 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 (resp. their complements) from the same enumeration

    • flag values shall be distinguishable 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

    • do not introduce new types when using flags on enumerations, except the complement template

  • opt-in shall be done by

    • by simple macro invocations

  • 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

    • erroneous usage of binary operators (e.g. applying operators to different enumerations) shall produce compilation errors

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.

Revision History

Changes in 1.??.0

  • Added to Boost

Reference

The contents of the library are in namespace boost::flags.

All operators and functions in this library

  • are constexpr and noexcept

  • use attribute nodiscard except for operators / functions returning a reference to one of its arguments

At most places they are left out for readability. Furthermore, many function signatures for operators are written with trailing return type, where the leading auto is also left out for readability.

Supported language versions

Boost.Flags requires at least C++11.

Concepts are used if available and not disabled by BOOST_FLAGS_HAS_CONCEPTS.

Macros to opt-in

BOOST_FLAGS

For an enumeration E at namespace scope the following variadic macro enables all operators of Boost.Flags

BOOST_FLAGS(E)

resp.

BOOST_FLAGS(E, OPTIONS)

where OPTIONS is an optional comma separated list containing at most one of each of the following options:

  • BOOST_FLAGS_LOGICAL_AND : additionally enables operator&& for E (see also logical_and)

  • BOOST_FLAGS_DISABLE_COMPLEMENT : disables the usage of complement<E> (see also disable_complement)

  • BOOST_FLAGS_NO_FORWARDING : will not generate forwarding friend operators when using BOOST_FLAGS_LOCAL (has no effect for BOOST_FLAGS) (see also Opt-in and BOOST_FLAGS_FORWARD_OPERATORS)

furthermore at most one of the following relational-operator options may be specified:

  • BOOST_FLAGS_DEFAULT_REL : use operators <, <=, >, >= and <=> which accept two compatible flag-arguments and compare their underlying values (cf. BOOST_FLAGS_SPECIALIZE_STD_LESS)

  • BOOST_FLAGS_DELETE_REL : delete operators <, <=, >, >= and <=> for type E (cf. BOOST_FLAGS_DELETE_REL)

If no relational-operator option is specified, the macro defaults to using BOOST_FLAGS_DEFAULT_REL.

Note

When errors are encountered using BOOST_FLAGS(E, …​) this can have multiple reasons (list is not exhaustive):

  • the invoked preprocessor is not standard conforming to at least C++11

  • misspelling one of the options

  • forgetting a comma or specifying a comma too much

  • specifying the same options more than once

  • specifying more than one relational-operator option

BOOST_FLAGS(E) imports all operators of Boost.Flags into the current namespace (cf. BOOST_FLAGS_USING_OPERATORS()).

BOOST_FLAGS_LOCAL

For an enumeration E at class scope use the following variadic macro to enables all operators of Boost.Flags

BOOST_FLAGS_LOCAL(E)

resp.

BOOST_FLAGS_LOCAL(E, OPTIONS)

enables all operators of Boost.Flags.

For the description of OPTIONS please refer to BOOST_FLAGS.

By default 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)).

Non-variadic macros for enabling

The following macros are primarily provided for non standard conforming preprocessors.

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_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

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_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_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_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_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…​ macros.

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.

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.

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 using declarations at class scope, Boost.Flags uses BOOST_FLAGS_FORWARD_OPERATORS_LOCAL to ensure that all operators can be found through ADL.

boost_flags_enable

The function boost_flags_enable can be overloaded for an enumeration E either

  • with return-type std::integral_constant<bool, true>:

    constexpr std::integral_constant<bool, true> boost_flags_enable(E) { return {}; }

    enables Boost.Flags for an enumeration E. All optional features are disabled.

  • or with return-type template<boost::flags::options Opts> boost::flags::options_constant, where the enumeration boost::flags::options has the following options:

    • enable: enables E

    • disable_complement: disables the usage of complement<E> (see also disable_complement)

    • logical_and: enables operator&& for E (see also logical_and) e.g.

      constexpr boost::flags::options_constant<
              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&&
          >
              boost_flags_enable(E) { return {}; }

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; }
    // ...
};

enable<T>

Alternatively to boost_flags_enable an enumeration can be enabled by specializing the following 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 {};

When boost::flags::enable is specialized for E any definition of boost_flags_enable for E are ignored (cf. boost_flags_enable).

In case variable templates are available (cf. BOOST_FLAGS_HAS_VARIABLE_TEMPLATES) then

template<typename E>
constexpr bool enable_v = enable<E>::value;

is also provided.

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
    {};

disable_complement

If the specialization of class template 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 behavior (cf. The underlying type and BOOST_FLAGS_DISABLE_COMPLEMENT).

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 BOOST_FLAGS_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. There exists also a function object total_order:

static constexpr boost::flags::total_order_t total_order;

When BOOST_FLAGS_DELETE_REL is specified, then total_order can be used as Compare-predicate for range-algorithms.

Operators

operator~

Reverses all bits of the underlying integer representation of its argument.

The signature of operator~ depends on whether complement is enabled (cf. BOOST_FLAGS_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. BOOST_FLAGS_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 viable operator& where at least one of the arguments is enabled for Boost.Flags and both are implicitly convertible to an integer type are deleted.

operator|

Applies a bitwise OR operation on the underlying integer representations of its arguments.

The signature of operator| depends on whether complement is enabled (cf. BOOST_FLAGS_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 viable operator| where at least one of the arguments is enabled for Boost.Flags 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. BOOST_FLAGS_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 viable operator^ where at least one of the arguments is enabled for Boost.Flags 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. BOOST_FLAGS_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 operator&=(complement<E>&, E) is not available, as it would change the type of the first argument.

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. BOOST_FLAGS_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 operator|=(E&, complement<E>) is not available, as it would change the type of the first argument.

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. BOOST_FLAGS_DISABLE_COMPLEMENT).

When complement is enabled for flags E

  • operator^=(E&,E) -> E&

  • operator^=(complement<E>&,E) -> complement<E>&

Note

The assignment operator^=(E&, complement<E>) and operator^=(complement<E>&, complement<E>) are not available, as they would change the type of the first argument.

otherwise

  • operator^=(E&,E) -> E&

operator!

The operator! tests if a value is empty.

    // pseudo code
    bool operator!(E e) { return e == E{}; }

Pseudo operator BOOST_FLAGS_AND

The pseudo operator BOOST_FLAGS_AND computes the bitwise AND of its arguments and converts the result to bool.

    // pseudo code
    bool operator BOOST_FLAGS_AND(E e1, E e2) { 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 provided only for operator==. Calls with reversed arguments and calls to operator!= are also available and return the respective results.

  • 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 viable operator== and operator!= where at least one of the arguments is enabled for Boost.Flags and both are implicitly convertible to an integer type are deleted.

Note

If E is a scoped enumeration then

    bool foo(E e){
        return e == 0;  // literal 0 converts to nullptr, thus
    }                   // operator==(E, std::nullptr_t) will be called

compiles and returns the expected result.

But, in case E is an unscoped enumeration then the above code will fail to compile, as the overload operator(E, int) is deleted for unscoped enumerations. (If it was not deleted, then e would implicitly convert to its underlying integer type and built-in operator== would step in. Thus, comparison with arbitrary integer values would be possible.)

Utility functions

any

    // pseudo code
    bool any(E e) { return e != E{}; }

Tests if a value is not empty.

none

    // pseudo code
    bool none(E e) { return e == E{}; }

Tests if a value is empty.

intersect

    // pseudo code
    bool intersect(E e1, E e2) { return e1 & e2 != E{}; }

Tests if two values have at least one common bits set.

disjoint

    // pseudo code
    bool disjoint(E e1, E e2) { return e1 & e2 == E{}; }

Tests if two values do not have a common bit set.

subseteq

    // pseudo code
    bool subseteq(E e1, E e2) { return e1 & e2 == e1; }

Tests if all bits set in the first argument are also set in the second argument.

subset

    // pseudo code
    bool subset(E e1, E e2) { return subseteq(e1, e2) && (e1 != e2); }

Tests if the bits set in the first argument are a proper subset of the bits in the second argument.

make_null

    // pseudo code
    E make_null(E) { return E{}; }

For flags E returns an empty instance of type E, i.e. with underlying value 0.

make_if

    // pseudo code
    E make_if(E e, bool set) { return set ? e : E{}; }

Depending on set returns either the first argument or empty an instance of type E.

add_if

    // pseudo code
    E add_if(E e, E mod, bool add) { 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
    E& add_if_inplace(E& e, E mod, bool add) { 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
    E remove_if(E e, E mod, bool remove) { 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
    E& remove_if_inplace(E& e, E mod, bool remove) { e = remove_if(e, mod, remove); return e; }

Similar to remove_if but applies the modification to e and returns it as reference.

modify

    // pseudo code
    E modify(E e, E mod, bool set) { 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
    E& modify_inplace(E& e, E mod, bool set) { e = modify(e, mod, set); return e; }

Similar to modify 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
    U get_underlying(E e) { 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
    E from_underlying(U u) { return static_cast<E>(u); }

nth_bit

    // pseudo code
    template<typename T = int>
    underlying_or_identity<T>::type nth_bit(unsigned int n) {
        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>
    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 behavior is undefined if more than one bit is set in value v.

Macros

BOOST_FLAGS_NULL

The macro BOOST_FLAGS_NULL can be used for (in-)equality 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_DELETE_REL

The macro BOOST_FLAGS_DELETE_REL(E) deletes all relational operators for a Boost.Flags enabled enumeration E.
It must 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).
It must be defined at global namespace.

The definition of BOOST_FLAGS_SPECIALIZE_STD_LESS(E) will not enable range algorithms to use that total order. Here it is required to explicitly specify total_order as compare object.

Library configuration macros

If not specified, all the following macros are deduced from the system, compiler, the C++ version and feature-test. (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_REWRITTEN_CANDIDATES

Specifies, if rewitten candidates are available.

Possible values: 0 or 1

BOOST_FLAGS_HAS_VARIABLE_TEMPLATES

Specifies, if variable templates are available.

Possible values: 0 or 1

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]]).

This documentation is

Many thanks to Andreas Buhr, Thomas Forell and Thimo Neubauer for many comments and suggestions.