From 91dd2f03b53f0f7194ac99a681d4f44977ab256e Mon Sep 17 00:00:00 2001
From: Kim-J-Smith <3440910457@qq.com>
Date: Thu, 28 May 2026 01:10:20 +0800
Subject: [PATCH 1/9] docs: update the version number and clear release notes.
---
CMakeLists.txt | 2 +-
README.md | 2 +-
docs/release/release_notes.md | 12 ++++--------
include/embed/embed_function.hpp | 2 +-
4 files changed, 7 insertions(+), 11 deletions(-)
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 1f0c2ac..dcabe28 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,6 +1,6 @@
cmake_minimum_required(VERSION 3.14)
-project(embedded_function VERSION 2.1.5 LANGUAGES CXX)
+project(embedded_function VERSION 2.1.6 LANGUAGES CXX)
add_library(embedded_function INTERFACE)
add_library(embedded_function::functions ALIAS embedded_function)
set_target_properties(embedded_function PROPERTIES EXPORT_NAME functions)
diff --git a/README.md b/README.md
index fd0179d..4afb9dd 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,7 @@
# Embedded Function
-
+
diff --git a/docs/release/release_notes.md b/docs/release/release_notes.md
index a39e4fa..7939c75 100644
--- a/docs/release/release_notes.md
+++ b/docs/release/release_notes.md
@@ -1,19 +1,15 @@
## Fixed
-- Fixed a bug where empty and trivial functor could be incorrectly treated as stateless.
-
-- Fixed MSVC 19.10β19.14 compatibility issue.
+-
## Changed
-- `fn_ref` now accepts temporary callable objects (previously disallowed rvalue references).
-
-- `basic_fn` no longer automatically aligns `BufferSize`.
+-
## Added
-- Added CTAD guides for `fn_ref` with `std::constant_wrapper`.
+-
## Notes
-- `std::constant_wrapper` support is experimental as of v2.1.5.
+- `std::constant_wrapper` support is experimental as of v2.1.6.
diff --git a/include/embed/embed_function.hpp b/include/embed/embed_function.hpp
index f72432b..92db107 100644
--- a/include/embed/embed_function.hpp
+++ b/include/embed/embed_function.hpp
@@ -3,7 +3,7 @@
*
* @date 2026-2-7
*
- * @version 2.1.5
+ * @version 2.1.6
*
* @copyright Copyright (c) 2026 Kim-J-Smith
* All rights reserved.
From f894aab4fb97a9e0e14b5fff5b41ff1e025ddf90 Mon Sep 17 00:00:00 2001
From: Kim-J-Smith <3440910457@qq.com>
Date: Thu, 28 May 2026 14:58:25 +0800
Subject: [PATCH 2/9] docs: update README.md and make_fn.md .
---
README.md | 10 +--
docs/api/make_fn.md | 205 ++++++++++++++++++++++++++------------------
2 files changed, 125 insertions(+), 90 deletions(-)
diff --git a/README.md b/README.md
index 4afb9dd..91a68da 100644
--- a/README.md
+++ b/README.md
@@ -193,11 +193,11 @@ auto f = ebd::make_fn(Ambiguous_Callable_Object);
```cpp
// Create specified function wrapper and automatically deduce the template arguments.
-// The Callable_Object should be unambiguously callable (non-overload).
-auto f = ebd::make_fn(Callable_Object);
-auto f = ebd::make_fn(Callable_Object);
-auto f = ebd::make_fn(Callable_Object);
-auto f = ebd::make_fn(Callable_Object);
+// The Callable_Object should be unambiguously callable (non-overload) if `Signature` is omitted.
+auto f = ebd::make_fn(Callable_Object);
+auto f = ebd::make_fn(Callable_Object);
+auto f = ebd::make_fn(Callable_Object);
+auto f = ebd::make_fn(Callable_Object);
```
```cpp
diff --git a/docs/api/make_fn.md b/docs/api/make_fn.md
index aee405f..32d9796 100644
--- a/docs/api/make_fn.md
+++ b/docs/api/make_fn.md
@@ -2,152 +2,187 @@
## Overview
-`ebd::make_fn` is a factory function that creates function wrapper objects. It automatically deduces the signature and buffer size of the callable object, and returns an appropriate function wrapper (`ebd::fn` or `ebd::unique_fn`).
+`ebd::make_fn` is a factory for creating Embedded Function wrappers from callable objects, function pointers, member pointers, and existing wrappers.
+
+It can:
+
+- deduce the signature of a lambda or other uniquely callable functor,
+- choose `ebd::fn` or `ebd::unique_fn` automatically,
+- build an empty wrapper with an explicit signature,
+- create a wrapper with a specific wrapper type such as `ebd::safe_fn` or `ebd::fn_ref`.
## Overloads
-### 1. Make function with specified signature for copyable functor
+### 1. Copyable functor with explicit signature
```cpp
-template
-EMBED_NODISCARD inline fn
-make_fn(Functor&& functor) noexcept;
+template ,
+ std::size_t BufferSize = sizeof(Class),
+ typename Fn = detail::conditional_t<
+ std::is_copy_constructible::value,
+ fn,
+ unique_fn>,
+ bool NoThrow = detail::is_nothrow_construct_from_functor::value>
+EMBED_NODISCARD inline Fn make_fn(Functor&& functor) noexcept(NoThrow);
```
-Creates an `ebd::fn` with the specified signature for a copyable functor.
+Creates a wrapper for a class-type callable when the signature is specified explicitly. For copyable functors, the resulting type is `ebd::fn`.
-### 2. Make function with specified signature for move-only functor
+### 2. Move-only functor with explicit signature
```cpp
-template
+template ::value>
EMBED_NODISCARD inline unique_fn
-make_fn(Functor&& functor) noexcept;
+make_fn(Functor&& functor) noexcept(NoThrow);
```
-Creates an `ebd::unique_fn` with the specified signature for a move-only functor.
+Creates an `ebd::unique_fn` for a move-only functor when the signature is specified explicitly.
-### 3. Make an empty function with specified signature and buffer size
+### 3. Empty wrapper with explicit signature
```cpp
-template
+template
EMBED_NODISCARD inline fn
make_fn(std::nullptr_t = nullptr) noexcept;
```
-Creates an empty `ebd::fn` with the specified signature and buffer size.
+Creates an empty `ebd::fn` with the given signature and buffer size.
-### 4. Make function for function pointer (auto deduce signature and buffer size)
+### 4. Function pointer with deduced signature
```cpp
template
EMBED_NODISCARD inline fn
-make_fn(Ret (*func_ptr) (Args...)) noexcept;
+make_fn(Ret (*func_ptr)(Args...)) noexcept;
```
-Creates an `ebd::fn` from a function pointer, automatically deducing the signature and buffer size.
+Creates an `ebd::fn` from a free-function pointer and deduces both signature and buffer size.
-### 5. Make function for function pointer with specified signature
+### 5. Function pointer with explicit signature
```cpp
-template ::pure_sig*>
+template ::pure_sig*>
EMBED_NODISCARD inline fn
make_fn(FunctionPtr func_ptr) noexcept;
```
-Creates an `ebd::fn` from a function pointer with the specified signature.
+Creates an `ebd::fn` from a free-function pointer using the specified signature.
-### 6. Create a function from another function (Copy)
+### 6. Copy from another wrapper
```cpp
template
EMBED_NODISCARD inline detail::function
-make_fn(const detail::function& fn) noexcept;
+make_fn(const detail::function& fn)
+noexcept(Cfg::isView || Cfg::assertNoThrow);
```
-Creates a function wrapper by copying another function wrapper.
+Creates a wrapper by copying another `ebd::detail::function`.
-### 7. Create a function from another function (Move)
+### 7. Move from another wrapper
```cpp
template
EMBED_NODISCARD inline detail::function
-make_fn(detail::function&& fn) noexcept;
+make_fn(detail::function&& fn)
+noexcept(Cfg::isView || Cfg::assertNoThrow);
```
-Creates a function wrapper by moving another function wrapper.
+Creates a wrapper by moving another `ebd::detail::function`.
-### 8. Make a function from lambda or unique-operator() functor
+### 8. Lambda or uniquely callable functor
```cpp
-template ,
+template ,
std::size_t BufferSize = sizeof(Class),
typename Signature = detail::get_unique_signature_t,
typename Fn = detail::conditional_t<
std::is_copy_constructible::value,
- fn, unique_fn
- >>
-EMBED_NODISCARD inline Fn make_fn(Lambda&& fn) noexcept;
+ fn,
+ unique_fn>,
+ bool NoThrow = detail::is_nothrow_construct_from_functor::value>
+EMBED_NODISCARD inline Fn make_fn(Lambda&& fn) noexcept(NoThrow);
```
-Creates a function wrapper from a lambda or functor with a unique `operator()`, automatically deducing the signature and buffer size.
+Creates a wrapper from a lambda or other functor with exactly one viable `operator()`. The signature is deduced automatically.
-### 9. Make function for pointer to member function
+### 9. Pointer to member function
```cpp
template
EMBED_NODISCARD inline auto
make_fn(Ret(Class::* memfunc)(Args...) C V REF NOEXCEPT) noexcept
--> fn, Args...) const, sizeof(memfunc)>;
+-> fn<
+ Ret(detail::get_qualified_with_t, Args...) const,
+ sizeof(memfunc)
+ >;
```
-Creates an `ebd::fn` from a pointer to member function, automatically deducing the signature and buffer size.
+Creates an `ebd::fn` from a pointer to member function. Cv/ref/noexcept qualifiers are preserved in the generated signature family.
-### 10. Make function for member function pointer with specified signature
+### 10. Member function pointer with explicit signature
```cpp
-template ,
+template ,
std::size_t BufferSize = sizeof(MemFuncPtr)>
EMBED_NODISCARD inline fn
make_fn(MemFuncPtr memfunc_ptr) noexcept;
```
-Creates an `ebd::fn` from a member function pointer with the specified signature.
+Creates an `ebd::fn` from a pointer to member function using the specified signature.
-### 11. Make function for pointer to member object
+### 11. Pointer to member object
```cpp
-template
+template ::type>
EMBED_NODISCARD inline auto make_fn(T Class::* ptr_memobj) noexcept
--> fn;
+-> fn;
```
-Creates an `ebd::fn` from a pointer to member object.
+Creates an `ebd::fn` that reads a member object from an instance.
-### 12. In-place make function (C++17+)
+### 12. In-place construction (C++17+)
```cpp
template
-EMBED_NODISCARD inline auto make_fn(std::in_place_type_t, CArgs&&... args) noexcept;
+EMBED_NODISCARD inline auto
+make_fn(std::in_place_type_t, CArgs&&... args)
+noexcept(std::is_nothrow_constructible::value);
template
EMBED_NODISCARD inline auto
-make_fn(std::in_place_type_t, std::initializer_list il, CArgs&&... args) noexcept;
+make_fn(std::in_place_type_t, std::initializer_list il, CArgs&&... args)
+noexcept(std::is_nothrow_constructible&, CArgs...>::value);
```
-Creates a function wrapper by in-place constructing the callable object within the internal buffer.
+Constructs the callable directly inside the wrapper buffer. The returned wrapper type is deduced from the functor.
-### 13. Make function with specified wrapper
+### 13. Explicit wrapper type
```cpp
-template class Fn, typename Functor,
+template class Fn,
+ typename SpecifiedSig = void,
+ typename Functor,
typename Deduction = decltype(make_fn(std::declval())),
- typename Signature = typename detail::is_ebd_fn::signature,
+ typename RawSig = typename detail::is_ebd_fn::signature,
+ typename Signature = detail::conditional_t<
+ std::is_void::value,
+ detail::noexcept_qualify_like_t,
+ SpecifiedSig>,
std::size_t BufferSize = sizeof(detail::decay_t),
- typename FnWrapper = Fn>
-EMBED_NODISCARD inline FnWrapper make_fn(Functor&& functor) noexcept;
+ typename FnWrapper = Fn,
+ bool NoThrow = noexcept(FnWrapper(std::declval()))>
+EMBED_NODISCARD inline FnWrapper make_fn(Functor&& functor) noexcept(NoThrow);
```
-Creates a function wrapper with the specified wrapper type (e.g., `ebd::fn`, `ebd::unique_fn`, `ebd::safe_fn`, or `ebd::fn_view`).
+Creates a wrapper with an explicitly chosen wrapper template such as `ebd::fn`, `ebd::unique_fn`, `ebd::safe_fn`, or `ebd::fn_ref`. If `SpecifiedSig` is omitted, the signature is deduced from `make_fn(functor)`.
## Usage Examples
@@ -156,75 +191,75 @@ Creates a function wrapper with the specified wrapper type (e.g., `ebd::fn`, `eb
```cpp
#include "embed/embed_function.hpp"
-// Create a function wrapper from a function pointer
-void foo() { /* do something */ }
+void foo() {}
+
auto fn1 = ebd::make_fn(&foo);
fn1();
-// Create a function wrapper from a lambda
-auto fn2 = ebd::make_fn([]() { /* do something */ });
-fn2();
+auto fn2 = ebd::make_fn([](int x) { return x + 1; });
+int y = fn2(41);
-// Create a function wrapper with specified signature
-auto fn3 = ebd::make_fn([](int x) { /* do something */ });
+auto fn3 = ebd::make_fn([](int x) { /* ... */ });
fn3(42);
-// Create an empty function wrapper
auto fn4 = ebd::make_fn();
if (!fn4) {
- // Handle empty function
+ // Handle empty wrapper
}
```
-### With Member Functions
+### Member Pointers
```cpp
struct MyClass {
- void method(int x) { /* do something */ }
+ void method(int x) { value += x; }
int value = 42;
};
MyClass obj;
-// Create a function wrapper for a member function
auto mem_fn = ebd::make_fn(&MyClass::method);
-mem_fn(obj, 100); // Call obj.method(100)
+mem_fn(obj, 8);
-// Create a function wrapper for a member variable
auto mem_obj = ebd::make_fn(&MyClass::value);
-int val = mem_obj(obj); // Get obj.value
+int value = mem_obj(obj);
```
-### With Custom Wrapper Type
+### Explicit Wrapper Type
```cpp
-// Create a safe_fn from a lambda
-auto safe_fn = ebd::make_fn([]() noexcept { /* do something */ });
+auto safe = ebd::make_fn([]() noexcept { /* ... */ });
+
+void bar() {}
+auto ref = ebd::make_fn(&bar);
-// Create a fn_view from a function pointer
-void bar() { /* do something */ }
-auto view = ebd::make_fn(&bar);
+auto ref2 = ebd::make_fn(bar);
```
-### In-place Construction (C++17+)
+### In-place Construction
```cpp
-// In-place construct a functor within the function wrapper
-auto fn = ebd::make_fn(std::in_place_type, /* constructor arguments */);
+struct Functor {
+ explicit Functor(int x) : value(x) {}
+ int operator()() const { return value; }
+ int value;
+};
+
+auto fn = ebd::make_fn(std::in_place_type, 42);
+int value = fn();
```
## Notes
-- `ebd::make_fn` automatically deduces the appropriate function wrapper type based on the callable object:
- - Returns `ebd::fn` for copyable callables
- - Returns `ebd::unique_fn` for move-only callables
-- The buffer size is automatically set to the size of the callable object.
-- For ambiguous callables (e.g., overloaded functions), you must specify the signature explicitly.
-- If `ebd::make_fn` cannot deduce an appropriate function wrapper, it will trigger a static assertion with a helpful error message.
+- `ebd::make_fn` returns `ebd::fn` for copyable deduced callables and `ebd::unique_fn` for move-only deduced callables.
+- When the callable is ambiguous, such as an overloaded function, specify the signature explicitly.
+- The explicit-wrapper overload accepts `ebd::fn`, `ebd::unique_fn`, `ebd::safe_fn`, and `ebd::fn_ref`.
+- `ebd::fn_view` is still available as a deprecated alias of `ebd::fn_ref`.
+- When deduction fails, the fallback overload triggers a static assertion with guidance.
## See Also
-- [`ebd::fn`](./fn.md) - Copyable function wrapper
-- [`ebd::unique_fn`](./unique_fn.md) - Move-only function wrapper
-- [`ebd::safe_fn`](./safe_fn.md) - Exception-safe function wrapper
-- [`ebd::fn_ref`](./fn_ref.md) - For non-owning views of callables
\ No newline at end of file
+- [`ebd::fn`](./fn.md) - Copyable owning wrapper
+- [`ebd::unique_fn`](./unique_fn.md) - Move-only owning wrapper
+- [`ebd::safe_fn`](./safe_fn.md) - Non-throwing wrapper
+- [`ebd::fn_ref`](./fn_ref.md) - Non-owning wrapper
From 26148490e0011c5a0ab1a4a568dc4860704adede Mon Sep 17 00:00:00 2001
From: Kim-J-Smith <3440910457@qq.com>
Date: Sun, 31 May 2026 11:53:18 +0800
Subject: [PATCH 3/9] Do some chores (#60)
* style: make_fn_log_error() don't need EMBED_INLINE.
* docs: better make_fn error log.
* chore: EMBED_DETAIL_REQUIRES_IMPL is useless.
* feat: remove useless operator=().
* style: rename check as not_empty.
* docs: update README.md .
* docs: better error information.
* docs: change EMBED_DETAIL_REPORT_IE.
* style: rename is_empty_normal and empty_normal.
* docs: add some comments.
* docs: change the README.md .
* test: enable bug test in GCC16.
---
README.md | 132 +++++++++++++------------------
include/embed/embed_function.hpp | 110 +++++++++-----------------
test/test_fallback_macros.hpp | 4 +
3 files changed, 98 insertions(+), 148 deletions(-)
diff --git a/README.md b/README.md
index 91a68da..13865cd 100644
--- a/README.md
+++ b/README.md
@@ -21,9 +21,11 @@
## π Overview
-*Embedded Function* is a **lightweight** and **no-heap-allocation** function wrapper collection implemented based on the C++11 standard, tailored specifically for embedded systems.
+*Embedded Function* is a **lightweight** and **no-heap-allocation** function wrapper collection implemented based on the C++11 standard, optimized([see below](#-performance-optimization)) for resource-constrained or high-performance environments.
-In only **one** [header file](./include/embed/embed_function.hpp), **4** function wrappers are provided as follows:
+The library is [freestanding](https://en.cppreference.com/w/cpp/freestanding), making it feasible for embedded development or kernel design of an operating system.
+
+In a [single header file](./include/embed/embed_function.hpp), **four** function wrappers are provided as follows:
```cpp
namespace ebd {
@@ -87,7 +89,9 @@ ebd::fn fn_;
> The *`Qualifier`* is used to restrict the callable objects wrapped within `ebd::fn`, rather than `ebd::fn` itself. In other words, the `operator()` of the `ebd::fn` object will be qualified with the `Qualifier` modifier.
-> The *`Buffer size`* is the size used to store the callable object, which can be omitted. If omitted, this parameter will be set to `detail::default_buffer_size::value` by default, which is sufficient to store most common callable objects, including function pointers, simple non-capturing and capturing lambdas, and lightweight custom classes.
+> The *`Buffer size`* is the size used to store the callable object, which can be omitted.
+> If omitted, this parameter will be set to *DefaultSize* by default, which is sufficient to store most common callable objects, including function pointers, simple non-capturing and capturing lambdas, and lightweight custom classes.
+> *If the buffer size is insufficient, a `static_assert` will be triggered.*
## π§ Design goals driving the design
@@ -138,25 +142,19 @@ ebd::fn fn_;
4. **Triviality**: `fn_ref` is trivially copyable (same as `std::function_ref`).
-## π Performance optimization
-
-### Branch elimination
-
-`ebd::fn` / `ebd::unique_fn` / `ebd::safe_fn` / `ebd::fn_ref` completely eliminate runtime checks for empty function states during invocation, significantly boosting performance of frequent function calls.
-
-### Smart forwarding
-
-`ebd::fn` / `ebd::unique_fn` / `ebd::safe_fn` / `ebd::fn_ref` 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.
-
-### Zero-stack overhead
-
-`ebd::fn_ref` occupies no stack space when used as a function parameter; it is passed entirely in registers. This allows the compiler to directly tail-call the wrapped target, removing the cost of an extra stack frame. See [x86_64-asm](./docs/perf/x86_64_gcc_fn_ref_zero_stack.md).
+### Convertibility
-### Stateless elimination
+- `Yes-D`: Convertible and direct wrapping (`To.BufferSize` >= `From.BufferSize`);
+- `Yes-I`: Convertible and indirect wrapping (`To.BufferSize` >= `sizeof(From)`);
+- `Yes-R`: Convertible and non-owning wrapping.
+- `No`: Inconvertible
-`ebd::fn` / `ebd::unique_fn` / `ebd::safe_fn` / `ebd::fn_ref` do not store the functor or its pointer if the functor is stateless (e.g., empty classes with trivial operations). This reduces memory access operations and improves cache efficiency.
-
-> Click [x64-asm](./docs/perf/x86_64_msvc_asm_analysis.md), [rv32-asm](./docs/perf/riscv_gcc_asm_analysis.md) and [arm32-asm](./docs/perf/arm_gcc_asm_analysis.md) to see more details.
+| From \ To | `ebd::fn` | `ebd::unique_fn` | `ebd::safe_fn` | `ebd::fn_ref` |
+| :---: | :---: | :---: | :---: | :---: |
+| `ebd::fn` | Yes-D | Yes-D | No | Yes-R |
+| `ebd::unique_fn` | No | Yes-D | No | Yes-R |
+| `ebd::safe_fn` | Yes-I | Yes-I | Yes-D | Yes-R |
+| `ebd::fn_ref` | Yes-I | Yes-I | Yes-I | Yes-D |
## π§© Automatic deduction
@@ -291,73 +289,53 @@ Every compiler with modern C++11 support should work.
Go to the `/test/` directory, and follow the instructions in [`HOW-TO-TEST.md`](./test/HOW-TO-TEST.md) to run the tests.
-## β±οΈ Benchmark
+## π Performance optimization
+
+### Branch elimination
-Go to the `/benchmark/` directory, and follow the instructions in [`HOW-TO-BENCHMARK.md`](./benchmark/HOW-TO-BENCHMARK.md) to run the tests.
+`ebd::fn` / `ebd::unique_fn` / `ebd::safe_fn` / `ebd::fn_ref` completely eliminate runtime checks for empty function states during invocation, significantly boosting performance of frequent function calls.
-> *( Compiler: `MSVC` Standard: `C++14` Config: `Release` Tool: [picobench](https://github.com/iboB/picobench) )*
+### Smart forwarding
-> **std**: `std::function`, **ebd**: `ebd::fn`, **fu2**: [`fu2::function`](https://github.com/Naios/function2)
+`ebd::fn` / `ebd::unique_fn` / `ebd::safe_fn` / `ebd::fn_ref` 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.
-```md
-## FreeFunction.ScalarParameters:
+### Zero-stack overhead
- 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:
+`ebd::fn_ref` occupies no stack space when used as a function parameter; it is passed entirely in registers. This allows the compiler to directly tail-call the wrapped target, removing the cost of an extra stack frame. See [x86_64-asm](./docs/perf/x86_64_gcc_fn_ref_zero_stack.md).
- 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:
+### Stateless elimination
- 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:
+`ebd::fn` / `ebd::unique_fn` / `ebd::safe_fn` / `ebd::fn_ref` do not store the functor or its pointer if the functor is stateless (e.g., empty classes with trivial operations). This reduces memory access operations and improves cache efficiency.
+
+> Click [x64-asm](./docs/perf/x86_64_msvc_asm_analysis.md), [rv32-asm](./docs/perf/riscv_gcc_asm_analysis.md) and [arm32-asm](./docs/perf/arm_gcc_asm_analysis.md) to see more details.
+
+## β±οΈ Benchmark
+
+**Embedded-Function has 5%~30% performance enhancement over `std::function`.**
+
+> *( `Compiler`: GCC-14 `Standard`: C++14 `Config`: -Os `Tool`: [picobench](https://github.com/iboB/picobench) `fu2`: [function2](https://github.com/Naios/function2) )*
+
+### StdOperatorWrapper.FunctionWrapperAsParams:
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
-```
-
-> See [here](https://github.com/Kim-J-Smith/Embedded-Function/actions/workflows/benchmark.yml) for more benchmark results.
+ `std::function` * | 10000 | 0.090 | 8 | - |111671952.5
+ `fu2::function` | 10000 | 0.176 | 17 | 1.968 | 56744349.7
+ **`ebd::fn`** | 10000 | 0.068 | 6 | 0.758 |147412179.2
+ `fu2::function_view` | 10000 | 0.034 | 3 | 0.379 |294602875.3
+ **`ebd::fn_ref`** | 10000 | 0.034 | 3 | 0.375 |297424305.5
+ `std::function` * | 100000 | 0.895 | 8 | - |111756442.5
+ `fu2::function` | 100000 | 1.765 | 17 | 1.973 | 56644386.5
+ **`ebd::fn`** | 100000 | 0.678 | 6 | 0.758 |147444347.1
+ `fu2::function_view` | 100000 | 0.340 | 3 | 0.380 |294061429.4
+ **`ebd::fn_ref`** | 100000 | 0.308 | 3 | 0.345 |324361494.4
+ `std::function` * | 1000000 | 9.952 | 9 | - |100481295.4
+ `fu2::function` | 1000000 | 17.733 | 17 | 1.782 | 56391833.9
+ **`ebd::fn`** | 1000000 | 6.832 | 6 | 0.686 |146378186.5
+ `fu2::function_view` | 1000000 | 3.420 | 3 | 0.344 |292392274.6
+ **`ebd::fn_ref`** | 1000000 | 3.249 | 3 | 0.326 |307826614.8
+
+> See [here](https://github.com/Kim-J-Smith/Embedded-Function/actions/workflows/benchmark.yml) for more benchmark results. Follow [`HOW-TO-BENCHMARK.md`](./benchmark/HOW-TO-BENCHMARK.md) to run the benchmark in your platform.
## π§ Future learning & evolution reference
diff --git a/include/embed/embed_function.hpp b/include/embed/embed_function.hpp
index 92db107..a43f403 100644
--- a/include/embed/embed_function.hpp
+++ b/include/embed/embed_function.hpp
@@ -204,9 +204,8 @@
/// @brief Similar to `requires` in C++20.
/// Using SFINAE trait `enable_if_t` to require the template arguments.
-#define EMBED_DETAIL_REQUIRES_IMPL(require_condition) \
- ::ebd::detail::enable_if_t<(require_condition), int> = 0
-#define EMBED_DETAIL_REQUIRES(...) EMBED_DETAIL_REQUIRES_IMPL((__VA_ARGS__))
+#define EMBED_DETAIL_REQUIRES(...) \
+ ::ebd::detail::enable_if_t<(__VA_ARGS__), int> = 0
#if defined(_MSC_VER)
# define EMBED_DETAIL_FORCE_EBO __declspec(empty_bases)
@@ -298,8 +297,8 @@
// Guidelines for reporting internal errors.
#define EMBED_DETAIL_REPORT_IE(error) \
- "An internal error has occurred: " error " This is unexpected. " \
- "Please report this bug at ."
+ "An internal library error has occurred: " error " This is unexpected.\n" \
+ "PLEASE report this bug at ."
namespace ebd EMBED_ABI_VISIBILITY(default) {
namespace detail {
@@ -799,7 +798,8 @@ inline namespace fn_traits {
struct always_false { static constexpr bool value = false; };
// Is trivial for the purposes of calls. (trivially destruct, copy and move)
- // See .
+ // See
+ // and .
template
struct is_call_trivial : public bool_constant<
std::is_trivially_destructible::value
@@ -1163,25 +1163,25 @@ inline namespace fn_traits {
// Utility struct to check if a callable object is not empty.
struct check_not_empty {
template
- static constexpr bool check(T* f) noexcept { return f != nullptr; }
+ static constexpr bool not_empty(T* f) noexcept { return f != nullptr; }
template
- static constexpr bool check(T Class::* f) noexcept { return f != nullptr; }
+ static constexpr bool not_empty(T Class::* f) noexcept { return f != nullptr; }
template
- static constexpr bool check(const T&) noexcept { return true; }
+ static constexpr bool not_empty(const T&) noexcept { return true; }
template
- static bool check(const ::std::function& f) noexcept
+ static bool not_empty(const ::std::function& f) noexcept
{ return static_cast(f); }
template static
- EMBED_CXX14_CONSTEXPR bool check(const function& f) noexcept
+ EMBED_CXX14_CONSTEXPR bool not_empty(const function& f) noexcept
{ return static_cast(f); }
#if __cpp_lib_move_only_function >= 202110L
template
- static bool check(const ::std::move_only_function& f) noexcept
+ static bool not_empty(const ::std::move_only_function& f) noexcept
{ return static_cast(f); }
#endif // ^^^ __cpp_lib_move_only_function >= 202110L
@@ -1189,7 +1189,7 @@ inline namespace fn_traits {
#if __cpp_lib_copyable_function >= 202306L
template
- static bool check(const ::std::copyable_function& f) noexcept
+ static bool not_empty(const ::std::copyable_function& f) noexcept
{ return static_cast(f); }
#endif // ^^^ __cpp_lib_copyable_function >= 202306L
@@ -1503,8 +1503,14 @@ inline namespace fn_traits {
struct asserts_for_function : public std::true_type {
static_assert(align_size_is_ok::value,
- "The size of Functor is too large, and the BufferSize is too small."
- " Try use greater 'BufferSize' as the template argument");
+ "The `BufferSize` is smaller than the callable object. Please use bigger "
+ "`BufferSize` and try again:\n\n"
+ " FnWrapper f = CallableObject;\n"
+ " ^^^^^^^^^^^^^^^^^\n"
+ " |\n"
+ " should be greater than `sizeof(CallableObject)`\n\n"
+ "`FnWrapper` can be `ebd::fn`, `ebd::unique_fn`, `ebd::safe_fn`, etc."
+ );
static_assert(assert_throwing_is_ok::value,
"The 'Functor' may throw exceptions during construction and destruction,"
@@ -1666,7 +1672,7 @@ inline namespace fn_traits {
// Lambda has trivially default constructor since C++20.
// See .
template
- struct is_empty_normal : bool_constant<
+ struct is_empty_trivial : bool_constant<
std::is_empty::value && std::is_trivially_default_constructible::value
&& std::is_trivially_destructible::value
> {};
@@ -1676,25 +1682,29 @@ inline namespace fn_traits {
struct is_stateless : bool_constant<
is_static_callable_functor::value
|| is_std_op_wrapper::value
- || (is_empty_normal::value && !IsView)
+ || (is_empty_trivial::value && !IsView)
// ^^^ empty trivial functor may use `this` in operator(). This is
// not strict stateless and cannot be used in reference semantic.
> {};
// Log error for make_fn.
template
- EMBED_INLINE constexpr bool make_fn_log_error() noexcept {
+ constexpr bool make_fn_log_error() noexcept {
static_assert(always_false::value,
- "`make_fn()` CANNOT infer the template arguments of `ebd::basic_fn` by given arguments.\n"
- "You can specified the signature and try again:\n\n"
+ "`make_fn()` CANNOT infer the template arguments of `ebd::basic_fn` from the given "
+ "arguments.\nYou can specify the signature and try again:\n\n"
" auto f = ebd::make_fn(CallableObject);\n"
- " auto f = ebd::make_fn(CallableObject);\n\n"
+ " ^^^^^^^^^\n"
+ " auto f = ebd::make_fn(CallableObject);\n"
+ " ^^^^^^^^^\n\n"
"The `Signature` is like `void()`, `float(int,int) const`;\n"
"The `FnWrapper` is an alias of `ebd::basic_fn` and has `template ` "
- "as template arguments list, such as `ebd::fn_ref`, `ebd::safe_fn`, etc."
+ "as a template argument list, such as `ebd::fn_ref`, `ebd::safe_fn`, etc. If omitted, "
+ "the `FnWrapper` will be inferred to be `ebd::fn` if the `CallableObject` is copyable, "
+ "and `ebd::unique_fn` otherwise."
);
return true;
- };
+ }
// `true` if Cfg::assertNoThrow || Cfg::isView
template
@@ -1886,7 +1896,7 @@ namespace invocation {
}; \
\
/* Used for stateless(empty) Functor (like std::less). */ \
- struct empty_normal { \
+ struct empty_trivial_class { \
template \
static Ret invoke(erasure_pass_t, smart_forward_t... args) { \
C V EmptyFn fn{}; /* Empty and trivial class. It is stateless */ \
@@ -2139,7 +2149,7 @@ namespace command {
using invoker_impl_target_t = conditional_t<
is_static_callable_functor::value,
typename invoker_impl_t::static_call,
- typename invoker_impl_t::empty_normal
+ typename invoker_impl_t::empty_trivial_class
>;
m_invoker = &invoker_impl_target_t::template invoke;
m_manager = &manager_impl_t::empty::manage;
@@ -2221,7 +2231,7 @@ namespace command {
using invoker_impl_target_t = conditional_t<
is_static_callable_functor::value,
typename invoker_impl_t::static_call,
- typename invoker_impl_t::empty_normal
+ typename invoker_impl_t::empty_trivial_class
>;
m_invoker = &invoker_impl_target_t::template invoke;
}
@@ -2380,7 +2390,7 @@ namespace crtp_mixins {
copy_impl& operator=(const copy_impl& other_raw) noexcept(Config::assertNoThrow) {
auto& other = static_cast(other_raw);
if (!other.is_empty() && this != std::addressof(other)) {
- Self(other).swap(static_cast(*this));
+ Self(other).swap(static_cast(*this)); // TODO: avoid using `swap`.
}
return *this;
}
@@ -2751,8 +2761,7 @@ namespace crtp_mixins {
// Suppress GCC warning: "-Wmaybe-uninitialized".
std::memset(&m_erasure, 0, sizeof(void*));
- other.m_command.clone(
- &m_erasure, const_cast(&other.m_erasure));
+ other.m_command.clone(&m_erasure, const_cast(&other.m_erasure));
std::memcpy(&m_command, &other.m_command, sizeof(command_t));
}
@@ -2792,7 +2801,7 @@ namespace crtp_mixins {
BufferSize, Config, Signature, Functor, Functor&&, erasure_t>::value,
EMBED_DETAIL_REPORT_IE("asserts_for_function<...>::value should be always true."));
- if (check_not_empty::check(functor)) {
+ if (check_not_empty::not_empty(functor)) {
m_command.template init<>(&m_erasure, std::forward(functor));
} else {
m_command.set_empty();
@@ -2947,46 +2956,6 @@ namespace crtp_mixins {
#endif
- // Assign a callable object to the object.
- EMBED_DETAIL_TEMPLATE_BEGIN(typename Functor)
- EMBED_DETAIL_REQUIRES_END(
- (!fn_can_convert::value)
- && (!is_self::value)
- && (!Config::isView)
- ) function& operator=(Functor&& fn)
- noexcept(is_nothrow_construct_from_functor::value) {
- /// Call move assignment in @e `crtp_mixins::move_impl`.
- *this = function(std::forward(fn));
- return *this;
- }
-
- // Assign another `function` object to this object.
- // Enable if the `function` object can be converted to the current object.
- EMBED_DETAIL_TEMPLATE_BEGIN(
- std::size_t OtherSize, typename OtherCfg, typename OtherSig)
- EMBED_DETAIL_REQUIRES_END(
- fn_can_convert>::value
- && (!Config::isView) // OWNING
- )
- function& operator=(const function& other)
- noexcept(is_cfg_noexcept::value && is_cfg_noexcept::value) {
- function(other).swap(*this);
- return *this;
- }
-
- // Assign another `function` object to this object.
- // Enable if the `function` object can be converted to the current object.
- EMBED_DETAIL_TEMPLATE_BEGIN(
- std::size_t OtherSize, typename OtherCfg, typename OtherSig)
- EMBED_DETAIL_REQUIRES_END(
- fn_can_convert>::value
- && Config::isView && OtherCfg::isView // NON-OWNING
- )
- function& operator=(const function& other) noexcept {
- std::memcpy(&m_erasure, &other.m_erasure, default_buffer_size::ref_buf);
- std::memcpy(&m_command, &other.m_command, sizeof(command_t));
- return *this;
- }
};
// `true` if the wrapper has no target, `false` otherwise. (noexcept)
@@ -3483,7 +3452,6 @@ namespace detail {
#undef EMBED_DETAIL_FN_EXPAND
#undef EMBED_DETAIL_FN_EXPAND_IMPL
#undef EMBED_DETAIL_REQUIRES
-#undef EMBED_DETAIL_REQUIRES_IMPL
#undef EMBED_DETAIL_FORCE_EBO
#undef EMBED_DETAIL_VIRTUAL_INHERITANCE
#undef EMBED_DETAIL_MOVE_FUNCTION
diff --git a/test/test_fallback_macros.hpp b/test/test_fallback_macros.hpp
index 3fc555d..f22e06a 100644
--- a/test/test_fallback_macros.hpp
+++ b/test/test_fallback_macros.hpp
@@ -15,6 +15,10 @@
#endif // defined(EBD_TEST_USE_FALLBACK)
+#if defined(__GNUC__) && __GNUC__ >= 16
+# define EBD_TEST_TRY_BUG__GCC_106067
+#endif
+
#if defined(__clang__) && __clang_major__ >= 22
# define EBD_TEST_TRY_BUG__Clang_SameNameStaticFunction
#endif
From 30dbbcd07681918544daf327dd68667c954ffeb9 Mon Sep 17 00:00:00 2001
From: Kim-J-Smith <3440910457@qq.com>
Date: Sun, 31 May 2026 20:42:30 +0800
Subject: [PATCH 4/9] feat: no swap in copy assignment. (#61)
* feat: no swap in copy assignment.
---
include/embed/embed_function.hpp | 18 +++++++++++++-----
1 file changed, 13 insertions(+), 5 deletions(-)
diff --git a/include/embed/embed_function.hpp b/include/embed/embed_function.hpp
index a43f403..7cc9d78 100644
--- a/include/embed/embed_function.hpp
+++ b/include/embed/embed_function.hpp
@@ -2357,11 +2357,12 @@ namespace crtp_mixins {
auto&& other = static_cast(other_raw);
using command_t = typename Self::command_t;
- // Clear and move from `other` to `self`.
- self.clear();
- if (!other.is_empty() && this != std::addressof(other)) {
+ if (this != std::addressof(other_raw)) {
+ self.m_command.destroy(&self.m_erasure);
+
other.m_command.move(&self.m_erasure, &other.m_erasure);
std::memcpy(&self.m_command, &other.m_command, sizeof(command_t));
+
other.m_command.destroy(&other.m_erasure);
other.m_command.set_empty();
}
@@ -2388,9 +2389,16 @@ namespace crtp_mixins {
}
copy_impl& operator=(const copy_impl& other_raw) noexcept(Config::assertNoThrow) {
+ auto& self = static_cast(*this);
auto& other = static_cast(other_raw);
- if (!other.is_empty() && this != std::addressof(other)) {
- Self(other).swap(static_cast(*this)); // TODO: avoid using `swap`.
+ using erasure_t = typename Self::erasure_t;
+ using command_t = typename Self::command_t;
+
+ if (this != std::addressof(other_raw)) {
+ self.m_command.destroy(&self.m_erasure);
+
+ other.m_command.clone(&self.m_erasure, const_cast(&other.m_erasure));
+ std::memcpy(&self.m_command, &other.m_command, sizeof(command_t));
}
return *this;
}
From de98514187316ef4c4b3011a396ebff17bd41e30 Mon Sep 17 00:00:00 2001
From: Kim-J-Smith <3440910457@qq.com>
Date: Fri, 5 Jun 2026 21:02:56 +0800
Subject: [PATCH 5/9] Add specialization for `is_reg_passible` for Windows x64.
(#62)
* docs: update chapter "Wrapper definition syntax" in README.md .
* docs: shorter example.
* docs: rename is_call_trivial as is_trivial_for_call.
* feat: add win_x64 'is_reg_passable' specialization.
---
README.md | 30 +++++++++++++++++++-----------
include/embed/embed_function.hpp | 25 ++++++++++++++++---------
2 files changed, 35 insertions(+), 20 deletions(-)
diff --git a/README.md b/README.md
index 13865cd..3ca023b 100644
--- a/README.md
+++ b/README.md
@@ -78,20 +78,28 @@ auto main() -> int {
```cpp
/// The definition of method of a function wrapper is as follows:
-ebd::fn fn_;
-// ^ ^ ^ ^ ^ ^
-// | | | | | |
-// Return type | | | | |
-// Parameters ~|~~~~~|~~~~~| | |
-// Qualifier ~~~~~~~~~~~~~~~~~~~~| |
-// Buffer size ~~~~~~~~~~~~~~~~~~~~~~~~~~~|
+ FnWrapper fn_ = +[](int, char) {};
+// ^ ^ ^~~~~~~ ^ ^~~~~~ ^~~~~~~~~~~~~
+// | | | | | |
+// Function wrapper | | | | |
+// Return type ~~~~~| | | | |
+// Parameters ~~~~~~~~~~| | | |
+// Qualifier ~~~~~~~~~~~~~~~~~~~~~~~~| | |
+// Buffer size ~~~~~~~~~~~~~~~~~~~~~~~~~~~~| |
+// Callable object ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~|
```
-> The *`Qualifier`* is used to restrict the callable objects wrapped within `ebd::fn`, rather than `ebd::fn` itself. In other words, the `operator()` of the `ebd::fn` object will be qualified with the `Qualifier` modifier.
+- *`Function wrapper`*: One of `ebd::fn`, `ebd::unique_fn`, `ebd::safe_fn` and `ebd::fn_ref`.
-> The *`Buffer size`* is the size used to store the callable object, which can be omitted.
-> If omitted, this parameter will be set to *DefaultSize* by default, which is sufficient to store most common callable objects, including function pointers, simple non-capturing and capturing lambdas, and lightweight custom classes.
-> *If the buffer size is insufficient, a `static_assert` will be triggered.*
+- *`Return type`*: A type that can be implicitly converted from the direct return type of *`Callable object`*.
+
+- *`Parameters`*: Types that can implicitly converts to the parameter types of *`Callable object`*.
+
+- *`Qualifier`*: Applies to the wrapper's `operator()` (e.g., `const`, `noexcept`, `&`, `&&`), restricting which callable objects can be stored.
+
+- *`Buffer size`*: Size (in bytes) of the internal storage. Defaults to `DefaultSize`. Triggers `static_assert` if insufficient - no heap allocation.
+
+- *`Callable object`*: Any entity callable with the target signature (function pointer, lambda, function object, `std::reference_wrapper`). Copied or moved into the buffer depending on wrapper type.
## π§ Design goals driving the design
diff --git a/include/embed/embed_function.hpp b/include/embed/embed_function.hpp
index 7cc9d78..a16294b 100644
--- a/include/embed/embed_function.hpp
+++ b/include/embed/embed_function.hpp
@@ -801,10 +801,10 @@ inline namespace fn_traits {
// See
// and .
template
- struct is_call_trivial : public bool_constant<
+ struct is_trivial_for_call : public bool_constant<
std::is_trivially_destructible::value
- && std::is_trivially_copy_constructible::value
- && std::is_trivially_move_constructible::value
+ && std::is_trivially_copy_constructible::value
+ && std::is_trivially_move_constructible::value
> {};
// std::is_trivial is deprecated in C++26. But we need it.
@@ -1464,17 +1464,24 @@ inline namespace fn_traits {
// optimization for each platform, we create `is_reg_passable`.
template
struct is_reg_passable {
- static constexpr std::size_t reg_size = sizeof(void*);
static constexpr std::size_t obj_size = sizeof(T);
- static constexpr bool is_trivial_obj = is_call_trivial::value;
+ static constexpr bool is_trivial_obj = is_trivial_for_call::value;
static constexpr bool is_scalar_obj = std::is_scalar::value;
+
#if defined(__sparc_v8__) || defined(__sparcv8)
- // class and union object are not allowed to pass by reg in SPARC V8 (32bit).
- static constexpr bool value = is_scalar_obj;
+ // Class and union object are not allowed to pass by reg in SPARC V8 (32bit).
+ static constexpr bool aggregate_passable = false;
+#elif defined(_WIN64) && defined(_M_X64)
+ static_assert(sizeof(void*) == 8, EMBED_DETAIL_REPORT_IE("sizeof(void*) != 8 in Windows x64."));
+ // See .
+ static constexpr bool aggregate_passable = is_trivial_obj
+ && (obj_size == 1 || obj_size == 2 || obj_size == 4 || obj_size == 8);
#else
- static constexpr bool value =
- is_scalar_obj || (obj_size <= 2 * reg_size && is_trivial_obj);
+ static constexpr bool aggregate_passable = is_trivial_obj
+ && obj_size <= 2 * sizeof(void*);
#endif
+
+ static constexpr bool value = is_scalar_obj || aggregate_passable;
};
// Used to choose either perfect forwarding or pass-by-value.
From afd22ded1f3a4fccadc4bbc0803340a9731a47d5 Mon Sep 17 00:00:00 2001
From: Kim-J-Smith <3440910457@qq.com>
Date: Fri, 5 Jun 2026 22:41:44 +0800
Subject: [PATCH 6/9] Refactor `is_callable_from` and `is_invocable_using`.
(#63)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* refactor: refactor `is_callable_from` and `is_invocable_using`.
* fix: fix bug in error "βvalueβ is not a member of βebd::detail::fn_traits::is_invocable_using_impl, int()>β".
* feat: simplify `typename unwrap_signature::template add_cv_like` as `add_cv_like_sig_t`.
---
include/embed/embed_function.hpp | 52 +++++++++++++++++---------------
1 file changed, 28 insertions(+), 24 deletions(-)
diff --git a/include/embed/embed_function.hpp b/include/embed/embed_function.hpp
index a16294b..9be831a 100644
--- a/include/embed/embed_function.hpp
+++ b/include/embed/embed_function.hpp
@@ -1086,7 +1086,7 @@ inline namespace fn_traits {
// Check the functor is callable with given arguments.
// [func.wrap.move.ctor]/1
template
- struct is_callable_from {
+ struct is_callable_from_impl {
using unwrap_sig = unwrap_signature;
using ret = typename unwrap_sig::ret;
using args_pack = typename unwrap_sig::args;
@@ -1549,22 +1549,18 @@ inline namespace fn_traits {
#endif
// .
- template ::pure_sig>
struct is_invocable_using_impl;
template
- struct is_invocable_using_impl, Ret(Args...)> {
- using type = conditional_t<
+ struct is_invocable_using_impl, Ret(Args...)>
+ : conditional_t<
unwrap_signature::isNoexcept,
is_nothrow_invocable_r,
is_invocable_r
- >;
- };
-
- // [func.wrap.ref.ctor]/1 is-invokable-using
- template
- using is_invocable_using_t =
- typename is_invocable_using_impl>::type;
+ >
+ {};
template
struct is_constant_wrapper : std::false_type {};
@@ -2695,6 +2691,17 @@ namespace crtp_mixins {
// `true` if self is copyable.
static constexpr bool internal_is_copyable = Config::isCopyable || Config::isView;
+ // [func.wrap.move.ctor]/1 is-callable-from
+ template
+ using is_callable_from = is_callable_from_impl;
+
+ // [func.wrap.ref.ctor]/1 is-invocable-using
+ template
+ using is_invocable_using = is_invocable_using_impl>;
+
+ template
+ using add_cv_like_sig_t = typename unwrap_signature::template add_cv_like;
+
public:
// The return type.
@@ -2807,7 +2814,7 @@ namespace crtp_mixins {
(!fn_can_convert::value)
&& (!is_self::value)
&& (!is_in_place_type>::value)
- && is_callable_from::value
+ && is_callable_from::value
&& (!Config::isView)
) function(Functor&& functor)
noexcept(is_nothrow_construct_from_functor::value) {
@@ -2829,7 +2836,7 @@ namespace crtp_mixins {
EMBED_DETAIL_TEMPLATE_BEGIN(typename Func)
EMBED_DETAIL_REQUIRES_END(
std::is_function::value
- && is_invocable_using_t::value
+ && is_invocable_using::value
&& Config::isView
) function(Func* function_ptr) noexcept {
@@ -2850,10 +2857,10 @@ namespace crtp_mixins {
/// @note Used for function reference only. (NON-OWNING)
EMBED_DETAIL_TEMPLATE_BEGIN(typename Functor,
typename Tp = remove_reference_t,
- typename Tp_cv = typename unwrap_signature::template add_cv_like)
+ typename Tp_cv = add_cv_like_sig_t)
EMBED_DETAIL_REQUIRES_END(
(!is_self::value)
- && is_invocable_using_t::value
+ && is_invocable_using::value
&& (!std::is_member_pointer::value)
&& (!fn_can_convert::value)
&& Config::isView
@@ -2875,7 +2882,7 @@ namespace crtp_mixins {
EMBED_DETAIL_TEMPLATE_BEGIN(typename Fn, typename... CArgs)
EMBED_DETAIL_REQUIRES_END(
std::is_constructible::value
- && is_callable_from::value
+ && is_callable_from::value
&& (!Config::isView)
) explicit function(std::in_place_type_t, CArgs&&... args)
noexcept(std::is_nothrow_constructible::value) {
@@ -2895,7 +2902,7 @@ namespace crtp_mixins {
EMBED_DETAIL_TEMPLATE_BEGIN(typename Fn, typename U, typename... CArgs)
EMBED_DETAIL_REQUIRES_END(
std::is_constructible&, CArgs...>::value
- && is_callable_from::value
+ && is_callable_from::value
&& (!Config::isView)
) explicit function(std::in_place_type_t, std::initializer_list il, CArgs&&... args)
noexcept(std::is_nothrow_constructible::value) {
@@ -2917,7 +2924,7 @@ namespace crtp_mixins {
// Create function reference with given `std::constant_wrapper` param.
template
- requires is_invocable_using_t::value
+ requires is_invocable_using::value
&& Config::isView
constexpr function(std::constant_wrapper) noexcept
: MemberVariableBase(nullptr) {
@@ -2933,8 +2940,7 @@ namespace crtp_mixins {
// Create function reference with given `std::constant_wrapper` and object params.
template >
requires (!std::is_rvalue_reference_v)
- && is_invocable_using_t::template add_cv_like&>::value
+ && is_invocable_using&>::value
&& Config::isView
constexpr function(std::constant_wrapper, Up&& obj) noexcept
: MemberVariableBase(nullptr) {
@@ -2948,11 +2954,9 @@ namespace crtp_mixins {
}
// Create function reference with given `std::constant_wrapper` and pointer params.
- template ::template add_cv_like
- >
+ template >
requires std::is_convertible_v
- && is_invocable_using_t::value
+ && is_invocable_using::value
&& Config::isView
constexpr function(std::constant_wrapper, Tp* obj) noexcept
: MemberVariableBase(nullptr) {
From 98740a7e83593b36e05935dacb0e58c6de71e049 Mon Sep 17 00:00:00 2001
From: Kim-J-Smith <3440910457@qq.com>
Date: Sat, 6 Jun 2026 23:36:37 +0800
Subject: [PATCH 7/9] style: rename `is_trivial_for_call` as
`is_itanium_trivial_for_calls` and rename `is_reg_passable` as
`is_register_passable`.
(https://github.com/llvm/llvm-project/pull/186692#discussion_r3364547728)
(#64)
---
include/embed/embed_function.hpp | 25 +++++++++++--------------
1 file changed, 11 insertions(+), 14 deletions(-)
diff --git a/include/embed/embed_function.hpp b/include/embed/embed_function.hpp
index 9be831a..aa56dce 100644
--- a/include/embed/embed_function.hpp
+++ b/include/embed/embed_function.hpp
@@ -801,7 +801,7 @@ inline namespace fn_traits {
// See
// and .
template
- struct is_trivial_for_call : public bool_constant<
+ struct is_itanium_trivial_for_calls : public bool_constant<
std::is_trivially_destructible::value
&& std::is_trivially_copy_constructible::value
&& std::is_trivially_move_constructible::value
@@ -1461,27 +1461,24 @@ inline namespace fn_traits {
// MSVC 19.21 and earlier have a bug when using `sizeof(T) <= sizeof(void*)`
// in `conditional_t`. To work around this issue and facilitate targeted
- // optimization for each platform, we create `is_reg_passable`.
+ // optimization for each platform, we create `is_register_passable`.
template
- struct is_reg_passable {
+ struct is_register_passable {
static constexpr std::size_t obj_size = sizeof(T);
- static constexpr bool is_trivial_obj = is_trivial_for_call::value;
- static constexpr bool is_scalar_obj = std::is_scalar