.. _module-pw_function: =========== pw_function =========== 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. -------- Overview -------- Basic usage =========== ``pw_function`` defines the :cpp:type:`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 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 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 initialized without a callable is implicitly null. pw::Function null_function; // Null functions may also be explicitly created or set. pw::Function explicit_null_function(nullptr); pw::Function function([]() {}); // Valid (non-null) function. function = nullptr; // Set to null, clearing the stored callable. // Functions are comparable to nullptr. if (function != nullptr) { function(); } :cpp:type:`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 my_function; }; // pw::Function and classes that use it may be constant initialized. constinit MyClass instance; 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. The :cpp:type:`pw::InlineFunction` alias is similar to :cpp:type:`pw::Function`, but is always inlined. That is, even if dynamic allocation is enabled for :cpp:type:`pw::Function`, :cpp:type:`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. .. 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 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 function((MyCallable())); .. admonition:: Dynamic allocation When ``PW_FUNCTION_ENABLE_DYNAMIC_ALLOCATION`` is enabled, a ``Function`` will use dynamic allocation to store callables that exceed the inline size. When it is enabled but a compile-time check for the inlining is still required ``pw::InlineFunction`` can be used. .. warning:: If ``PW_FUNCTION_ENABLE_DYNAMIC_ALLOCATION`` is enabled then attempts to cast from `:cpp:type:`pw::InlineFunction` to a regular :cpp:type:`pw::Function` will **ALWAYS** allocate memory. --------- API usage --------- Reference ========= .. doxygentypedef:: pw::Function .. doxygentypedef:: pw::InlineFunction .. doxygentypedef:: pw::Callback .. doxygentypedef:: pw::InlineCallback ``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. .. 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, const pw::Function& callback); :cpp:type:`pw::Function` is movable, but not copyable, so APIs must accept :cpp:type:`pw::Function` objects either by const reference (``const pw::Function&``) or rvalue reference (``const pw::Function&&``). If the :cpp:type:`pw::Function` simply needs to be called, it should be passed by const reference. If the :cpp:type:`pw::Function` needs to be stored, it should be passed as an rvalue reference and moved into a :cpp:type:`pw::Function` variable as appropriate. .. code-block:: c++ // This function calls a pw::Function but doesn't store it, so it takes a // const reference. void CallTheCallback(const pw::Function& callback) { callback(123); } // This function move-assigns a pw::Function to another variable, so it takes // an rvalue reference. void StoreTheCallback(pw::Function&& callback) { stored_callback_ = std::move(callback); } .. admonition:: Rules of thumb for passing a :cpp:type:`pw::Function` to a function * **Pass by value**: Never. This results in unnecessary :cpp:type:`pw::Function` instances and move operations. * **Pass by const reference** (``const pw::Function&``): When the :cpp:type:`pw::Function` is only invoked. When a :cpp:type:`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 :cpp:type:`pw::Function` is moved. When the function takes ownership of the :cpp:type:`pw::Function` object, always use an rvalue reference (``pw::Function&&``) instead of a mutable lvalue reference (``pw::Function&``). An rvalue reference forces the caller to ``std::move`` when passing a preexisting :cpp:type:`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 :cpp:type:`pw::Function` variable. Use an rvalue reference instead if the :cpp:type:`pw::Function` is moved into another variable. Calling functions that use ``pw::Function`` =========================================== A :cpp:type:`pw::Function` can be implicitly constructed from any callback object. When calling an API that takes a :cpp:type:`pw::Function`, simply pass the callable object. There is no need to create an intermediate :cpp:type:`pw::Function` object. .. code-block:: c++ // 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 :cpp:type:`pw::Function` variable, the variable can be passed directly to functions that take a const reference. If the function takes ownership of the :cpp:type:`pw::Function`, move the :cpp:type:`pw::Function` variable at the call site. .. code-block:: c++ // Accepts the pw::Function by const reference. CallTheCallback(my_function_); // Takes ownership of the pw::Function. void StoreTheCallback(std::move(my_function)); ``pw::Callback`` for one-shot functions ======================================= :cpp:type:`pw::Callback` is a specialization of :cpp:type:`pw::Function` that can only be called once. After a :cpp:type:`pw::Callback` is called, the target function is destroyed. A :cpp:type:`pw::Callback` in the "already called" state has the same state as a :cpp:type:`pw::Callback` that has been assigned to nullptr. Invoking ``pw::Function`` from a C-style API ============================================ .. doxygenfile:: pw_function/pointer.h :sections: detaileddescription .. doxygenfunction:: GetFunctionPointer() .. doxygenfunction:: GetFunctionPointer(const FunctionType&) .. doxygenfunction:: GetFunctionPointerContextFirst() .. doxygenfunction:: GetFunctionPointerContextFirst(const FunctionType&) ---------- ScopeGuard ---------- .. doxygenclass:: pw::ScopeGuard :members: ------------ Size reports ------------ Function class ============== The following size report compares an API using a :cpp:type:`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 ------ :cpp:type:`pw::Function` is an alias of `fit::function `_. :cpp:type:`pw::Callback` is an alias of `fit::callback `_. ------ Zephyr ------ To enable ``pw_function` for Zephyr add ``CONFIG_PIGWEED_FUNCTION=y`` to the project's configuration.