The pw_function module provides a standard, general-purpose API for wrapping callable objects. pw_function is similar in spirit and API to std::function, but doesn’t allocate, and uses several tricks to prevent code bloat.


Basic usage#

pw_function defines the pw::Function class. A Function is a move-only callable wrapper constructable from any callable object. Functions are templated on the signature of the callable they store.

Functions implement the call operator — invoking the object will forward to the stored callable.

int Add(int a, int b) { return a + b; }

// Construct a Function object from a function pointer.
pw::Function<int(int, int)> add_function(Add);

// Invoke the function object.
int result = add_function(3, 5);
EXPECT_EQ(result, 8);

// Construct a function from a lambda.
pw::Function<int(int)> negate([](int value) { return -value; });
EXPECT_EQ(negate(27), -27);

Functions are nullable. Invoking a null function triggers a runtime assert.

// A function initialized without a callable is implicitly null.
pw::Function<void()> null_function;

// Null functions may also be explicitly created or set.
pw::Function<void()> explicit_null_function(nullptr);

pw::Function<void()> function([]() {});  // Valid (non-null) function.
function = nullptr;  // Set to null, clearing the stored callable.

// Functions are comparable to nullptr.
if (function != nullptr) {

pw::Function’s default constructor is constexpr, so default-constructed functions may be used in classes with constexpr constructors and in constinit expressions.

class MyClass {
  // Default construction of a pw::Function is constexpr.
  constexpr MyClass() { ... }

  pw::Function<void(int)> my_function;

// pw::Function and classes that use it may be constant initialized.
constinit MyClass instance;


By default, a Function stores its callable inline within the object. The inline storage size defaults to the size of one pointer, but is configurable through the build system.

The pw::InlineFunction alias is similar to pw::Function, but is always inlined. That is, even if dynamic allocation is enabled for pw::Function, pw::InlineFunction will fail to compile if the callable is larger than the inline storage size.

Attempting to construct a function from a callable larger than its inline size is a compile-time error unless dynamic allocation is enabled.

Inline storage size

The default inline size of one pointer is sufficient to store most common callable objects, including function pointers, simple non-capturing and capturing lambdas, and lightweight custom classes.

// The lambda is moved into the function's internal storage.
pw::Function<int(int, int)> subtract([](int a, int b) { return a - b; });

// Functions can be also be constructed from custom classes that implement
// operator(). This particular object is large (8 ints of space).
class MyCallable {
  int operator()(int value);

  int data_[8];

// Compiler error: sizeof(MyCallable) exceeds function's inline storage size.
pw::Function<int(int)> function((MyCallable()));

Dynamic allocation

When PW_FUNCTION_ENABLE_DYNAMIC_ALLOCATION is enabled, a Function will use dynamic allocation to store callables that exceed the inline size. An Allocator type can be optionally supplied as a template argument. The default Allocator type can also be changed by overriding PW_FUNCTION_DEFAULT_ALLOCATOR_TYPE (the value_type of the Allocator is irrelevant, since it must support rebinding). When dynamic allocation is enabled but a compile-time check for the inlining is still required pw::InlineFunction can be used.


If PW_FUNCTION_ENABLE_DYNAMIC_ALLOCATION is enabled then attempts to cast from :cpp:type:`pw::InlineFunction to a regular pw::Function will ALWAYS allocate memory.

API usage#


template<typename FunctionType, std::size_t inline_target_size = function_internal::config::kInlineCallableSize, typename Allocator = PW_FUNCTION_DEFAULT_ALLOCATOR_TYPE>
using pw::Function = fit::function_impl<inline_target_size, !function_internal::config::kEnableDynamicAllocation, FunctionType, Allocator>#

pw::Function is a wrapper for an arbitrary callable object. It can be used by callback-based APIs to allow callers to provide any type of callable.


template <typename T>
bool All(const pw::Vector<T>& items,
         const pw::Function<bool(const T& item)>& predicate) {
  for (const T& item : items) {
    if (!predicate(item)) {
      return false;
  return true;

bool ElementsArePositive(const pw::Vector<int>& items) {
  return All(items, [](const int& i) { return i > 0; });

bool IsEven(const int& i) { return i % 2 == 0; }

bool ElementsAreEven(const pw::Vector<int>& items) {
  return All(items, IsEven);

Template Parameters:

Allocator – The Allocator used to dynamically allocate the callable, if it exceeds inline_target_size and dynamic allocation is enabled. Its value_type is irrelevant, since it must support rebinding.

template<typename FunctionType, std::size_t inline_target_size = function_internal::config::kInlineCallableSize>
using pw::InlineFunction = fit::inline_function<FunctionType, inline_target_size>#

Version of pw::Function that exclusively uses inline storage.

IMPORTANT: If pw::Function is configured to allow dynamic allocations then any attempt to convert pw::InlineFunction to pw::Function will ALWAYS allocate.

template<typename FunctionType, std::size_t inline_target_size = function_internal::config::kInlineCallableSize, typename Allocator = PW_FUNCTION_DEFAULT_ALLOCATOR_TYPE>
using pw::Callback = fit::callback_impl<inline_target_size, !function_internal::config::kEnableDynamicAllocation, FunctionType, Allocator>#

pw::Callback is identical to pw::Function except:

  1. On the first call to invoke a pw::Callback, the target function held by the pw::Callback cannot be called again.

  2. When a pw::Callback is invoked for the first time, the target function is released and destructed, along with any resources owned by that function (typically the objects captured by a lambda).

A pw::Callback in the “already called” state has the same state as a pw::Callback that has been assigned to nullptr.

template<typename FunctionType, std::size_t inline_target_size = function_internal::config::kInlineCallableSize>
using pw::InlineCallback = fit::inline_callback<FunctionType, inline_target_size>#

Version of pw::Callback that exclusively uses inline storage.

template<auto method, typename T>
auto pw::bind_member(T *instance)#

Returns a Callable which, when called, invokes method on instance using the arguments provided.

This is useful for binding the this argument of a callable.

pw::bind_member<&T::MethodName>(instance) is roughly equivalent to [instance](Arg arg1, ...) { instance->MethodName(arg1, ...) }, albeit with proper support for overloads and argument forwarding.

pw::Function as a function parameter#

When implementing an API which takes a callback, a Function can be used in place of a function pointer or equivalent callable.

// Before:
void DoTheThing(int arg, void (*callback)(int result));

// After. Note that it is possible to have parameter names within the function
// signature template for clarity.
void DoTheThing(int arg, const pw::Function<void(int result)>& callback);

pw::Function is movable, but not copyable, so APIs must accept pw::Function objects either by const reference (const pw::Function<void()>&) or rvalue reference (const pw::Function<void()>&&). If the pw::Function simply needs to be called, it should be passed by const reference. If the pw::Function needs to be stored, it should be passed as an rvalue reference and moved into a pw::Function variable as appropriate.

// This function calls a pw::Function but doesn't store it, so it takes a
// const reference.
void CallTheCallback(const pw::Function<void(int)>& callback) {

// This function move-assigns a pw::Function to another variable, so it takes
// an rvalue reference.
void StoreTheCallback(pw::Function<void(int)>&& callback) {
  stored_callback_ = std::move(callback);

Rules of thumb for passing a pw::Function to a function

  • Pass by value: Never. This results in unnecessary pw::Function instances and move operations.

  • Pass by const reference (const pw::Function&): When the pw::Function is only invoked.

    When a pw::Function is called or inspected, but not moved, take a const reference to avoid copies and support temporaries.

  • Pass by rvalue reference (pw::Function&&): When the pw::Function is moved.

    When the function takes ownership of the pw::Function object, always use an rvalue reference (pw::Function<void()>&&) instead of a mutable lvalue reference (pw::Function<void()>&). An rvalue reference forces the caller to std::move when passing a preexisting pw::Function variable, which makes the transfer of ownership explicit. It is possible to move-assign from an lvalue reference, but this fails to make it obvious to the caller that the object is no longer valid.

  • Pass by non-const reference (pw::Function&): Rarely, when modifying a variable.

    Non-const references are only necessary when modifying an existing pw::Function variable. Use an rvalue reference instead if the pw::Function is moved into another variable.

Calling functions that use pw::Function#

A pw::Function can be implicitly constructed from any callback object. When calling an API that takes a pw::Function, simply pass the callable object. There is no need to create an intermediate pw::Function object.

// Implicitly creates a pw::Function from a capturing lambda and calls it.
CallTheCallback([this](int result) { result_ = result; });

// Implicitly creates a pw::Function from a capturing lambda and stores it.
StoreTheCallback([this](int result) { result_ = result; });

When working with an existing pw::Function variable, the variable can be passed directly to functions that take a const reference. If the function takes ownership of the pw::Function, move the pw::Function variable at the call site.

// Accepts the pw::Function by const reference.

// Takes ownership of the pw::Function.
void StoreTheCallback(std::move(my_function));

pw::Callback for one-shot functions#

pw::Callback is a specialization of pw::Function that can only be called once. After a pw::Callback is called, the target function is destroyed. A pw::Callback in the “already called” state has the same state as a pw::Callback that has been assigned to nullptr.

Invoking pw::Function from a C-style API#

One use case for invoking pw_function from a C-style API is to automate the generation of trampoline layers.

Traditional callback APIs often use a function pointer and void* context argument. The context argument makes it possible to use the callback function with non-global data. For example, the qsort_s and bsearch_s functions take a pointer to a comparison function that has void* context as its last parameter. pw::Function does not naturally work with these kinds of APIs.

The functions below make it simple to adapt a pw::Function for use with APIs that accept a function pointer and void* context argument.

template<typename FunctionType>
constexpr auto pw::function::GetFunctionPointer()#

Returns a function pointer that invokes a pw::Function, lambda, or other callable object from a void* context argument. This makes it possible to use C++ callables with C-style APIs that take a function pointer and void* context.

The returned function pointer has the same return type and arguments as the pw::Function or pw::Callback, except that the last parameter is a void*. GetFunctionPointerContextFirst places the void* context parameter first.

The following example adapts a C++ lambda function for use with C-style API that takes an int (*)(int, void*) function and a void* context.

void TakesAFunctionPointer(int (*function)(int, void*), void* context);

void UseFunctionPointerApiWithPwFunction() {
  // Declare a callable object so a void* pointer can be obtained for it.
  auto my_function = [captures](int value) {
     // ...
     return value + captures;

  // Invoke the API with the function pointer and callable pointer.

The function returned from this must ONLY be used with the exact type for which it was created! Function pointer / context APIs are not type safe.

template<typename FunctionType>
constexpr auto pw::function::GetFunctionPointer(const FunctionType&)#

GetFunctionPointer overload that uses the type of the function passed to this call.

template<typename FunctionType>
constexpr auto pw::function::GetFunctionPointerContextFirst()#

Same as GetFunctionPointer, but the context argument is passed first. Returns a void(void*, int) function for a pw::Function<void(int)>.

template<typename FunctionType>
constexpr auto pw::function::GetFunctionPointerContextFirst(const FunctionType&)#

GetFunctionPointerContextFirst overload that uses the type of the function passed to this call.


template<typename Functor>
class ScopeGuard#

ScopeGuard ensures that the specified functor is executed no matter how the current scope exits, unless it is dismissed.


pw::Status SomeFunction() {
  ScopeGuard undo_operation_one(UndoOperationOne);
  ScopeGuard undo_operation_two(UndoOperationTwo);
  return pw::OkStatus();

Public Functions

inline void Dismiss()#

Dismisses the ScopeGuard, meaning it will no longer execute the Functor when it goes out of scope.

Size reports#

Function class#

The following size report compares an API using a pw::Function to a traditional function pointer.




Simple pw::Function vs. function pointer



Callable sizes#

The table below demonstrates typical sizes of various callable types, which can be used as a reference when sizing external buffers for Function objects.




Function pointer



Static lambda (operator+)



Non-capturing lambda



Simple capturing lambda















Multi-argument capturing lambda















Custom class




pw::Function is an alias of fit::function_impl.

pw::Callback is an alias of fit::callback_impl.

Why pw::Function is not a literal#

The default constructor for pw::Function is constexpr but pw::Function is not a literal type. Instances can be declared constinit but can’t be used in constexpr contexts. There are a few reasons for this:

  • pw::Function supports wrapping any callable type, and the wrapped type might not be a literal type.

  • pw::Function stores inline callables in a bytes array, which is not constexpr-friendly.

  • pw::Function optionally uses dynamic allocation, which doesn’t work in constexpr contexts (at least before C++20).


To enable pw_function` for Zephyr add ``CONFIG_PIGWEED_FUNCTION=y to the project’s configuration.