Embedded std::function alternative: lightweight, deterministic, heap-free.
Embedded Function is an embedded-friendly lightweight function wrapper implemented based on the C++11 standard, tailored specifically for embedded systems.
While functionally and conceptually analogous to std::function, it offers substantially reduced overhead and superior real-time performance characteristics. Notably, Embedded Function eliminates dynamic heap memory allocations entirely, ensuring deterministic execution behavior and predictable real-time performance for embedded applications.
A function wrapper is declared as following:
ebd::fn<int (int, float, char) const, 3*sizeof(void*)>
// ^ ^ ^ ^ ^ ^
// | | | | | |
// Return type | | | | |
// Parameters ~|~~~~~|~~~~~| | |
// Qualifier ~~~~~~~~~~~~~~~~~~~~| |
// Buffer size ~~~~~~~~~~~~~~~~~~~~~~~~~~~|The
Qualifieris used to restrict the callable objects wrapped withinebd::fn, rather thanebd::fnitself. In other words, theoperator()of theebd::fnobject will be qualified with theQualifiermodifier.
The
Buffer sizecan be omitted. If omitted, this parameter will be set todetail::default_buffer_size::valueby default, which is sufficient to store most common callable objects, including function pointers, simple non-capturing and capturing lambdas, and lightweight custom classes.
-
Clone the repository or download the
header_only.zipin the "Release". -
Add include path
<repo_root>/include. -
In program
#include "embed/embed_function.hpp". -
Use the
ebd::fntemplate class.
#include "embed/embed_function.hpp"
struct Example {
static void static_mem_fn(int) {};
void mem_fn(int) {};
void operator()(int) {};
};
auto main() -> int {
Example e;
ebd::fn<void(int)> fn_;
fn_ = &Example::static_mem_fn;
fn_(123);
fn_ = [e](int arg) { e.mem_fn(arg); };
fn_(456);
fn_ = e;
fn_(789);
}-
Should behave close to a normal function pointer. Small, efficient, no heap allocation.
-
Support the packaging of all callable objects in C++, including:
- Free function.
- Lambda function.
- Functor.
- Static member function.
- Member function.
-
Be usable with C++11 while offering more functionality for later editions.
-
Be constexpr and exception friendly. As much as possible should be declared constexpr and noexcept.
-
Should be based on the analysis of N4159, P2548 and LWG2393, and should avoid repeating the mistakes made by
std::function. Therefore, Embedded Function should:- NOT implement the method
target()andtarget_type(). - Allow the application of qualifiers, such as
const,volatile,&and&&, to the function signature. - Ensure that the qualifier of the underlying object is consistent or more restrictive than that of the function signature.
- NOT implement the method
-
Learn and refer to the optimization experience of
std::functionin libstdc++, libc++ and Microsoft C++ Standard Library. -
Following the above design goals,
ebd::fn,ebd::unique_fn,ebd::safe_fnandebd::fn_viewwere designed for developers to use.
| Wrapper Type | Copyable | View (Non-owning) | Throws on Empty Call | Assert No-Throw (Ctor/Dtor) | Buffer Size | Primary Use Case |
|---|---|---|---|---|---|---|
ebd::fn |
Yes | No | Yes (std::bad_function_call) |
No | Configurable (aligned, default: sizeof(void(Class::*)())) |
Copyable callable wrapper |
ebd::unique_fn |
No | No | Yes (std::bad_function_call) |
No | Configurable (aligned, default: sizeof(void(Class::*)())) |
Move-only callable wrapper |
ebd::safe_fn |
Yes | No | No (std::terminate()) |
Yes | Configurable (aligned, default: sizeof(void(Class::*)())) |
Exception-safe copyable callable wrapper |
ebd::fn_view |
Yes | Yes | No (std::terminate()) |
No | Fixed (sizeof(void(Class::*)()), template param unused) |
Lightweight non-owning view of callables |
-
Ownership & Copy:
fn/safe_fnown callables (copyable),unique_fnowns but is move-only,fn_viewis non-owning (view). -
Exception Behavior: Only
fn/unique_fnthrow on empty calls;safe_fn/fn_viewterminate (no exceptions). -
Buffer Configuration:
fn/unique_fn/safe_fnsupport configurable buffer sizes (aligned), whilefn_viewuses a fixed buffer (unused template param).
ebd::fn / ebd::unique_fn / ebd::safe_fn / ebd::fn_view completely eliminate runtime checks for empty function states during invocation, significantly boosting performance of frequent function calls.
ebd::fn / ebd::unique_fn / ebd::safe_fn / ebd::fn_view enable scalar arguments and small-sized trivial arguments to be passed via registers instead of having to be passed via the stack as in std::function. This significantly reduces the memory access overhead during parameter passing.
In order to simplify the use of ebd::fn, function ebd::make_fn() is provided, which can automatically deduce the signature and buffer size of the callable object and create a ebd::fn or ebd::unique_fn object. (Return ebd::unique_fn only when the callable object is of the move-only type.)
The Concepts language feature is available for use provided that the compiler is configured to support the C++20 standard.
[]means optional.Signature: The signature of the callable object. (such asvoid(int))BufferSize: The buffer size of the callable object. (such as2*sizeof(void*))
// Create empty ebd::fn with specified signature and buffer size.
// If the BufferSize is omitted, it will be set by default (usually 2*sizeof(void*)).
auto f = ebd::make_fn<Signature[, BufferSize]>();
auto f = ebd::make_fn<Signature[, BufferSize]>(nullptr);// Create ebd::fn or ebd::unique_fn from unambiguous callable object.
// If the Signature is omitted, the signature will be deduced from Callable_Object.
auto f = ebd::make_fn[<Signature>](Callable_Object);// Create ebd::fn or ebd::unique_fn from ambiguous callable object with specified signature, such as overload free function, overload member function, etc.
auto f = ebd::make_fn<Signature>(Ambiguous_Callable_Object);In embedded MCU development, it is often necessary to pass a C-style free function pointer as an argument, as existing libraries are typically written in C. To address this, we have implemented an operator* overload that simplifies converting an object of type ebd::fn / ebd::unique_fn / ebd::safe_fn / ebd::fn_view to a C-style free function pointer.
If the object encapsulated by the function wrapper is a valid function pointer, this mechanism returns the pointer; otherwise, it returns nullptr. Basically, it is equivalent to a highly restricted target() method.
void free_function() {}
struct Functor { void operator()() {} };
ebd::fn<void()> fn_ = &free_function;
void(*free_function_pointer)() = *fn_;
ASSERT_EQ(free_function_pointer, &free_function);
fn_ = +[]() { /* ... */ }; // lambda -> function pointer
free_function_pointer = *fn_;
ASSERT_NE(free_function_pointer, nullptr); // NOT equal nullptr
fn_ = []() { /* ... */ };
free_function_pointer = *fn_;
ASSERT_EQ(free_function_pointer, nullptr);
fn_ = Functor{};
free_function_pointer = *fn_;
ASSERT_EQ(free_function_pointer, nullptr);Every compiler with modern C++11 support should work. Embedded Function only depends on the standard library.
- GCC 5.1+
- Clang 3.7+
- MSVC v19.34+ (VS17.4+)
Go to the <root>/test/ directory, and follow the instructions in HOW-TO-TEST.md to run the tests.
Go to the <root>/benchmark/ directory, and follow the instructions in HOW-TO-BENCHMARK.md to run the tests.
std:
std::function, ebd:ebd::fn, fu2:fu2::function
( MSVC C++14 Release )
## FreeFunction.ScalarParameters:
Name (* = baseline) | Dim | Total ms | ns/op |Baseline| Ops/second
--------------------------|--------:|----------:|--------:|-------:|----------:
free_scalar_std * | 10000 | 0.030 | 3 | - |332225913.6
free_scalar_ebd | 10000 | 0.028 | 2 | 0.930 |357142857.1
free_scalar_fu2 | 10000 | 0.052 | 5 | 1.731 |191938579.7
free_scalar_std * | 100000 | 0.301 | 3 | - |332667997.3
free_scalar_ebd | 100000 | 0.265 | 2 | 0.881 |377643504.5
free_scalar_fu2 | 100000 | 0.523 | 5 | 1.742 |191021967.5
free_scalar_std * | 1000000 | 3.006 | 3 | - |332712270.4
free_scalar_ebd | 1000000 | 2.708 | 2 | 0.901 |369317132.6
free_scalar_fu2 | 1000000 | 5.264 | 5 | 1.751 |189958778.9
## FreeFunction.TrivialParameters:
Name (* = baseline) | Dim | Total ms | ns/op |Baseline| Ops/second
--------------------------|--------:|----------:|--------:|-------:|----------:
free_trivial_std * | 10000 | 0.032 | 3 | - |311526479.8
free_trivial_ebd | 10000 | 0.024 | 2 | 0.754 |413223140.5
free_trivial_fu2 | 10000 | 0.052 | 5 | 1.626 |191570881.2
free_trivial_std * | 100000 | 0.322 | 3 | - |310366232.2
free_trivial_ebd | 100000 | 0.240 | 2 | 0.746 |415800415.8
free_trivial_fu2 | 100000 | 0.510 | 5 | 1.583 |196001568.0
free_trivial_std * | 1000000 | 3.222 | 3 | - |310375865.2
free_trivial_ebd | 1000000 | 2.508 | 2 | 0.778 |398692289.3
free_trivial_fu2 | 1000000 | 5.792 | 5 | 1.798 |172660876.8
## FreeFunction.CopyHardParameters:
Name (* = baseline) | Dim | Total ms | ns/op |Baseline| Ops/second
--------------------------|--------:|----------:|--------:|-------:|----------:
free_copyhard_std * | 10000 | 0.197 | 19 | - | 50684237.2
free_copyhard_ebd | 10000 | 0.198 | 19 | 1.004 | 50505050.5
free_copyhard_fu2 | 10000 | 0.303 | 30 | 1.537 | 32981530.3
free_copyhard_std * | 100000 | 1.976 | 19 | - | 50604726.5
free_copyhard_ebd | 100000 | 1.982 | 19 | 1.003 | 50456632.5
free_copyhard_fu2 | 100000 | 3.044 | 30 | 1.541 | 32849352.9
free_copyhard_std * | 1000000 | 19.898 | 19 | - | 50256307.2
free_copyhard_ebd | 1000000 | 20.052 | 20 | 1.008 | 49870088.4
free_copyhard_fu2 | 1000000 | 31.358 | 31 | 1.576 | 31889890.6
## FreeFunction.CallTrivialParameters:
Name (* = baseline) | Dim | Total ms | ns/op |Baseline| Ops/second
--------------------------|--------:|----------:|--------:|-------:|----------:
free_calltrivial_std * | 10000 | 0.032 | 3 | - |311526479.8
free_calltrivial_ebd | 10000 | 0.024 | 2 | 0.751 |414937759.3
free_calltrivial_fu2 | 10000 | 0.056 | 5 | 1.757 |177304964.5
free_calltrivial_std * | 100000 | 0.320 | 3 | - |312597686.8
free_calltrivial_ebd | 100000 | 0.257 | 2 | 0.802 |389711613.4
free_calltrivial_fu2 | 100000 | 0.584 | 5 | 1.827 |171115674.2
free_calltrivial_std * | 1000000 | 3.223 | 3 | - |310269934.8
free_calltrivial_ebd | 1000000 | 2.407 | 2 | 0.747 |415506710.4
free_calltrivial_fu2 | 1000000 | 5.934 | 5 | 1.841 |168517551.1