2021-05-12 02:58:01 +00:00
|
|
|
.. _module-pw_function:
|
|
|
|
|
|
|
|
-----------
|
|
|
|
pw_function
|
|
|
|
-----------
|
|
|
|
The function module provides a standard, general-purpose API for wrapping
|
|
|
|
callable objects.
|
|
|
|
|
|
|
|
.. note::
|
|
|
|
This module is under construction and its API is not complete.
|
|
|
|
|
|
|
|
Overview
|
|
|
|
========
|
|
|
|
|
|
|
|
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.
|
|
|
|
|
|
|
|
.. code-block:: c++
|
|
|
|
|
|
|
|
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.
|
|
|
|
|
|
|
|
.. code-block:: c++
|
|
|
|
|
|
|
|
// A function intialized 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) {
|
|
|
|
function();
|
|
|
|
}
|
|
|
|
|
2021-06-14 20:53:23 +00:00
|
|
|
``pw::Function``'s default constructor is ``constexpr``, so default-constructed
|
|
|
|
functions may be used in classes with ``constexpr`` constructors and in
|
|
|
|
``constinit`` expressions.
|
|
|
|
|
|
|
|
.. code-block:: c++
|
|
|
|
|
|
|
|
class MyClass {
|
|
|
|
public:
|
|
|
|
// 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;
|
|
|
|
|
2021-05-12 02:58:01 +00:00
|
|
|
Storage
|
|
|
|
-------
|
|
|
|
By default, a ``Function`` stores its callable inline within the object. The
|
|
|
|
inline storage size defaults to the size of two pointers, but is configurable
|
|
|
|
through the build system. The size of a ``Function`` object is equivalent to its
|
|
|
|
inline storage size.
|
|
|
|
|
|
|
|
Attempting to construct a function from a callable larger than its inline size
|
|
|
|
is a compile-time error.
|
|
|
|
|
|
|
|
.. admonition:: Inline storage size
|
|
|
|
|
|
|
|
The default inline size of two pointers is sufficient to store most common
|
|
|
|
callable objects, including function pointers, simple non-capturing and
|
|
|
|
capturing lambdas, and lightweight custom classes.
|
|
|
|
|
|
|
|
.. code-block:: c++
|
|
|
|
|
|
|
|
// 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 {
|
|
|
|
public:
|
|
|
|
int operator()(int value);
|
|
|
|
|
|
|
|
private:
|
|
|
|
int data_[8];
|
|
|
|
};
|
|
|
|
|
|
|
|
// Compiler error: sizeof(MyCallable) exceeds function's inline storage size.
|
|
|
|
pw::Function<int(int)> function((MyCallable()));
|
|
|
|
|
|
|
|
..
|
|
|
|
For larger callables, a ``Function`` can be constructed with an external buffer
|
|
|
|
in which the callable should be stored. The user must ensure that the lifetime
|
|
|
|
of the buffer exceeds that of the function object.
|
|
|
|
|
|
|
|
.. code-block:: c++
|
|
|
|
|
|
|
|
// Initialize a function with an external 16-byte buffer in which to store its
|
|
|
|
// callable. The callable will be stored in the buffer regardless of whether
|
|
|
|
// it fits inline.
|
|
|
|
pw::FunctionStorage<16> storage;
|
|
|
|
pw::Function<int()> get_random_number([]() { return 4; }, storage);
|
|
|
|
|
|
|
|
.. admonition:: External storage
|
|
|
|
|
|
|
|
Functions which use external storage still take up the configured inline
|
|
|
|
storage size, which should be accounted for when storing function objects.
|
|
|
|
|
|
|
|
In the future, ``pw::Function`` may support dynamic allocation of callable
|
|
|
|
storage using the system allocator. This operation will always be explicit.
|
|
|
|
|
|
|
|
API usage
|
|
|
|
=========
|
|
|
|
|
|
|
|
Implementation-side
|
|
|
|
-------------------
|
|
|
|
When implementing an API which takes a callback, a ``Function`` can be used in
|
|
|
|
place of a function pointer or equivalent callable.
|
|
|
|
|
|
|
|
.. code-block:: c++
|
|
|
|
|
|
|
|
// 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, pw::Function<void(int result)> callback);
|
|
|
|
|
|
|
|
An API can accept a function either by value or by reference. If taken by value,
|
|
|
|
the implementation is responsible for managing the function by moving it into an
|
|
|
|
appropriate location.
|
|
|
|
|
|
|
|
.. admonition:: Value or reference?
|
|
|
|
|
|
|
|
It is preferable for APIs to take functions by value rather than by reference.
|
|
|
|
This provides callers of the API with a more convenient interface, as well as
|
|
|
|
making their lives easier by not requiring management of resources or
|
|
|
|
lifetimes.
|
|
|
|
|
|
|
|
Caller-side
|
|
|
|
-----------
|
|
|
|
When calling an API which takes a function by reference, the standard pattern is
|
|
|
|
to implicitly construct the function in place from a callable object. Simply
|
|
|
|
pass the desired callable directly to the API.
|
|
|
|
|
|
|
|
.. code-block:: c++
|
|
|
|
|
|
|
|
// Implicitly initialize a Function from a capturing lambda.
|
|
|
|
DoTheThing(42, [this](int result) { result_ = result; });
|
|
|
|
|
|
|
|
Size reports
|
|
|
|
============
|
|
|
|
|
|
|
|
Function class
|
|
|
|
--------------
|
|
|
|
The following size report compares an API using a ``pw::Function`` to a
|
|
|
|
traditional function pointer.
|
|
|
|
|
|
|
|
.. include:: function_size
|
|
|
|
|
|
|
|
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.
|
|
|
|
|
|
|
|
.. include:: callable_size
|
|
|
|
|
|
|
|
Design
|
|
|
|
======
|
|
|
|
``pw::Function`` is based largely on
|
|
|
|
`fbl::Function <https://cs.opensource.google/fuchsia/fuchsia/+/main:zircon/system/ulib/fbl/include/fbl/function.h>`_
|
|
|
|
from Fuchsia with some changes to make it more suitable for embedded
|
|
|
|
development.
|
|
|
|
|
2021-06-10 00:03:52 +00:00
|
|
|
Functions are movable, but not copyable. This allows them to store and manage
|
2021-05-12 02:58:01 +00:00
|
|
|
callables without having to perform bookkeeping such as reference counting, and
|
|
|
|
avoids any reliance on dynamic memory management. The result is a simpler
|
|
|
|
implementation which is easy to conceptualize and use in an embedded context.
|