Boost C++ Libraries

...one of the most highly regarded and expertly designed C++ library projects in the world. Herb Sutter and Andrei Alexandrescu, C++ Coding Standards

This is the documentation for a snapshot of the master branch, built from commit cfd508e424.

Overview

Boost.Compat is a repository of C++11 implementations of standard components added in later C++ standards. Its target audience is Boost library authors whose libraries support a lower C++ standard, but wish to utilize a component added in a subsequent one.

The criteria for inclusion in Boost.Compat are as follows:

  • The implementation should be relatively simple and header-only.

  • The component must implement the standard functionality exactly, without deviations or extensions. This allows (but does not require) the implementation to be a simple using declaration in case the standard component is available.

  • The component must not depend on any Boost libraries except Boost.Config, Boost.Assert, or Boost.ThrowException.

  • The component must not be a vocabulary type, visible in the library APIs. The user should never see a boost::compat type; the use of Compat types should be confined to library implementations.

Revision History

Changes in 1.86.0

  • Added bind_front.hpp, bind_back.hpp, invoke.hpp, mem_fn.hpp, integer_sequence.hpp and type_traits.hpp.

  • Added function_ref.hpp.

Changes in 1.83.0

  • Added latch.hpp, an implementation of std::latch (contributed by Christian Mazakas.)

  • Added shared_lock.hpp, a (partial) implementation of std::shared_lock (contributed by Christian Mazakas.)

<boost/compat/bind_back.hpp>

Description

The header <boost/compat/bind_back.hpp> implements the C++20 function std::bind_back.

bind_back is a limited variant of std::bind. It only supports binding the last several parameters of a function object to specific argument values.

Example

struct X
{
    void f(int a, int b) const noexcept;
};

int main()
{
    auto fn = boost::compat::bind_back(&X::f, 1, 2);

    X x;
    fn(x); // calls x.f(1, 2)
}

Synopsis

namespace boost
{
namespace compat
{

template<class F, class... A> auto bind_back(F&& f, A&&... a);

} // namespace compat
} // namespace boost

bind_back

template<class F, class... A> auto bind_back( F&& f, A&&... a );
Returns:

A function object fn such that fn(b…​) is equivalent to invoke(f, b…​, a…​).

<boost/compat/bind_front.hpp>

Description

The header <boost/compat/bind_front.hpp> implements the C++20 function std::bind_front.

bind_front is a limited variant of std::bind. It only supports binding the first several parameters of a function object to specific argument values.

Example

struct X
{
    void f(int a, int b) const noexcept;
};

int main()
{
    X x;
    auto fn = boost::compat::bind_front(&X::f, &x);

    fn(1, 2); // calls x.f(1, 2)
}

Synopsis

namespace boost
{
namespace compat
{

template<class F, class... A> auto bind_front(F&& f, A&&... a);

} // namespace compat
} // namespace boost

bind_front

template<class F, class... A> auto bind_front(F&& f, A&&... a);
Returns:

A function object fn such that fn(b…​) is equivalent to invoke(f, a…​, b…​).

<boost/compat/function_ref.hpp>

Description

The header <boost/compat/function_ref.hpp> implements the C++26 class std::function_ref.

function_ref is a lightweight polymorphic function wrapper that only stores a pointer to the supplied Callable object and a pointer to an unspecified function, meaning it does not participate in ownership of the Callable and does not allocate. All specializations of function_ref satisfy TriviallyCopyable.

function_ref supports every combination of const and noexcept and is useful for writing higher-order functions as it can avoid a template parameter or an allocation (as std::function is known for).

Example

int add(int x, int y) noexcept { return x * 10 + y; }

auto add2 = [](int x, int y) { return x * 100 + y; };

std::vector<boost::compat::function_ref<int(int, int)>> fns;
fns.push_back({add});
fns.push_back({add2});

for (auto fn : fns) {
  std::cout << fn(1, 2) << std::endl;
}

Synopsis

namespace boost
{
namespace compat
{

template <class... S>
struct function_ref;

// cv is either `const` or empty
// noex is either `true` or `false`
template<class R, class... ArgTypes>
class function_ref<R(ArgTypes...) cv noexcept(noex)> {
public:
  template<class F> function_ref(F*) noexcept;
  template<class F> function_ref(F&&) noexcept;

  function_ref(const function_ref&) noexcept = default;
  function_ref& operator=(const function_ref&) noexcept = default;
  template<class T> function_ref& operator=(T) = delete;

  R operator()(ArgTypes...) const noexcept(noex);
};

} // namespace compat
} // namespace boost

Constructors

Function Pointer Constructor

template<class F> function_ref(F* fn) noexcept;
Preconditions

fn != nullptr.

Effects

Constructs a function_ref which uses the supplied function pointer as its Callable.

Calling the function_ref is expression-equivalent to invoke_r<R>(f, call-args…​).

Object Constructor

template<class F> function_ref(F&& fn) noexcept;
Effects:

Constructs a function_ref that stores the address of the supplied Callable object fn. This overload only participates in resolution when fn is not a pointer-to-member or pointer-to-member-function.

Calling the function_ref is expression-equivalent to: + invoke_r<R>(static_cast<cv T&>(f), call-args…​).

Copy Constructor

function_ref(const function_ref&) noexcept = default;
Effects:

function_ref is a TriviallyCopyable type.

Member Functions

call operator

R operator()(ArgTypes...) const noexcept(noex);
Effects:

Invokes the underlying Callable object by forwarding the supplied arguments.

Assignment

Copy Assignment

function_ref& operator=(const function_ref&) noexcept = default;
template<class T> function_ref& operator=(T) = delete;
Effects:

operator=(T) participates in overload resolution if:

  • T is not the same as function_ref

  • is_pointer_v<T> is false.

<boost/compat/integer_sequence.hpp>

Description

The header <boost/compat/integer_sequence.hpp> implements the C++14 utilities std::integer_sequence, std::index_sequence, std::make_integer_sequence, std::make_index_sequence, and std::index_sequence_for.

Synopsis

namespace boost
{
namespace compat
{

template<class T, T... I> struct integer_sequence;

template<class T, T N> using make_integer_sequence = /*...*/;

template<std::size_t... I> using index_sequence = /*...*/;

template<std::size_t N> using make_index_sequence = /*...*/;

template<class... T> using index_sequence_for = /*...*/;

} // namespace compat
} // namespace boost

integer_sequence

template<class T, T... I> struct integer_sequence {};

make_integer_sequence

template<class T, T N> using make_integer_sequence = /*...*/;

make_integer_sequence<T, N> is an alias for integer_sequence<T, 0, 1, 2, …​, N-1>.

index_sequence

template<std::size_t... I> using index_sequence = integer_sequence<std::size_t, I...>;

make_index_sequence

template<std::size_t N> using make_index_sequence = make_integer_sequence<std::size_t, N>;

index_sequence_for

template<class... T> using index_sequence_for = make_integer_sequence<std::size_t, sizeof...(T)>;

<boost/compat/invoke.hpp>

Description

The header <boost/compat/invoke.hpp> implements the C++17 function std::invoke, the C++23 function std::invoke_r, and the associated utilities invoke_result_t, is_invocable, is_invocable_r, is_nothrow_invocable, and is_nothrow_invocable_r.

invoke(f, a…​) generally returns f(a…​), but when f is a pointer to member, it invokes it as if by returning mem_fn(f)(a…​). This allows functions, function objects, and pointers to members to be treated uniformly by components such as bind_front.

invoke_r<R>(f, a…​) returns invoke(f, a…​), converted to R.

Example

struct X
{
    void f(int a, int b) const noexcept;
};

int main()
{
    X x;
    boost::compat::invoke(&X::f, x, 1, 2); // calls x.f(1, 2)
}

Synopsis

namespace boost
{
namespace compat
{

template<class F, class... A> auto invoke(F&& f, A&&... a);

template<class F, class... A> using invoke_result_t = /*...*/;

template<class F, class... A> struct is_invocable;

template<class F, class... A> struct is_nothrow_invocable;

template<class R, class F, class... A> R invoke_r(F&& f, A&&... a);

template<class R, class F, class... A> struct is_invocable_r;

template<class R, class F, class... A> struct is_nothrow_invocable_r;

} // namespace compat
} // namespace boost

invoke

template<class F, class... A> auto invoke(F&& f, A&&... a) noexcept(/*...*/) -> /*...*/;
Returns:
  • std::forward<F>(f)(std::forward<A>(a)…​), when f is not a pointer to member;

  • mem_fn(f)(std::forward<A>(a)…​), otherwise.

Constraints:

the return expression must be valid.

Remarks:

The return type is decltype(r), and the noexcept clause is noexcept(noexcept(r)), where r is the return expression.

invoke_result_t

template<class F, class... A> using invoke_result_t =
  decltype( invoke(std::declval<F>(), std::declval<A>()...) );

is_invocable

template<class F, class... A> struct is_invocable: public /*...*/;

The base class of is_invocable<F, A…​> is std::true_type when invoke(std::declval<F>(), std::declval<A>()…​) is a valid expression, std::false_type otherwise.

is_nothrow_invocable

template<class F, class... A> struct is_nothrow_invocable: public /*...*/;

The base class of is_nothrow_invocable<F, A…​> is std::false_type when is_invocable<F, A…​>::value is false, std::integral_constant<bool, noexcept(invoke(std::declval<F>(), std::declval<A>()…​))> otherwise.

invoke_r

template<class R, class F, class... A> R invoke_r(F&& f, A&&... a) noexcept(/*...*/);
Returns:
  • static_cast<R>(invoke(std::forward<F>(f), std::forward<A>(a)…​)), when R is (possibly cv-qualified) void;

  • invoke(std::forward<F>(f), std::forward<A>(a)…​), implicitly converted to R, otherwise.

Constraints:

is_invocable<F, A…​>::value must be true and, if R is not cv void, std::is_convertible<invoke_result_t<F, A…​>, R>::value must be true.

Remarks:

The noexcept clause is noexcept(noexcept(static_cast<R>(invoke(std::forward<F>(f), std::forward<A>(a)…​)))).

is_invocable_r

template<class R, class F, class... A> struct is_invocable: public /*...*/;

The base class of is_invocable_r<R, F, A…​> is std::true_type when invoke_r<R>(std::declval<F>(), std::declval<A>()…​) is a valid expression, std::false_type otherwise.

is_nothrow_invocable_r

template<class R, class F, class... A> struct is_nothrow_invocable: public /*...*/;

The base class of is_nothrow_invocable<R, F, A…​> is std::false_type when is_invocable_r<R, F, A…​>::value is false, std::integral_constant<bool, noexcept(invoke_r<R>(std::declval<F>(), std::declval<A>()…​))> otherwise.

<boost/compat/latch.hpp>

Description

The header <boost/compat/latch.hpp> implements, in a portable way, the C++20 <latch> header.

latch is a single-use barrier which counts downwards, enabling synchronization between a set of threads. The counter can be manually decremented by any value, but decrementing below zero causes undefined behavior. The maximum number of waiters is specified to be boost::compat::latch::max().

Example

std::ptrdiff_t const num_threads = 16;
boost::compat::latch l(num_threads);

std::vector<std::thread> threads;
for (int i = 0; i < num_threads; ++i) {
  threads.emplace_back([&l] {
    // do some preliminary work here...

    // block until all threads have reached this statement
    l.arrive_and_wait();

    // continue with further work...
  });
}

for (auto& t: threads) { t.join(); }

Synopsis

namespace boost
{
namespace compat
{

class latch {
  explicit latch(std::ptrdiff_t expected);

  latch(latch const &) = delete;
  latch &operator=(latch const &) = delete;

  ~latch() = default;

  void count_down(std::ptrdiff_t n = 1);
  bool try_wait() const noexcept;
  void wait() const;
  void arrive_and_wait(std::ptrdiff_t n = 1);

  static constexpr std::ptrdiff_t max() noexcept;
};

} // namespace compat
} // namespace boost

Constructors

Counter Constructor

explicit latch(std::ptrdiff_t expected);
Preconditions:

expected >= 0 && expected <= boost::compat::latch::max().

Effects:

Constructs a latch with an internal counter value of expected.

Copy Constructor

latch(latch const &) = delete;

latch is not copyable or movable.

Member Functions

count_down

void count_down(std::ptrdiff_t n = 1);
Preconditions:

n must not be larger than the current internal counter’s value.

Effects:

Decrements the internal counter by n.

try_wait

bool try_wait() const noexcept;

Returns a boolean indicating whether or not the latch’s internal counter has reached 0 (true) or not (false).

wait

void wait() const;

Blocks the current thread until the internal counter has reached 0.

arrive_and_wait

void arrive_and_wait(std::ptrdiff_t n = 1);
Preconditions:

n must not be larger than the current internal counter’s value.

Effects:

Decrements the internal counter by n and if the counter non-zero, blocks the current thread.

max

static constexpr std::ptrdiff_t max() noexcept;

Returns an implementation-defined number representing the maximum amount of waiters. Currently PTRDIFF_MAX.

<boost/compat/mem_fn.hpp>

Description

The header <boost/compat/mem_fn.hpp> implements the C++11 function std::mem_fn.

mem_fn(pm), where pm is a pointer to member, returns a function object that can be used to invoke the member function, or obtain a reference to the data member, using a function call syntax.

Even though std::mem_fn is C++11, later standards place stricter requirements on the returned function object (it needs to be SFINAE friendly and propagate noexcept correctly.) boost::compat::mem_fn implements these stricter requirements.

Example

struct X
{
    void f(int a, int b) const noexcept;
};

int main()
{
    auto fn = boost::compat::mem_fn(&X::f);

    X x;
    fn(x, 1, 2); // calls x.f(1, 2)
}

Synopsis

namespace boost
{
namespace compat
{

template<class M, class T> auto mem_fn(M T::* pm) noexcept;

} // namespace compat
} // namespace boost

mem_fn

template<class M, class T> auto mem_fn(M T::* pm) noexcept;
Returns:

A function object fn such that:

  • fn(x, a…​) is equivalent to (x.*pm)(a…​), when M is a function type and the type of X is T or derived from T;

  • fn(x, a…​) is equivalent to ((*x).*pm)(a…​), when M is a function type and the type of X is not T or derived from T;

  • fn(x) is equivalent to x.*pm, when M is an object type and the type of X is T or derived from T;

  • fn(x) is equivalent to (*x).*pm, when M is an object type and the type of X is not T or derived from T.

<boost/compat/shared_lock.hpp>

Description

The header <boost/compat/shared_lock.hpp> implements, in a portable way, the C++14 std::shared_lock class template.

The class shared_lock is a RAII wrapper that manages locking and unlocking the provided Mutex, provided that it implements SharedLockable.

This is the shared analog of unique_lock and calls lock_shared() instead of lock().

Example

#include <boost/compat/shared_lock.hpp>

shared_mutex m;

// acquire the lock by calling `m.lock_shared()`
// `m.unlock_shared()` is called automatically for us by `guard` now
boost::compat::shared_lock<shared_mutex> guard(m);
assert(guard.owns_lock());

Synopsis

namespace boost {
namespace compat {

template <class Mutex>
class shared_lock;

template <class Mutex>
void swap( shared_lock<Mutex>& x, shared_lock<Mutex>& y ) noexcept;

template <class Mutex>
class shared_lock {
  using mutex_type = Mutex;

  shared_lock() noexcept = default;
  explicit shared_lock( mutex_type& m );
  shared_lock( mutex_type& m, std::defer_lock_t ) noexcept;
  shared_lock( mutex_type& m, std::try_lock_t );
  shared_lock( mutex_type& m, std::adopt_lock_t );

  ~shared_lock();

  shared_lock( const shared_lock& ) = delete;
  shared_lock& operator=( const shared_lock& ) = delete;

  shared_lock( shared_lock&& u ) noexcept;
  shared_lock& operator=( shared_lock&& u ) noexcept;

  void lock();
  bool try_lock();
  void unlock();

  void swap( shared_lock& u ) noexcept;
  mutex_type* release() noexcept;

  mutex_type* mutex() const noexcept;

  bool owns_lock() const noexcept;
  explicit operator bool() const noexcept;
};

} // namespace compat
} // namespace boost

Constructors

Default Constructor

shared_lock() noexcept = default;
Postconditions:

mutex() == nullptr and owns_lock() == false.

Locking Constructor

explicit shared_lock( mutex_type& m );
Effects:

Calls m.lock_shared().

Postconditions:

mutex() == std::addressof(m) and owns_lock() == true.

Deferred Constructor

shared_lock( mutex_type& m, std::defer_lock_t ) noexcept;
Postconditions:

mutex() == std::addressof(m) and owns_lock() == false.

Try-to-Lock Constructor

shared_lock( mutex_type& m, std::try_lock_t );
Effects:

Calls m.try_lock_shared().

Postconditions:

mutex() == std::addressof(m) and owns_lock() == res where res is the result of the m.try_lock_shared() call.

Adopting Constructor

shared_lock( mutex_type& m, std::adopt_lock_t );
Preconditions:

m must be held by a previous call to m.lock_shared() or a successful call to m.try_lock_shared().

Postconditions:

mutex() == std::addressof(m) and owns_lock() == true.

Copy Constructor

shared_lock( const shared_lock& ) = delete;

shared_lock is not copyable.

Move Constructor

shared_lock( shared_lock&& u ) noexcept;
Postconditions:

mutex() == s.mutex() and owns_lock() == s.owns_lock() where s is the state of u before move. u.mutex() == nullptr and u.owns_lock() == false after move.

Assignment

Copy Assignment

shared_lock& operator=( const shared_lock& ) = delete;

shared_lock is not copyable.

Move Assignment

shared_lock& operator=( shared_lock&& u ) noexcept;
Effects:

If owns_lock() == true, calls unlock().

Postconditions:

mutex() == s.mutex() and owns_lock() == s.owns_lock() where s is the state of u before move. u.mutex() == nullptr and u.owns_lock() == false after move.

Destructor

~shared_lock();
Effects:

If owns_lock() == true, calls unlock().

Member Functions

Locking

lock
void lock();
Effects:

Calls mutex()->lock_shared().

Postconditions:

owns_lock() == true.

Throws:

Any exception caused by mutex()->lock_shared(). std::system_error when mutex() == nullptr (with std::errc::operation_not_permitted) or owns_lock() == true (with std::errc::resource_deadlock_would_occur).

try_lock
bool try_lock();
Effects:

Calls mutex()->try_lock_shared().

Postconditions:

owns_lock() == res where res = mutex()->try_lock_shared().

Throws:

Any exception caused by mutex()->try_lock_shared(). std::system_error when mutex() == nullptr (with std::errc::operation_not_permitted) or owns_lock() == true (with std::errc::resource_deadlock_would_occur).

unlock
void unlock();
Effects:

Calls mutex()->unlock_shared().

Postconditions:

owns_lock() == false.

Throws:

std::system_error (with std::errc::operation_not_permitted) if owns_lock() == false.

Modifiers

swap
void swap( shared_lock& u ) noexcept;
Effects:

Swaps the data members of *this and u.

release
mutex_type* release() noexcept;
Postconditions:

mutex() == nullptr and owns_lock() == false.

Returns:

The previous value of mutex().

Free Function swap
template <class Mutex>
void swap( shared_lock<Mutex>& x, shared_lock<Mutex>& y ) noexcept;
Effects:

Swaps the data members of x and y via x.swap(y).

Observers

mutex
mutex_type* mutex() const noexcept;
Returns:

The value of the internal pointer, either nullptr or the address of the mutex.

owns_lock
bool owns_lock() const noexcept;
Returns:

A boolean indicating whether or not the mutex is locked by the current shared_lock instance.

boolean conversion
explicit operator bool() const noexcept;
Returns:

A boolean indicating whether or not the mutex is locked by the current shared_lock instance.

<boost/compat/type_traits.hpp>

Description

The header <boost/compat/type_traits.hpp> implements some of the post-C++11 additions to the standard header <type_traits>.

Synopsis

#include <type_traits>

namespace boost
{
namespace compat
{

template<class T> using remove_const_t = typename std::remove_const<T>::type;

template<class T> using remove_cv_t = typename std::remove_cv<T>::type;

template<class T> using remove_reference_t = typename std::remove_reference<T>::type;

template<class T> using remove_cvref_t = remove_cv_t<remove_reference_t<T>>;

template<class T> using decay_t = typename std::decay<T>::type;

template<bool B, class T = void> using enable_if_t = typename std::enable_if<B, T>::type;

template<bool B, class T, class F> using conditional_t = typename std::conditional<B, T, F>::type;

template<class... T> using void_t = void;


} // namespace compat
} // namespace boost

This documentation is copyright 2023, 2024 Peter Dimov and contributors and is distributed under the Boost Software License, Version 1.0.