Single-header header-only C++11 / C++14 / C++17 library for easily managing set of auto-generated type-safe flags.

Overview

Single-header header-only C++11 / C++14 / C++17 library for easily managing set of auto-generated type-safe flags.

License Build Status Codecov Standard Standard Standard Compiler explorer

Quick start

#include <bitflags/bitflags.hpp>

BEGIN_BITFLAGS(Flags)
    FLAG(none)
    FLAG(flag_a)
    FLAG(flag_b)
    FLAG(flag_c)
END_BITFLAGS(Flags)

int main() {
    Flags flags = Flags::flag_a | Flags::flag_b;

    if (flags & Flags::flag_a) {
        std::cout << "flag_a is set" << std::endl;
    } else {
        std::cout << "flag_a is not set" << std::endl;
    }

    flags.toggle(Flags::flag_a);

    // ...

    return 0;
}

Table of Contents

Motivation

Inspiration for writing this bitflags library I got from the homonymous Rust's crate that you may find here.

Some people may ask: Why not just use enum class and assign binary literals?

Following example presents using enum class with std::uint8_t as underlying type (so that max number of flags is 9):

enum class Flags : std::uint8_t {
    none   = 0b0000,
    flag_a = 0b0001,
    flag_b = 0b0010,
    flag_c = 0b0010
};

Nothing is wrong with above usage, right?

Oh... you might not have noticed that both Flags::flag_b and Flags::flag_c have the same value assigned! That's the exact same mistake that developers make in everyday work... This mistake happened with std::uint8_t as underlying type but imagine how often would be mistakes if the underlying value is std::uint32_t or even std::uint64_t.

You got the point?

Assigning binary literals to flags is manual and error-prone process, especially if there is lots of flags and if developers change them over time.

So, why should I use bitflags library?

bitflags library provides you decently safe way of specifying your flags with 2 core features:

  • auto - generated flag values
  • auto - detected underlying type

Getting Started

bitflags is a single-header header-only C++11 / C++14 / C++17 library for easily managing set of auto-generated type-safe flags.

Raw flags vs flags?

bitflags library provides you with 2 flag types:

  • raw_flag without its string representation (sort of lightweight version)
  • flag with its string representation

How to Declare Set of Flags?

In order to declare a set of auto-generated flags, there are few helper macros that hide kind of "ugly" declaration syntax and provide auto-generated value for each flag.

Macros for creating set of raw flags, i.e. flags without string representation:

  • BEGIN_RAW_BITFLAGS(NAME)

    • NAME - name of set of raw flags
  • RAW_FLAG(NAME)

    • NAME - name of specific raw flag
  • END_RAW_BITFLAGS(NAME)

    • NAME - name of set of raw flags

Macros for creating set of ordinary flags, i.e. flags with string representation:

  • BEGIN_BITFLAGS(NAME)

    • NAME - name of set of flags
  • FLAG(NAME)

    • NAME - name of specific flag
  • END_BITFLAGS(NAME)

    • NAME - name of set of flags

Following snippet shows the use case of the above macros:

BEGIN_BITFLAGS(Flags)
    FLAG(none)
    FLAG(flag_a)
    FLAG(flag_b)
    FLAG(flag_c)
END_BITFLAGS()

and is translated into:

template <typename T>
struct FlagsImpl {
    using flag = bf::internal::flag<FlagsImpl, T>
    static constexpr flag none{ 0b0000, "none" };
    static constexpr flag flag_a{ 0b0001, "flag_a" };
    static constexpr flag flag_b{ 0b0010, "flag_b" };
    static constexpr flag flag_c{ 0b0100, "flag_c" };
};

using Flags = bf::bitflags<
    FlagsImpl<
        bf::bitflags<
            FlagsImpl<std::uint8_t>
        >::underlying_type
    >
>;

Usage is basically the same for raw_flags.

Bits and Names

While bits are part of both raw_flag and flag, names are part of flag type only.

Once the flags are specified, it is possible to get bits representing each flag as well as string representation of each flag:

BEGIN_RAW_BITFLAGS(RawFlags)
    RAW_FLAG(none)
    RAW_FLAG(flag_a)
    RAW_FLAG(flag_b)
END_RAW_BITFLAGS(RawFlags)

std::cout << RawFlags::flag_a.bits << std::endl;

and

BEGIN_BITFLAGS(Flags)
    FLAG(none)
    FLAG(flag_a)
    FLAG(flag_b)
END_BITFLAGS(Flags)

std::cout << Flags::flag_a.bits << " - " << Flags::flag_a.name << std::endl;

Bitwise Operators

The following binary operators are implemented for the generated flags:

  • NOT (~) operator
  • AND (&) operator
  • OR (|) operator
  • XOR (^) operator

Is Specific Flag Set?

In case we want to check whether specific flag is set or not, we have 2 options:

  1. use AND operator
BEGIN_BITFLAGS(Flags)
    FLAG(none)
    FLAG(flag_a)
    FLAG(flag_b)
END_BITFLAGS(Flags)

Flags flags = Flags::flag_a;

std::cout << static_cast<bool>(flags & Flags::flag_a) << std::endl; // true
std::cout << static_cast<bool>(flags & Flags::flag_b) << std::endl; // false
  1. use contains member function
BEGIN_BITFLAGS(Flags)
    FLAG(none)
    FLAG(flag_a)
    FLAG(flag_b)
    FLAG(flag_c)
END_BITFLAGS(Flags)

Flags flags_1 = Flags::flag_a;

std::cout << flags_1.contains(Flags::flag_a) << std::endl; // true
std::cout << flags_1.contains(Flags::flag_b) << std::endl; // false

Flags flags_2 = Flags::flag_a | Flags::flag_b;

std::cout << flags_2.contains(Flags::flag_a, Flags::flag_b) << std::endl; // true
std::cout << flags_2.contains(Flags::flag_a, Flags::flag_c) << std::endl; // false

All or Empty

Following member functions are available for setting all the flags or setting no flag:

  1. all / is_all
BEGIN_BITFLAGS(Flags)
    FLAG(none)
    FLAG(flag_a)
    FLAG(flag_b)
END_BITFLAGS(Flags)

Flags flags = Flags::all();

std::cout << flags.is_all() << std::endl;                // true
std::cout << flags.contains(Flags::flag_a) << std::endl; // true
std::cout << flags.contains(Flags::flag_b) << std::endl; // true
std::cout << flags.is_empty() << std::endl;              // false
  1. empty / is_empty
BEGIN_BITFLAGS(Flags)
    FLAG(none)
    FLAG(flag_a)
    FLAG(flag_b)
END_BITFLAGS(Flags)

Flags flags = Flags::empty();

std::cout << flags.is_all() << std::endl;                // false
std::cout << flags.contains(Flags::flag_a) << std::endl; // false
std::cout << flags.contains(Flags::flag_b) << std::endl; // false
std::cout << flags.is_empty() << std::endl;              // true

Set and Remove Specific Flag

Not only that one can set and remove specific flag by using bitwise operators, but there are also special member functions set and remove that have the same purpose.

BEGIN_BITFLAGS(Flags)
    FLAG(none)
    FLAG(flag_a)
    FLAG(flag_b)
END_BITFLAGS(Flags)

Flags flags = Flags::empty();

std::cout << flags.contains(Flags::flag_a) << std::endl; // false
std::cout << flags.contains(Flags::flag_b) << std::endl; // false

flags.set(Flags::flag_a);
flags.set(Flags::flag_b);

std::cout << flags.contains(Flags::flag_a) << std::endl; // true
std::cout << flags.contains(Flags::flag_b) << std::endl; // true

flags.remove(Flags::flag_a);

std::cout << flags.contains(Flags::flag_a) << std::endl; // false
std::cout << flags.contains(Flags::flag_b) << std::endl; // true

Toggle Flags

It is possible to toggle specific flag, i.e. if the flag is not already set, it will be set. On the other hand, if the flag is already set, it will be unset.

BEGIN_BITFLAGS(Flags)
    FLAG(none)
    FLAG(flag_a)
    FLAG(flag_b)
END_BITFLAGS(Flags)

Flags flags = Flags::flag_a;

std::cout << flags.contains(Flags::flag_a) << std::endl; // true
flags.toggle(Flags::flag_a);
std::cout << flags.contains(Flags::flag_a) << std::endl; // false

std::cout << flags.contains(Flags::flag_b) << std::endl; // false
flags.toggle(Flags::flag_b);
std::cout << flags.contains(Flags::flag_b) << std::endl; // true

Clear Flags

In order to clear all the flags currently set, one can use clear member function.

BEGIN_BITFLAGS(Flags)
    FLAG(none)
    FLAG(flag_a)
    FLAG(flag_b)
    FLAG(flag_c)
END_BITFLAGS(Flags)

Flags flags = Flags::flag_a | Flags::flag_b;

std::cout << flags.contains(Flags::flag_a) << std::endl; // true
std::cout << flags.contains(Flags::flag_b) << std::endl; // true
std::cout << flags.contains(Flags::flag_c) << std::endl; // false

flags.clear();

std::cout << flags.contains(Flags::flag_a) << std::endl; // false
std::cout << flags.contains(Flags::flag_b) << std::endl; // false
std::cout << flags.contains(Flags::flag_c) << std::endl; // false

Benchmark

As you can see from the following chart, using raw_flags is as fast as using std::bitset. However, using ordinary flags (i.e. flags with string representation) is a bit slower (as it is expected because of additional feature of having string representation).

If you want to run the benchmark yourself, you can use plot.py script like:

$ python3 plot.py --benchmarks-dir <benchmark-json-dir>

Building Samples and Tests

$ git clone https://github.com/m-peko/bitflags
$ cd bitflags

$ # create the build directory
$ mkdir build
$ cd build

$ # configure the project
$ cmake -DBITFLAGS_BUILD_SAMPLES=ON -DBITFLAGS_BUILD_TESTS=ON ..

$ # compile
$ make

$ # compile tests
$ make tests

$ # run tests
$ make test

Compiler Compatibility

  • Clang/LLVM >= 5
  • MSVC++ >= 14.11 / Visual Studio >= 2017
  • GCC >= 7.3

There are no 3rd party dependencies.

Contributing

Feel free to contribute.

If you find that any of the tests fail, please create a ticket in the issue tracker indicating the following information:

  • platform
  • architecture
  • library version
  • minimal reproducible example

License

The project is available under the MIT license.

Support

If you like the work bitflags library is doing, please consider supporting it:

Buy Me A Coffee

Comments
  • Bitwise math not working

    Bitwise math not working

    The quickstart code is not working for me. It fails with this error:

    /usr/bin/ld: /tmp/ccoU6Pce.o: in function `main':
    bittest.cc:(.text+0x1e): undefined reference to `FlagsImpl<unsigned char>::flag_b'
    /usr/bin/ld: bittest.cc:(.text+0x25): undefined reference to `FlagsImpl<unsigned char>::flag_a'
    /usr/bin/ld: bittest.cc:(.text+0x54): undefined reference to `FlagsImpl<unsigned char>::flag_a'
    /usr/bin/ld: bittest.cc:(.text+0xda): undefined reference to `FlagsImpl<unsigned char>::flag_a'
    collect2: error: ld returned 1 exit status
    

    Simply declaring a flag and setting it equal, i.e:

    Flags flags = Flags::flag_a;
    

    causes no problems; however,

    Flags flags = Flags::flag_a | Flags::flag_b;
    

    causes said issues.

    bug 
    opened by hari387 6
  • trouble with compiling using arm gcc 7

    trouble with compiling using arm gcc 7

    Hi! I really interested in this library, and I want to use it in my project for embedded systems. Unfortunately it doesn't compile using arm-none-eabi-gcc 7.2.1. Here's a link. https://godbolt.org/z/jEv8xY Thanks in advance.

    opened by simvv-exe 4
  • Update CMakeLists.txt

    Update CMakeLists.txt

    • Added missing modules
    • Used correct minor version
    • Renamed _config to -config as find_package only looks for XXConfig or XX-config not XX_config
    • Removed bitflags_targets and bitflags_config.cmake.in. You don't have any dependencies so that's not really doing anything (it also used the old library name).
    opened by SuperWig 2
  • Update API

    Update API

    Renames contains > is_set remove(Flag) > clear(Flag) is_all > all_set

    I think I found a bug in the All code. The All() method I believe is returning all bits, not just defined flags. I updated the test to show this problem. Wasn't sure exactly how to fix it and figured you'd have thoughts.

    I disabled the broken test.

    Also didn't update documentation. DIdn't want to churn until you were good with the API changes.

    opened by EliSchleifer 5
  • Additional methods is_set, unset

    Additional methods is_set, unset

    Would you be open to a PR that aliased the verbs

    unset and is_set. The class is super cool, but contains and remove feel like STL container verbs, and if this is a bitset - I don't need to know the impl underneath. I'd like the verbs to be binary set/unset and is_set.

    Thoughts?

    enhancement 
    opened by EliSchleifer 5
Releases(v1.5.0)
Owner
Marin Peko
craft::cpp maintainer & software engineer with focus on high-perfomance, clean and expressive code
Marin Peko
expected lite - Expected objects in C++11 and later in a single-file header-only library

expected lite: expected objects for C++11 and later expected lite is a single-file header-only library for objects that either represent a valid value

Martin Moene 254 Jan 4, 2023
gsl-lite – A single-file header-only version of ISO C++ Guidelines Support Library (GSL) for C++98, C++11, and later

gsl-lite: Guidelines Support Library for C++98, C++11 up metadata build packages try online gsl-lite is an implementation of the C++ Core Guidelines S

gsl-lite 774 Jan 7, 2023
optional lite - A C++17-like optional, a nullable object for C++98, C++11 and later in a single-file header-only library

optional lite: A single-file header-only version of a C++17-like optional, a nullable object for C++98, C++11 and later Contents Example usage In a nu

Martin Moene 361 Dec 28, 2022
span lite - A C++20-like span for C++98, C++11 and later in a single-file header-only library

span lite: A single-file header-only version of a C++20-like span for C++98, C++11 and later Contents Example usage In a nutshell License Dependencies

Martin Moene 427 Dec 31, 2022
string_view lite - A C++17-like string_view for C++98, C++11 and later in a single-file header-only library

string_view lite: A single-file header-only version of a C++17-like string_view for C++98, C++11 and later Contents Example usage In a nutshell Licens

Martin Moene 357 Dec 28, 2022
gsl-lite – A single-file header-only version of ISO C++ Guidelines Support Library (GSL) for C++98, C++11, and later

gsl-lite: Guidelines Support Library for C++98, C++11 up metadata build packages try online gsl-lite is an implementation of the C++ Core Guidelines S

gsl-lite 772 Dec 31, 2022
A thread-safe, easy-to-use, utility for sending and receiving notifications. It allows you to decouple different modules of your application.

NotificationManager NotificationManager is a thread-safe, easy-to-use utility for sending and receiving notifications. It allows you to decouple diffe

Carlos Aragonés 6 Dec 27, 2021
Liberasure - A no-dependencies C++ extensible type erasure library

erasure A no-dependencies C++ extensible type erasure library Basic Usage LICENSE This work is distributed under the terms of the Apache 2.0 licence,

Gašper Ažman 37 Dec 10, 2022
jkds is a modern header-only C++20 library that complements the standard library.

jkds is a modern header-only C++20 library that complements the standard library. It provides generic atypical data structures, ergonomic functional programming abstractions, and then some.

Alberto Schiabel 6 Nov 16, 2022
Modern C++ generic header-only template library.

nytl A lightweight and generic header-only template library for C++17. Includes various utility of all kind that i needed across multiple projects: Ex

Jan 95 Nov 3, 2022
Bsl - Rust 2018 and C++20, "constexpr everything", AUTOSAR compliant header-only library intended to support the development of critical systems applications

Description The Bareflank Support Library (BSL) is a Rust 2018 and C++20, "constexpr everything", AUTOSAR compliant header-only library intended to su

Bareflank 76 Dec 8, 2022
A collection of std-like single-header C++ libraries

itlib: iboB's Template Libraries A collection of small single-header C++ libraries similar to or extending the C++ standard library. See below for a l

Borislav Stanimirov 98 Dec 29, 2022
A header-only, unobtrusive, almighty alternative to the C++ switch statement that looks just like the original.

uberswitch A header-only, unobtrusive, almighty alternative to the C++ switch statement that looks just like the original. Sample usage (incomplete -

Fabio 85 Jan 3, 2023
Lightweight single-file utilities for C99. Portable & zero dependency

plainlibs Lightweight single-file utilities for C99. Key Features Portable across Unix & Windows (including MSVC) Zero dependencies (besides C stdlib)

null 5 Oct 5, 2022
Library that simplify to find header for class from STL library.

Library that simplify to find header for class from STL library. Instead of searching header for some class you can just include header with the class name.

null 6 Jun 7, 2022
Improved and configurable drop-in replacement to std::function that supports move only types, multiple overloads and more

fu2::function an improved drop-in replacement to std::function Provides improved implementations of std::function: copyable fu2::function move-only fu

Denis Blank 429 Dec 12, 2022
Libft is an individual project at 42 that requires us to re-create some standard C library functions including some additional ones that can be used later to build a library of useful functions for the rest of the program.

?? Index What is Libft? List of Functions Technologies ✨ What is Libft? Libft is an individual project at 42 that requires us to re-create some standa

Paulo Rafael Ramalho 7 Jan 17, 2022
Thrust is a C++ parallel programming library which resembles the C++ Standard Library.

Thrust: Code at the speed of light Thrust is a C++ parallel programming library which resembles the C++ Standard Library. Thrust's high-level interfac

NVIDIA Corporation 4.3k Dec 31, 2022
Bionic BSD-3-ClauseBionic - Google's standard library, developed for Android. BSD-3-Clause

bionic bionic is Android's C library, math library, and dynamic linker. Using bionic as an app developer See the user documentation. Working on bionic

Android Open Source Project 564 Dec 31, 2022