xref: /aosp_15_r20/external/pigweed/pw_function/docs.rst (revision 61c4878ac05f98d0ceed94b57d316916de578985)
1.. _module-pw_function:
2
3===========
4pw_function
5===========
6.. pigweed-module::
7   :name: pw_function
8
9* **Familiar**. ``pw_function`` provides a standard, general-purpose API for
10  wrapping callable objects that's similar to `std::function`_.
11* **Optimized**. ``pw_function`` doesn't allocate (unless you want it to) and
12  uses several tricks to prevent code bloat.
13
14.. _std\:\:function: https://en.cppreference.com/w/cpp/utility/functional/function
15
16.. code-block:: c++
17
18   #include "pw_function/function.h"
19
20   // pw::Function can be constructed from a function pointer...
21   int _a(int a, int b) { return a + b; }
22   pw::Function<int(int, int)> add(_a);
23   // ... or a lambda.
24   pw::Function<int(int)> square([](int num) { return num * num; });
25
26   // pw::Callback can only be invoked once. After the first call, the target
27   // function is released and destroyed, along with any resources owned by
28   // that function.
29   pw::Callback<void(void)> flip_table_once([](void) {
30     // (╯°□°)╯︵ ┻━┻
31   });
32
33   add(5, 6);
34   add = nullptr;  // pw::Function and pw::Callback are nullable
35   add(7, 2);  // CRASH
36
37   square(4);
38
39   if (flip_table_once != nullptr) {  // Safe to call
40     flip_table_once();
41   } else {
42     // ┬─┬ノ( º _ ºノ)
43   }
44
45
46.. _module-pw_function-start:
47
48-----------
49Get started
50-----------
51.. tab-set::
52
53   .. tab-item:: Bazel
54
55      Add ``@pigweed//pw_function`` to your target's ``deps``:
56
57      .. code-block::
58
59         cc_library("...") {
60           # ...
61           deps = [
62             # ...
63             "@pigweed//pw_function",
64             # ...
65           ]
66         }
67
68      This assumes that your Bazel ``WORKSPACE`` has a `repository
69      <https://bazel.build/concepts/build-ref#repositories>`_ named ``@pigweed``
70      that points to the upstream Pigweed repository.
71
72   .. tab-item:: GN
73
74      Add ``$dir_pw_function`` to your target's ``deps``:
75
76      .. code-block::
77
78         pw_executable("...") {
79           # ...
80           deps = [
81             # ...
82             "$dir_pw_function",
83             # ...
84           ]
85         }
86
87   .. tab-item:: CMake
88
89      Link your library to ``pw_function``:
90
91      .. code-block::
92
93         add_library(my_lib ...)
94         target_link_libraries(my_lib PUBLIC pw_function)
95
96Use ``pw_function`` in your C++ code:
97
98.. code-block:: c++
99
100   #include "pw_function/function.h"
101
102   // ...
103
104.. _module-pw_function-guides:
105
106------
107Guides
108------
109
110Construct ``pw::Function`` from a function pointer
111==================================================
112:cpp:type:`pw::Function` is a move-only callable wrapper constructable from any
113callable object. It's templated on the signature of the callable it stores and
114implements the call operator; invoking a ``pw::Function`` object forwards to
115the stored callable.
116
117.. code-block:: c++
118
119   int Add(int a, int b) { return a + b; }
120
121   // Construct a Function object from a function pointer.
122   pw::Function<int(int, int)> add_function(Add);
123
124   // Invoke the function object.
125   int result = add_function(3, 5);
126   EXPECT_EQ(result, 8);
127
128Construct ``pw::Function`` from a lambda
129========================================
130.. code-block:: c++
131
132   // Construct a function from a lambda.
133   pw::Function<int(int)> negate([](int value) { return -value; });
134   EXPECT_EQ(negate(27), -27);
135
136Create single-use functions with ``pw::Callback``
137=================================================
138:cpp:type:`pw::Callback` is a specialization of :cpp:type:`pw::Function` that
139can only be called once. After a :cpp:type:`pw::Callback` is called, the target
140function is released and destroyed, along with any resources owned by that
141function. A :cpp:type:`pw::Callback` in the "already called" state
142has the same state as a :cpp:type:`pw::Function` that has been assigned to
143nullptr.
144
145.. code-block:: cpp
146
147   pw::Callback<void(void)> flip_table_once([](void) {
148     // (╯°□°)╯︵ ┻━┻
149   });
150
151   flip_table_once();  // OK
152   flip_table_once();  // CRASH
153
154Nullifying functions and comparing to null
155==========================================
156``pw::Function`` and ``pw::Callback`` are nullable and can be compared to
157``nullptr``. Invoking a null function triggers a runtime assert.
158
159.. code-block:: c++
160
161   // A function initialized without a callable is implicitly null.
162   pw::Function<void()> null_function;
163
164   // Null functions may also be explicitly created or set.
165   pw::Function<void()> explicit_null_function(nullptr);
166
167   pw::Function<void()> function([]() {});  // Valid (non-null) function.
168   function = nullptr;  // Set to null, clearing the stored callable.
169
170   // Functions are comparable to nullptr.
171   if (function != nullptr) {
172     function();
173   }
174
175``constexpr`` constructors and ``constinit`` expressions
176========================================================
177The default constructor for :cpp:type:`pw::Function` is ``constexpr``, so
178default-constructed functions may be used in classes with ``constexpr``
179constructors and in ``constinit`` expressions.
180
181.. code-block:: c++
182
183   class MyClass {
184    public:
185     // Default construction of a pw::Function is constexpr.
186     constexpr MyClass() { ... }
187
188     pw::Function<void(int)> my_function;
189   };
190
191   // pw::Function and classes that use it may be constant initialized.
192   constinit MyClass instance;
193
194``pw::Function`` as a function parameter
195========================================
196When implementing an API which uses callbacks, ``pw::Function`` can be used in
197place of a function pointer or equivalent callable.
198
199.. code-block:: c++
200
201   // Before:
202   void DoTheThing(int arg, void (*callback)(int result));
203
204   // After:
205   void DoTheThing(int arg, const pw::Function<void(int result)>& callback);
206   // Note the parameter name within the function signature template for clarity.
207
208.. _module-pw_function-move-semantics:
209
210Move semantics
211==============
212:cpp:type:`pw::Function` is movable, but not copyable, so APIs must accept
213:cpp:type:`pw::Function` objects either by const reference (``const
214pw::Function<void()>&``) or rvalue reference (``const pw::Function<void()>&&``).
215If the :cpp:type:`pw::Function` simply needs to be called, it should be passed
216by const reference. If the :cpp:type:`pw::Function` needs to be stored, it
217should be passed as an rvalue reference and moved into a
218:cpp:type:`pw::Function` variable as appropriate.
219
220.. code-block:: c++
221
222   // This function calls a pw::Function but doesn't store it, so it takes a
223   // const reference.
224   void CallTheCallback(const pw::Function<void(int)>& callback) {
225     callback(123);
226   }
227
228   // This function move-assigns a pw::Function to another variable, so it takes
229   // an rvalue reference.
230   void StoreTheCallback(pw::Function<void(int)>&& callback) {
231     stored_callback_ = std::move(callback);
232   }
233
234.. admonition:: Rules of thumb for passing a :cpp:type:`pw::Function` to a function
235
236   * **Pass by value**: Never.
237     This results in unnecessary :cpp:type:`pw::Function` instances and move
238     operations.
239
240   * **Pass by const reference** (``const pw::Function&``): When the
241     :cpp:type:`pw::Function` is only invoked.
242
243     When a :cpp:type:`pw::Function` is called or inspected, but not moved, take
244     a const reference to avoid copies and support temporaries.
245
246   * **Pass by rvalue reference** (``pw::Function&&``): When the
247     :cpp:type:`pw::Function` is moved.
248
249     When the function takes ownership of the :cpp:type:`pw::Function` object,
250     always use an rvalue reference (``pw::Function<void()>&&``) instead of a
251     mutable lvalue reference (``pw::Function<void()>&``). An rvalue reference
252     forces the caller to ``std::move`` when passing a preexisting
253     :cpp:type:`pw::Function` variable, which makes the transfer of ownership
254     explicit. It is possible to move-assign from an lvalue reference, but this
255     fails to make it obvious to the caller that the object is no longer valid.
256
257   * **Pass by non-const reference** (``pw::Function&``): Rarely, when modifying
258     a variable.
259
260     Non-const references are only necessary when modifying an existing
261     :cpp:type:`pw::Function` variable. Use an rvalue reference instead if the
262     :cpp:type:`pw::Function` is moved into another variable.
263
264Calling functions that use ``pw::Function``
265===========================================
266A :cpp:type:`pw::Function` can be implicitly constructed from any callback
267object. When calling an API that takes a :cpp:type:`pw::Function`, simply pass
268the callable object.  There is no need to create an intermediate
269:cpp:type:`pw::Function` object.
270
271.. code-block:: c++
272
273   // Implicitly creates a pw::Function from a capturing lambda and calls it.
274   CallTheCallback([this](int result) { result_ = result; });
275
276   // Implicitly creates a pw::Function from a capturing lambda and stores it.
277   StoreTheCallback([this](int result) { result_ = result; });
278
279When working with an existing :cpp:type:`pw::Function` variable, the variable
280can be passed directly to functions that take a const reference. If the function
281takes ownership of the :cpp:type:`pw::Function`, move the
282:cpp:type:`pw::Function` variable at the call site.
283
284.. code-block:: c++
285
286   // Accepts the pw::Function by const reference.
287   CallTheCallback(my_function_);
288
289   // Takes ownership of the pw::Function.
290   void StoreTheCallback(std::move(my_function));
291
292Managing inline storage size
293============================
294By default, ``pw::Function`` stores its callable inline within the object. The
295inline storage size defaults to the size of one pointer, but is configurable
296through the build system.
297
298:cpp:type:`pw::InlineFunction` is similar to ``pw::Function``,
299but is always inlined. That is, even if dynamic allocation is enabled for
300``pw::Function``, ``pw::InlineFunction`` will fail to compile if
301the callable is larger than the inline storage size.
302
303Attempting to construct a function from a callable larger than its inline size
304is a compile-time error unless dynamic allocation is enabled.
305
306.. admonition:: Inline storage size
307
308   The default inline size of one pointer is sufficient to store most common
309   callable objects, including function pointers, simple non-capturing and
310   capturing lambdas, and lightweight custom classes.
311
312.. code-block:: c++
313
314   // The lambda is moved into the function's internal storage.
315   pw::Function<int(int, int)> subtract([](int a, int b) { return a - b; });
316
317   // Functions can be also be constructed from custom classes that implement
318   // operator(). This particular object is large (8 ints of space).
319   class MyCallable {
320    public:
321     int operator()(int value);
322
323    private:
324     int data_[8];
325   };
326
327   // Compiler error: sizeof(MyCallable) exceeds function's inline storage size.
328   pw::Function<int(int)> function((MyCallable()));
329
330.. _module-pw_function-dynamic-allocation:
331
332Dynamic allocation
333==================
334You can configure the inline allocation size of ``pw::Function`` and whether it
335dynamically allocates, but it applies to all uses of ``pw::Function``.
336
337As mentioned in :ref:`module-pw_function-design`, ``pw::Function`` is an alias
338of Fuchsia's ``fit::function``. ``fit::function`` allows you to specify the
339inline (static) allocation size and whether to dynamically allocate if the
340callable target doesn't inline. If you want to use a function class with
341different attributes, you can interact with ``fit::function`` directly but note
342that the resulting functions may not be interchangeable, i.e. callables for one
343might not fit in the other.
344
345When ``PW_FUNCTION_ENABLE_DYNAMIC_ALLOCATION`` is enabled, a ``pw::Function``
346will use dynamic allocation to store callables that exceed the inline size.
347An :ref:`allocator <module-pw_allocator>` type can be optionally supplied as a
348template argument. The default allocator type can also be changed by overriding
349``PW_FUNCTION_DEFAULT_ALLOCATOR_TYPE`` (the ``value_type`` of the allocator
350is irrelevant, since it must support rebinding). When dynamic allocation is
351enabled but a compile-time check for the inlining is still required,
352``pw::InlineFunction`` can be used.
353
354.. warning::
355
356   If ``PW_FUNCTION_ENABLE_DYNAMIC_ALLOCATION`` is enabled then attempts to
357   cast from :cpp:type:`pw::InlineFunction` to a regular
358   :cpp:type:`pw::Function` will **ALWAYS** allocate memory.
359
360.. note::
361
362   When building Pigweed itself for host platforms, we enable dynamic
363   allocation.  This is required for some modules that use ``pw::Function``,
364   like :ref:`module-pw_bluetooth_sapphire`.  But it is *not* the default for
365   downstream projects because it introduces a difference between host and
366   non-host builds. This difference has the potential to cause breakages if
367   code is built for host first, and then later ported to device.
368
369Invoking ``pw::Function`` from a C-style API
370============================================
371.. _trampoline layers: https://en.wikipedia.org/wiki/Trampoline_(computing)
372
373One use case for invoking ``pw_function`` from a C-style API is to automate the
374generation of `trampoline layers`_. See
375:cpp:type:`pw::function::GetFunctionPointer()`.
376
377.. _module-pw_function-reference:
378
379-------------
380API reference
381-------------
382
383``pw::Function``
384================
385.. doxygentypedef:: pw::Function
386
387``pw::InlineFunction``
388======================
389.. doxygentypedef:: pw::InlineFunction
390
391``pw::Callback``
392================
393.. doxygentypedef:: pw::Callback
394
395``pw::InlineCallback``
396======================
397.. doxygentypedef:: pw::InlineCallback
398
399``pw::bind_member()``
400=====================
401.. doxygenfunction:: pw::bind_member
402
403``pw::function::GetFunctionPointer()``
404======================================
405.. doxygenfile:: pw_function/pointer.h
406   :sections: detaileddescription
407.. doxygenfunction:: GetFunctionPointer()
408.. doxygenfunction:: GetFunctionPointer(const FunctionType&)
409
410``pw::function::GetFunctionPointerContextFirst()``
411==================================================
412.. doxygenfunction:: GetFunctionPointerContextFirst()
413.. doxygenfunction:: GetFunctionPointerContextFirst(const FunctionType&)
414
415``pw::ScopeGuard``
416==================
417.. doxygenclass:: pw::ScopeGuard
418   :members:
419
420.. _module-pw_function-design:
421
422------
423Design
424------
425``pw::Function`` is an alias of Fuchsia's ``fit::function_impl`` and
426``pw::Callback`` is an alias of Fuchsia's ``fit::callback_impl``. See the
427following links for more information about Fuchsia's implementations:
428
429* `//third_party/fuchsia/repo/sdk/lib/fit/include/lib/fit/function.h <https://cs.opensource.google/pigweed/pigweed/+/main:third_party/fuchsia/repo/sdk/lib/fit/include/lib/fit/function.h>`_
430* `fit::function <https://fuchsia.googlesource.com/fuchsia/+/HEAD/sdk/lib/fit/#fit_function>`_
431
432.. _module-pw_function-non-literal:
433
434Why ``pw::Function`` is not a literal
435=====================================
436The default constructor for ``pw::Function`` is ``constexpr`` but
437``pw::Function`` is not a literal type. Instances can be declared ``constinit``
438but can't be used in ``constexpr`` contexts. There are a few reasons for this:
439
440* ``pw::Function`` supports wrapping any callable type, and the wrapped type
441  might not be a literal type.
442* ``pw::Function`` stores inline callables in a bytes array, which is not
443  ``constexpr``-friendly.
444* ``pw::Function`` optionally uses dynamic allocation, which doesn't work in
445  ``constexpr`` contexts (at least before C++20).
446
447------------
448Size reports
449------------
450
451Comparing ``pw::Function`` to a traditional function pointer
452============================================================
453The following size report compares an API using a :cpp:type:`pw::Function` to a
454traditional function pointer.
455
456.. include:: function_size
457
458Typical sizes of various callable types
459=======================================
460The table below demonstrates typical sizes of various callable types, which can
461be used as a reference when sizing external buffers for ``pw::Function``
462objects.
463
464.. include:: callable_size
465