etl/docs/callbacks/delegate-vs-inplace_function.md
John Wellbelove 4a88884b39
Issue/add hugo support for documentation (#1449)
* Add ranges

* Initial Hugo setup

* Work in progress

* Added selection for local or remote site

* Updated to 'light' theme

* Changed to using Hextra Hugo theme

* Changed to using Hextra Hugo theme

* Changed to Hextra Hugo theme

* Change to Hextra Hugo theme

* Updated Hugo setup.

* Updated Hugo setup.

# Conflicts:
#	docs/releases/_index.md

* Work in progress

* Added new fonts

Added new documentation

* Latest documentation updates

* Latest documentation updates

# Conflicts:
#	docs/containers/array.md
#	docs/containers/array_view.md
#	docs/containers/array_wrapper.md
#	docs/containers/bip_buffer_spsc_atomic.md
#	docs/containers/bitset.md
#	docs/containers/indirect_vector.md
#	docs/containers/vector.md
#	docs/getting-started/compilers.md

* Added bloom_filter markdown doc

* Added more documentation

Updated CSS for light and dark modes

* Fixed some menus

Added mode documentation files

* Updated CSS rules

Added badges to home page
Added uniqur_ptr + pool tutorial

* Fixed formatting on the home page markdown

Modified light amd dark code formatting

* Updated unique_ptr-with-pool

* Added container and shared message tutorials

* Updates to documentation

* Added const_multimap

* Updated source-formatting.md

* Added initial raw text files form Web site editor

* Innore coverage build directory

* Exported raw text documentation files from the web site editor

* Hugo updates

* Added Hugo intalation and markdown descriptions

* More addition to the documentation

* Added closure.md and updates to delegate.md

* Added format.md

* Added documentation for etl::delegate_observable, etl::function, Base64 codec

* Added io_port documentation

* Added basic_format_spec

* Added documentation for string_stream and string utilities.

* Added more documentation

Updated the documentation CSS

* Added documentation for clocks, day, duration

* Added more documentation for chrono classes

Updated callouts

* More chrono documentation

* Completed chrono documentation

* Maths functions documentation

* Completed maths documentation

* Completed maths documentation

* Completed maths documentation

* Completed maths documentation

* Added multiple documentation files

* Added iterator.md

* Added debug_count.md and versions.md

* Added debug_count.md and versions.md

* Added more documentation

* More documentation

* Added some design pattern documentation

Modified some of the layout files
Modified the About documentation

* Converted more documentation pages

Modified the site CSS

* Added more documentation

Moced some documentation files to new directories

* Added more documentation

Tweaks to CSS

* Added callback_timer_deferred_locked documentation

* Added callback_timer_locked documentation

* More documentation updates

* More documentation updates

* More documentation updates

* New documentation files.

Harmonised file name format

* New documentation files.

* Multiple document updates

* Multiple document updates

* Final conversion of web pages

* Updates before PR

* Updates before PR

* Updates before PR

# Conflicts:
#	docs/blog/_index.md

* Final pre PR updates

* Updates to message framework documentation

* Renamed directory

* Fix spelling

* Added author and date to blog files

Moved documentation files merged from development

* Fixed 'Description' typo

* Fix typos

# Conflicts:
#	docs/IO/io_port.md
#	docs/containers/sets/const-multiset.md
#	docs/containers/sets/const-set.md
#	docs/maths/correlation.md
#	docs/maths/gamma.md

* Renamed two files to lower case

* Minor renaming

* Added author and date

* Updated callout on bresenham_line.md

Added support for showing the ETL version on the documentation first page, by copying the version.txt file as a hugo asset.
Updated the Python 'update_release.py' to copy 'version.txt'

* Replace space in filename with hyphen.

Added more information to hugo-commands.md

* Replace space in filename with hyphen.

Added more information to hugo-commands.md

# Conflicts:
#	docs/getting-started/view-the-docs-locally/hugo-commands.md

* Added a link to pseudo_moving_average.md

* Updated title pages for groups

* Fixed missing 404 for non-existent pages

* Fixed coordinate variable names in the 'Calculating the intersection' example

---------

Co-authored-by: Roland Reichwein <Roland.Reichwein@bmw.de>
Co-authored-by: John Wellbelove <john.wellbelove@etlcpp.com>
Co-authored-by: John Wellbelove <john.wellbelove@etlcpp.co.uk>
2026-06-06 13:12:44 +01:00

221 lines
9.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
title: "delegate vs inplace_function"
weight: 1
---
## Efficiency and performance
### Invocation cost
**delegate**
One indirect call via stub function pointer; no allocation; forwards args directly; minimal overhead.
**inplace_function**
One indirect call via vtable invoke pointer; no allocation; overhead effectively the same as `delegate`.
### Copy / move cost
**delegate**
Trivial copy/move (object pointer + stub pointer).
**inplace_function**
Copies/moves the stored callable in an inline buffer; cost depends on callable traits; may run destructor on clear/reset.
### Size / footprint
**delegate**
Typically two pointers; very small, stable footprint.
**inplace_function**
Buffer + vtable pointer + object pointer; size depends on `Object_Size`/`Object_Alignment`.
### Ownership / lifetime
**delegate**
Non-owning for functors/lambdas; stores pointer to external callable; caller must ensure lifetime.
**inplace_function**
Owning; stores a copy/move of the callable inline (RAII).
### When to prefer
**delegate**
Minimal footprint, cheapest copies, free/member function binding, controlled lifetimes.
**inplace_function**
Value semantics, safe storage of lambdas with captures, uniform SBO wrapper.
## External API differences
### Template shape and size
`delegate<TSignature>`
`inplace_function<TSignature, Object_Size = 32, Object_Alignment = alignof(void*)>`
### Construction / binding
**delegate**
`create<FreeFn>()`
`create(T& obj, Method)` and `create(const T& obj, ConstMethod)` at run time
`create<T, Method, Instance>()`
Construct from functor/lambda by reference (non-owning); rvalues deleted to avoid dangling
`set()` mirrors `create()` for re-binding
**inplace_function**
Constructor from function pointer
Constructor from object + (const/non-const) member function (run time)
Constructor from functor/lambda by (const) reference; callable stored inline (owning)
`set()` mirrors constructors; also `set<FreeFn>()` and `set<T, Method, Instance>()` (compile time)
`create<FreeFn>()`, `create<T, Method, Instance>()` (compile time)
`make_inplace_function` helpers: `make_inplace_function(free_fn)`, `make_inplace_function(obj, &T::Method)` , `make_inplace_function<TSignature>(lambda)`.
### Equality and swap
**delegate**
`operator==`/`!=` compare stub and object; structural equality.
**inplace_function**
`operator==`/`!=` with `nullptr`; `swap()` provided; no general equality between two functions.
### Introspection helpers
**delegate**
`delegate_tag`
`is_delegate<T>`.
**inplace_function**
`is_inplace_function<T>`.
### Call helpers
`is_valid()`
`explicit bool`
`call_if(...)`
`call_or(alternative, ...)`
`call_or<FreeFn>(...)`.
### Functor storage semantics
**delegate**
Non-owning pointer to external functor/lambda.
**inplace_function**
Owns a copy/move in fixed buffer.
### Size control
**delegate**
Fixed small size (two pointers).
**inplace_function**
User-controlled via `Object_Size`/`Object_Alignment`; aliases `inplace_function_for` and `inplace_function_for_any` to compute required size/alignment.
### C++ language features
Classic non-type template parameter APIs; C++17 "auto" `make_*` helpers exist when enabled.
## Performance differences:
### Call overhead
**delegate**
One indirect call through a stub function pointer; minimal overhead.
**inplace_function**
One indirect call via `vtable->invoke(object, ...);` typically one extra pointer load; practically similar.
### Copy / move and lifetime
**delegate**
Trivial POD-like copy/move (object pointer + stub); no construction/destruction of target.
**inplace_function**
Copies/moves/destroys the stored callable in the inline buffer; cost depends on callable traits.
### Construction / binding cost
**delegate**
Binding free/member functions sets two pointers; binding a functor stores its address (non-owning).
**inplace_function**
Constructs a copy/move of the callable into the SBO buffer; more work up front.
### Memory footprint and cache locality
**delegate**
~2 pointers regardless of target; high container density and good cache locality.
**inplace_function**
Buffer + vtable pointer + object pointer; larger, size depends on Object_Size/Object_Alignment.
### Clear / reset cost
**delegate**
Clears pointers; no destructor invocation.
**inplace_function**
May run callables destructor; potentially non-trivial.
### Determinism / Real-time suitability
**delegate**
Very predictable and minimal costs at call/copy/reset; good for ISR/RT paths.
**inplace_function**
Deterministic at call; copy/move/reset depend on callables traits (still allocation-free).
## Rule of thumb
- Use `delegate` when:
You only bind free/member functions or non-owning functors and want the smallest handle with the cheapest copies.
- Use `inplace_function` when:
You must own captured state safely (value semantics) and still avoid the heap; accept slightly larger handle/copy costs for safety.
## Choose etl::delegate when
- You need the smallest possible callable handle (two pointers) for large arrays/vectors of callbacks.
- You must avoid any allocation, construction, destruction, or SBO buffer management in the callable wrapper.
- You bind free functions or member functions and the target objects lifetime is externally guaranteed.
- You want trivial copy/move/equality semantics (copy two pointers; compare stub+object) for fast shuffling/lookup.
- You need deterministic, RT/ISR-safe behavior with no hidden destructors or move/destruct paths on clear/reset.
- You build static/ROM lookup tables of callbacks (compile-time create<Fn>(), create<T,Method,Instance>()).
- You store callbacks in fixed-capacity containers (etl::array/vector) and must minimize memory per element.
- You need ABI-stable, non-owning callback tokens passed across modules without copying callables.
- You already use etl::delegate_service or similar indexed dispatch; its API expects etl::delegate.
- You don't want to size/tune an inline buffer (Object_Size/Object_Alignment) per signature or platform.
### Typical scenarios
- Interrupt vectors, GPIO/event ISR callbacks, hard real-time control loops.
- Command/ID -> handler tables, message routing switchboards, state-machine transition actions.
- Global registries/singletons where handler objects are static or live for program lifetime.
- Shared tables of callbacks across DLLs/modules where owning captured state is undesirable.
### Why etl::delegate over etl::inplace_function in these cases
- Lower footprint: always two pointers versus user-sized SBO buffer + vtable pointer + object pointer.
- Lower copy/move cost: trivial POD-like copies; no callable copy/move/dtor required.
- Non-owning by design: avoids accidental copies of heavy callables; no lifetime surprises at clear/reset.
- Compile-time binding support: completely payload-free delegates for fixed targets (no storage touched).
- Equality: pointer-based equality lets you deduplicate/lookup handlers efficiently.
### Notes
- `etl::delegate` is non-owning; only choose it when the bound object outlives the delegate (rvalue lambdas are rejected by API).
- Prefer `etl::inplace_function`0 when you must own captured state (value semantics) or need to accept arbitrary lambdas safely.
## Choose etl::inplace_function when
- You need value semantics (own the callable) so lifetimes are safe without tracking external objects.
- You want to accept arbitrary lambdas with captures and store them inline using small-buffer optimization (no heap).
- You need a single uniform callable wrapper that erases the concrete type across modules/APIs.
- You plan to copy/move callbacks between threads/queues safely (callable is self-contained in the buffer).
- You must avoid dynamic allocation but still need to store captured state (tune Object_Size/Object_Alignment).
- You want compile-time or run-time binding to free functions, member functions, and functors in one type.
- You need reset/clear semantics that correctly destroy the stored callable (RAII).
- You prefer exceptions on misuse (invoking uninitialized) to surface bugs early.
### Typical scenarios
- Job systems, task queues, and executors that capture small state per task without heap churn.
- Pipelines and event buses that accept user-provided lambdas with captures.
- Configurable command tables where handlers carry lightweight configuration/state.
- Deferred work items, timeouts, and continuations stored in fixed-capacity containers.
- API boundaries returning/storing callbacks where the provider shouldn't manage the callee's lifetime.
### Why etl::inplace_function over etl::delegate in these cases
- Ownership: inplace_function owns the callable; no external lifetime management needed.
- Safety with captures: stores copies/moves of lambdas/functors; no dangling pointers to external objects.
- Uniform wrapper: a single type covers free/member/functor targets with the same call site and storage.
- No heap: SBO avoids allocations while still allowing captured state; predictable embedded-friendly footprint.
- Destruction: clear()/reset() run the callable's destructor when needed; no leaks or latent state.
### Notes
- Tune the buffer size/alignment to the largest expected callable; too-small buffers will `static_assert`.
- Copy/move cost depends on the callable; prefer lightweight captures for best performance.
- Invocation overhead is one indirect call through a vtable stub (similar to delegate).
- Use make_inplace_function helpers or create<> to bind targets; is_valid(), call_if(), and call_or() are available.
- If you only bind free/member functions and can guarantee lifetimes, etl::delegate is smaller and cheaper to copy.