diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml
index 92fd066..c792dfe 100644
--- a/.github/workflows/benchmark.yml
+++ b/.github/workflows/benchmark.yml
@@ -14,6 +14,7 @@ on:
- 'include/**'
- 'benchmark/**'
- '.github/workflows/benchmark.yml'
+ workflow_dispatch:
jobs:
# Ubuntu check
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 7d7827a..9316004 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -6,6 +6,7 @@ on:
pull_request:
branches: [ main, version-* ]
types: [ opened, synchronize, reopened ]
+ workflow_dispatch:
jobs:
# GCC 13
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 51bf941..e76fa64 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -18,7 +18,7 @@ To make sure the process of accepting patches goes smoothly for everyone (especi
3. Do your modifications on that branch. Except for special cases, your contribution should include proper unit tests and documentation.
-4. Make sure your modifications did not break anything by building and running the [tests](./test/HOW-TO-TEST.md).
+4. Make sure your modifications did not break anything by building and running the [tests](./test/README.md).
5. Commit your changes. Your commit message should start with a one line short description of the modifications, with the details and explanations of your modifications following in subsequent paragraphs or bullet points.
diff --git a/README.md b/README.md
index 9331e60..62127bb 100644
--- a/README.md
+++ b/README.md
@@ -3,7 +3,7 @@
-
+
@@ -32,7 +32,7 @@ template
template
class unique_fn; // Wrapper for movable, especially move-only callable objects.
template
- class safe_fn; // Wrapper for copyable callable objects which assert no-throw in Ctor and Dtor.
+ class classic_fn; // Wrapper for copyable callable objects (throws on empty, like std::function).
template
class fn_ref; // View (non-owning wrapper) for callable objects.
}
@@ -89,7 +89,7 @@ auto main() -> int {
// Callable object ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~|
```
-- *`Function wrapper`*: One of `ebd::fn`, `ebd::unique_fn`, `ebd::safe_fn` and `ebd::fn_ref`.
+- *`Function wrapper`*: One of `ebd::fn`, `ebd::unique_fn`, `ebd::classic_fn` and `ebd::fn_ref`.
- *`Return type`*: A type that can be implicitly converted from the direct return type of *`Callable object`*.
@@ -126,7 +126,7 @@ auto main() -> int {
- Provide a view or reference to the callable object, referring to the [`std::function_ref` P0792](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2023/p0792r14.html).
- - Following the above design goals, `ebd::fn`, `ebd::unique_fn`, `ebd::safe_fn` and `ebd::fn_ref` were designed for developers to use.
+ - Following the above design goals, `ebd::fn`, `ebd::unique_fn`, `ebd::classic_fn` and `ebd::fn_ref` were designed for developers to use.
## ✨ Core function wrappers
@@ -134,19 +134,19 @@ auto main() -> int {
| Wrapper Type | Copyable | View (Non-owning) | Throws on Empty Call | Assert No-Throw (Ctor/Dtor) | Buffer Size | Primary Use Case |
| :----------- | :---: | :---: | :---: | :---: | :---: | :---: |
-| [`ebd::fn`](./docs/api/fn.md) | Yes | No | Yes (`std::bad_function_call`) | No | Configurable (aligned, default: `sizeof(void(Class::*)())`) | Copyable callable wrapper |
-| [`ebd::unique_fn`](./docs/api/unique_fn.md) | No | No | Yes (`std::bad_function_call`) | No | Configurable (aligned, default: `sizeof(void(Class::*)())`) | Move-only callable wrapper |
-| [`ebd::safe_fn`](./docs/api/safe_fn.md) | Yes | No | No (`std::terminate()`) | Yes | Configurable (aligned, default: `sizeof(void(Class::*)())`) | Exception-safe copyable callable wrapper |
+| [`ebd::fn`](./docs/api/fn.md) | Yes | No | No (`std::terminate()`) | No | Configurable (aligned, default: `sizeof(void(Class::*)())`) | Copyable callable wrapper |
+| [`ebd::unique_fn`](./docs/api/unique_fn.md) | No | No | No (`std::terminate()`) | No | Configurable (aligned, default: `sizeof(void(Class::*)())`) | Move-only callable wrapper |
+| [`ebd::classic_fn`](./docs/api/classic_fn.md) | Yes | No | Yes (`std::bad_function_call`) | No | Configurable (aligned, default: `sizeof(void(Class::*)())`) | Classic wrapper (like `std::function`) |
| [`ebd::fn_ref`](./docs/api/fn_ref.md) | Yes | Yes | No (*NO EMPTY STATE*) | No | Fixed | Lightweight non-owning reference(view) of callables |
| [`ebd::basic_fn`](./docs/api/basic_fn.md) | - | - | - | - | - | Customized by the user |
### Key takeaways
-1. **Ownership & Copy**: `fn`/`safe_fn` own callables (copyable), `unique_fn` owns but is move-only, `fn_ref` is non-owning (view).
+1. **Ownership & Copy**: `fn`/`classic_fn` own callables (copyable), `unique_fn` owns but is move-only, `fn_ref` is non-owning (view).
-2. **Exception Behavior**: Only `fn`/`unique_fn` throw on empty calls; `safe_fn`/`fn_ref` terminate (no exceptions).
+2. **Exception Behavior**: `fn`/`unique_fn` terminate on empty calls (no exceptions); `classic_fn` throws `std::bad_function_call` (like `std::function`).
-3. **Buffer Configuration**: `fn`/`unique_fn`/`safe_fn` support configurable buffer sizes (aligned), while `fn_ref` uses a fixed buffer (unused template param).
+3. **Buffer Configuration**: `fn`/`unique_fn`/`classic_fn` support configurable buffer sizes (aligned), while `fn_ref` uses a fixed buffer (unused template param).
4. **Triviality**: `fn_ref` is trivially copyable (same as `std::function_ref`).
@@ -157,11 +157,11 @@ auto main() -> int {
- `Yes-R`: Convertible and non-owning wrapping.
- `No`: Inconvertible
-| From \ To | `ebd::fn` | `ebd::unique_fn` | `ebd::safe_fn` | `ebd::fn_ref` |
+| From \ To | `ebd::fn` | `ebd::unique_fn` | `ebd::classic_fn` | `ebd::fn_ref` |
| :---: | :---: | :---: | :---: | :---: |
-| `ebd::fn` | Yes-D | Yes-D | No | Yes-R |
+| `ebd::fn` | Yes-D | Yes-D | Yes-I | Yes-R |
| `ebd::unique_fn` | No | Yes-D | No | Yes-R |
-| `ebd::safe_fn` | Yes-I | Yes-I | Yes-D | Yes-R |
+| `ebd::classic_fn` | Yes-I | Yes-I | Yes-D | Yes-R |
| `ebd::fn_ref` | Yes-I | Yes-I | Yes-I | Yes-D |
## 🧩 Automatic deduction
@@ -202,7 +202,7 @@ auto f = ebd::make_fn(Ambiguous_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);
auto f = ebd::make_fn(Callable_Object);
```
@@ -216,7 +216,7 @@ auto f = ebd::make_fn(std::in_place_type, {/*std::initializer_list*/},
### Brief introduction
-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_ref` to a C-style free function pointer.
+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::classic_fn` / `ebd::fn_ref` 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.
@@ -262,7 +262,7 @@ export namespace ebd {
using ::ebd::basic_fn;
using ::ebd::fn;
using ::ebd::unique_fn;
- using ::ebd::safe_fn;
+ using ::ebd::classic_fn;
using ::ebd::fn_ref;
using ::ebd::make_fn;
}
@@ -276,7 +276,7 @@ import ebd.function;
auto main() -> int {
ebd::fn fn1 = []() { /* ... */ };
ebd::unique_fn fn2 = []() { /* ... */ };
- ebd::safe_fn fn3 = []() { /* ... */ };
+ ebd::classic_fn fn3 = []() { /* ... */ };
ebd::fn_ref fn4 = fn2;
auto fn5 = ebd::make_fn([]() { /* ... */ });
@@ -301,11 +301,11 @@ Go to the `/test/` directory, and follow the instructions in [`test/README
### 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.
+`ebd::fn` / `ebd::unique_fn` / `ebd::classic_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.
+`ebd::fn` / `ebd::unique_fn` / `ebd::classic_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
@@ -313,7 +313,7 @@ Go to the `/test/` directory, and follow the instructions in [`test/README
### Stateless elimination
-`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.
+`ebd::fn` / `ebd::unique_fn` / `ebd::classic_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.
diff --git a/docs/api/basic_fn.md b/docs/api/basic_fn.md
index 9150da9..476da93 100644
--- a/docs/api/basic_fn.md
+++ b/docs/api/basic_fn.md
@@ -68,7 +68,7 @@ view(42);
## Notes
-- Prefer using the predefined aliases (`ebd::fn`, `ebd::unique_fn`, `ebd::safe_fn`, `ebd::fn_view`) unless you need a combination not covered by them.
+- Prefer using the predefined aliases (`ebd::fn`, `ebd::unique_fn`, `ebd::classic_fn`, `ebd::fn_ref`) unless you need a combination not covered by them.
- The buffer size is automatically aligned to the nearest alignment boundary.
- If the callable object is too large for the specified buffer size, a `static_assert` will be triggered at compile time.
@@ -77,5 +77,5 @@ view(42);
- [`ebd::detail::function`](./detail/function.md) - The underlying implementation
- [`ebd::fn`](./fn.md) - For copyable callables
- [`ebd::unique_fn`](./unique_fn.md) - For move-only callables
-- [`ebd::safe_fn`](./safe_fn.md) - For exception-safe callables
+- [`ebd::classic_fn`](./classic_fn.md) - For callables with `std::bad_function_call` on empty
- [`ebd::fn_ref`](./fn_ref.md) - For non-owning views of callables
\ No newline at end of file
diff --git a/docs/api/classic_fn.md b/docs/api/classic_fn.md
new file mode 100644
index 0000000..a343416
--- /dev/null
+++ b/docs/api/classic_fn.md
@@ -0,0 +1,73 @@
+# `ebd::classic_fn`
+
+## Overview
+
+`ebd::classic_fn` is a function object wrapper for copyable and callable objects. It is an alias of `ebd::detail::function` with specific configuration parameters that match the traditional `std::function` behavior: calling an empty wrapper throws `std::bad_function_call`. (if exceptions are enabled)
+
+## Template Parameters
+
+| Parameter | Description |
+|-----------|-------------|
+| `Signature` | Function signature, e.g., `Ret(Args...)` or `Ret(Args...) const`. |
+| `BufferSize` | Buffer size used for storing the callable object. Defaults to `detail::default_buffer_size::value` if omitted. |
+
+## Configuration
+
+`ebd::classic_fn` is configured with the following parameters:
+
+| Configuration | Value | Description |
+|---------------|-------|-------------|
+| `IsCopyable` | `true` | The callable object must be copyable. |
+| `IsView` | `false` | This is not a view; the wrapper owns the callable object. |
+| `IsThrowing` | `true` | The wrapper will throw `std::bad_function_call` when called in an empty state. |
+| `AssertObjectNoThrow` | `false` | The callable object does not need to be nothrow-constructible or nothrow-destructible. |
+
+## Member Functions
+
+All member functions of `ebd::detail::function` are available for `ebd::classic_fn`. For detailed documentation, see [`ebd::detail::function`](./detail/function.md).
+
+## Usage Examples
+
+### Basic Usage
+
+```cpp
+#include "embed/embed_function.hpp"
+
+// Create a classic function wrapper for a void(int) signature
+ebd::classic_fn fn;
+
+// Assign a function pointer
+void foo(int x) { /* do something */ }
+fn = &foo;
+fn(42);
+
+// Assign a lambda
+fn = [](int x) { /* do something */ };
+fn(42);
+```
+
+### With Custom Buffer Size
+
+```cpp
+// Create a classic function wrapper with a custom buffer size
+ebd::classic_fn fn; // 32-byte buffer
+
+// Assign a larger lambda with captures
+int value = 42;
+fn = [value](int x) { /* use value */ };
+fn(100);
+```
+
+## Notes
+
+- `ebd::classic_fn` is copyable and owns the callable object it wraps.
+- The buffer size is automatically aligned to the nearest alignment boundary.
+- If the callable object is too large for the specified buffer size, a `static_assert` will be triggered at compile time.
+- When called in an empty state, `ebd::classic_fn` will throw `std::bad_function_call` (if exceptions are enabled), matching `std::function` behavior.
+
+## See Also
+
+- [`ebd::detail::function`](./detail/function.md) - The underlying implementation
+- [`ebd::fn`](./fn.md) - For copyable callables (terminate on empty)
+- [`ebd::unique_fn`](./unique_fn.md) - For move-only callables
+- [`ebd::fn_ref`](./fn_ref.md) - For non-owning views of callables
diff --git a/docs/api/detail/function.md b/docs/api/detail/function.md
index 4bf5667..a60cc2f 100644
--- a/docs/api/detail/function.md
+++ b/docs/api/detail/function.md
@@ -276,7 +276,7 @@ int result = add(10, 20); // result = 30
## Notes
-- The `ebd::detail::function` class is not intended for direct use. Instead, use the predefined aliases such as `ebd::fn`, `ebd::unique_fn`, `ebd::safe_fn`, and `ebd::fn_view`.
+- The `ebd::detail::function` class is not intended for direct use. Instead, use the predefined aliases such as `ebd::fn`, `ebd::unique_fn`, `ebd::classic_fn`, and `ebd::fn_ref`.
- The buffer size is automatically aligned to the nearest alignment boundary.
- The function wrapper supports all callable objects, including free functions, lambdas, functors, static member functions, and member functions.
- When `Config::isView` is `true`, the wrapper acts as a non-owning view, similar to `std::function_ref`.
\ No newline at end of file
diff --git a/docs/api/fn.md b/docs/api/fn.md
index 1a84206..519a0a5 100644
--- a/docs/api/fn.md
+++ b/docs/api/fn.md
@@ -19,7 +19,7 @@
|---------------|-------|-------------|
| `IsCopyable` | `true` | The callable object must be copyable. |
| `IsView` | `false` | This is not a view; the wrapper owns the callable object. |
-| `IsThrowing` | `true` | The wrapper will throw `std::bad_function_call` when called in an empty state. |
+| `IsThrowing` | `false` | The wrapper will call `std::terminate()` when called in an empty state. |
| `AssertObjectNoThrow` | `false` | The callable object does not need to be nothrow-constructible or nothrow-destructible. |
## Member Functions
@@ -70,11 +70,11 @@ fn(100);
- `ebd::fn` is copyable and owns the callable object it wraps.
- The buffer size is automatically aligned to the nearest alignment boundary.
- If the callable object is too large for the specified buffer size, a `static_assert` will be triggered at compile time.
-- When called in an empty state, `ebd::fn` will throw `std::bad_function_call` (if exceptions are enabled).
+- When called in an empty state, `ebd::fn` will call `std::terminate()`.
## See Also
- [`ebd::detail::function`](./detail/function.md) - The underlying implementation
- [`ebd::unique_fn`](./unique_fn.md) - For move-only callables
-- [`ebd::safe_fn`](./safe_fn.md) - For exception-safe callables
+- [`ebd::classic_fn`](./classic_fn.md) - For callables with `std::bad_function_call` on empty
- [`ebd::fn_ref`](./fn_ref.md) - For non-owning views of callables
\ No newline at end of file
diff --git a/docs/api/fn_ref.md b/docs/api/fn_ref.md
index 3f5ddc1..28837b4 100644
--- a/docs/api/fn_ref.md
+++ b/docs/api/fn_ref.md
@@ -99,8 +99,8 @@ process_data(100, &handle_result);
- `ebd::fn_ref` is a non-owning view, so the underlying callable object must outlive the view.
- `ebd::fn_ref` is trivially copyable and has minimal overhead.
-- The buffer size is fixed to `detail::default_buffer_size::ref_buf`, which is sufficient to store function pointers and member pointers.
-- `ebd::fn_ref` cannot be initialized with rvalue references, as it would create a dangling reference.
+- The buffer size is fixed to `detail::default_buffer_size::ref_buf`, which is sufficient to store function pointers.
+- `ebd::fn_ref` now can be initialized with rvalue references, although it may create a dangling reference. (same as `std::function_ref`)
## Compare `ebd::fn_ref` with `std::function_ref`
@@ -125,4 +125,4 @@ process_data(100, &handle_result);
- [`ebd::detail::function`](./detail/function.md) - The underlying implementation
- [`ebd::fn`](./fn.md) - For copyable callables
- [`ebd::unique_fn`](./unique_fn.md) - For move-only callables
-- [`ebd::safe_fn`](./safe_fn.md) - For exception-safe callables
+- [`ebd::classic_fn`](./classic_fn.md) - For callables with `std::bad_function_call` on empty
diff --git a/docs/api/make_fn.md b/docs/api/make_fn.md
index 32d9796..2772193 100644
--- a/docs/api/make_fn.md
+++ b/docs/api/make_fn.md
@@ -9,7 +9,7 @@ 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`.
+- create a wrapper with a specific wrapper type such as `ebd::classic_fn` or `ebd::fn_ref`.
## Overloads
@@ -18,16 +18,12 @@ It can:
```cpp
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);
+EMBED_NODISCARD inline fn
+make_fn(Functor&& functor) noexcept(NoThrow);
```
-Creates a wrapper for a class-type callable when the signature is specified explicitly. For copyable functors, the resulting type is `ebd::fn`.
+Creates an `ebd::fn` for a class-type callable when the signature is specified explicitly. The buffer size is automatically deduced as `sizeof(Class)`.
### 2. Move-only functor with explicit signature
@@ -61,11 +57,21 @@ make_fn(Ret (*func_ptr)(Args...)) noexcept;
Creates an `ebd::fn` from a free-function pointer and deduces both signature and buffer size.
+### 4b. noexcept function pointer with deduced signature (C++17+)
+
+```cpp
+template
+EMBED_NODISCARD inline fn
+make_fn(Ret (*func_ptr)(Args...) noexcept) noexcept;
+```
+
+Creates an `ebd::fn` from a noexcept free-function pointer. The noexcept qualifier is preserved in the signature. Only available when noexcept is part of the type system (C++17 or `__cpp_noexcept_function_type >= 201510L`).
+
### 5. Function pointer with explicit signature
```cpp
template ::pure_sig*>
+ typename FunctionPtr = typename detail::unwrap_signature::pure_sig_noex*>
EMBED_NODISCARD inline fn
make_fn(FunctionPtr func_ptr) noexcept;
```
@@ -118,7 +124,7 @@ template
EMBED_NODISCARD inline auto
make_fn(Ret(Class::* memfunc)(Args...) C V REF NOEXCEPT) noexcept
-> fn<
- Ret(detail::get_qualified_with_t, Args...) const,
+ Ret(detail::get_qualified_with_t, Args...) const NOEXCEPT,
sizeof(memfunc)
>;
```
@@ -143,10 +149,11 @@ Creates an `ebd::fn` from a pointer to member function using the specified signa
template ::type>
EMBED_NODISCARD inline auto make_fn(T Class::* ptr_memobj) noexcept
--> fn;
+-> fn::value),
+ sizeof(ptr_memobj)>;
```
-Creates an `ebd::fn` that reads a member object from an instance.
+Creates an `ebd::fn` that reads a member object from an instance. The noexcept qualifier is deduced from the member access expression.
### 12. In-place construction (C++17+)
@@ -174,7 +181,7 @@ template class Fn,
typename RawSig = typename detail::is_ebd_fn::signature,
typename Signature = detail::conditional_t<
std::is_void::value,
- detail::noexcept_qualify_like_t,
+ detail::get_correct_signature_t,
SpecifiedSig>,
std::size_t BufferSize = sizeof(detail::decay_t),
typename FnWrapper = Fn,
@@ -182,7 +189,7 @@ template class Fn,
EMBED_NODISCARD inline FnWrapper make_fn(Functor&& functor) noexcept(NoThrow);
```
-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)`.
+Creates a wrapper with an explicitly chosen wrapper template such as `ebd::fn`, `ebd::unique_fn`, `ebd::classic_fn`, or `ebd::fn_ref`. If `SpecifiedSig` is omitted, the signature is deduced from `make_fn(functor)`.
## Usage Examples
@@ -228,7 +235,7 @@ int value = mem_obj(obj);
### Explicit Wrapper Type
```cpp
-auto safe = ebd::make_fn([]() noexcept { /* ... */ });
+auto safe = ebd::make_fn([]() { /* ... */ });
void bar() {}
auto ref = ebd::make_fn(&bar);
@@ -253,7 +260,7 @@ int value = fn();
- `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`.
+- The explicit-wrapper overload accepts `ebd::fn`, `ebd::unique_fn`, `ebd::classic_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.
@@ -261,5 +268,5 @@ int value = fn();
- [`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::classic_fn`](./classic_fn.md) - Classic wrapper with `std::bad_function_call` on empty
- [`ebd::fn_ref`](./fn_ref.md) - Non-owning wrapper
diff --git a/docs/api/safe_fn.md b/docs/api/safe_fn.md
index 3bd2ea9..1c68d55 100644
--- a/docs/api/safe_fn.md
+++ b/docs/api/safe_fn.md
@@ -1,81 +1,20 @@
# `ebd::safe_fn`
-## Overview
+## *Deprecated*
-`ebd::safe_fn` is a safe function object wrapper for copyable and callable objects. It is an alias of `ebd::detail::function` with specific configuration parameters optimized for exception-safe operations.
+Please use [`ebd::fn`](./fn.md) instead.
-## Template Parameters
+### Migration Note
-| Parameter | Description |
-|-----------|-------------|
-| `Signature` | Function signature, e.g., `Ret(Args...)` or `Ret(Args...) const`. |
-| `BufferSize` | Buffer size used for storing the callable object. Defaults to `detail::default_buffer_size::value` if omitted. |
-
-## Configuration
-
-`ebd::safe_fn` is configured with the following parameters:
-
-| Configuration | Value | Description |
-|---------------|-------|-------------|
-| `IsCopyable` | `true` | The callable object must be copyable. |
-| `IsView` | `false` | This is not a view; the wrapper owns the callable object. |
-| `IsThrowing` | `false` | The wrapper will not throw `std::bad_function_call` when called in an empty state; instead, it will call `std::terminate()`. |
-| `AssertObjectNoThrow` | `true` | The callable object must be nothrow-constructible, nothrow-destructible, nothrow-copy-constructible, and nothrow-move-constructible. |
-
-## Member Functions
-
-All member functions of `ebd::detail::function` are available for `ebd::safe_fn`. For detailed documentation, see [`ebd::detail::function`](./detail/function.md).
-
-## Usage Examples
-
-### Basic Usage
-
-```cpp
-#include "embed/embed_function.hpp"
-
-// Create a safe function wrapper for a void(int) signature
-ebd::safe_fn fn;
-
-// Assign a function pointer
-void foo(int x) noexcept { /* do something */ }
-fn = &foo;
-fn(42);
-
-// Assign a noexcept lambda
-fn = [](int x) noexcept { /* do something */ };
-fn(42);
-
-// Assign a noexcept functor
-struct NoThrowFunctor {
- void operator()(int x) noexcept { /* do something */ }
-};
-fn = NoThrowFunctor{};
-fn(42);
-```
-
-### With Custom Buffer Size
+The `safe_fn` alias still works but has been deprecated. If you migrate to `ebd::fn`, note that `AssertObjectNoThrow` changes to `false` — meaning `ebd::fn` no longer requires the callable object to be noexcept-constructible/destructible. If you need the `AssertObjectNoThrow=true` guarantee, use `ebd::basic_fn` directly:
```cpp
-// Create a safe function wrapper with a custom buffer size
-ebd::safe_fn fn; // 32-byte buffer
-
-// Assign a larger noexcept lambda with captures
-int value = 42;
-fn = [value](int x) noexcept { /* use value */ };
-fn(100);
+template
+using my_safe_fn = ebd::basic_fn<
+ Signature, ebd::detail::get_aligned_size(BufferSize),
+ true, // IsCopyable
+ false, // IsView
+ false, // IsThrowing
+ true // AssertObjectNoThrow
+>;
```
-
-## Notes
-
-- `ebd::safe_fn` is copyable and owns the callable object it wraps.
-- The buffer size is automatically aligned to the nearest alignment boundary.
-- The callable object must be noexcept in all operations (construction, destruction, copy, move).
-- If the callable object is too large for the specified buffer size, a `static_assert` will be triggered at compile time.
-- When called in an empty state, `ebd::safe_fn` will call `std::terminate()` instead of throwing an exception.
-
-## See Also
-
-- [`ebd::detail::function`](./detail/function.md) - The underlying implementation
-- [`ebd::fn`](./fn.md) - For copyable callables (non-noexcept)
-- [`ebd::unique_fn`](./unique_fn.md) - For move-only callables
-- [`ebd::fn_ref`](./fn_ref.md) - For non-owning views of callables
\ No newline at end of file
diff --git a/docs/api/unique_fn.md b/docs/api/unique_fn.md
index 3865ce2..3e8a83f 100644
--- a/docs/api/unique_fn.md
+++ b/docs/api/unique_fn.md
@@ -19,7 +19,7 @@
|---------------|-------|-------------|
| `IsCopyable` | `false` | The callable object can be move-only (copyable objects are also accepted but only move operations will be used). |
| `IsView` | `false` | This is not a view; the wrapper owns the callable object. |
-| `IsThrowing` | `true` | The wrapper will throw `std::bad_function_call` when called in an empty state. |
+| `IsThrowing` | `false` | The wrapper will call `std::terminate()` when called in an empty state. |
| `AssertObjectNoThrow` | `false` | The callable object does not need to be nothrow-constructible or nothrow-destructible. |
## Member Functions
@@ -76,11 +76,11 @@ fn(100);
- `ebd::unique_fn` is move-only and owns the callable object it wraps.
- The buffer size is automatically aligned to the nearest alignment boundary.
- If the callable object is too large for the specified buffer size, a `static_assert` will be triggered at compile time.
-- When called in an empty state, `ebd::unique_fn` will throw `std::bad_function_call` (if exceptions are enabled).
+- When called in an empty state, `ebd::unique_fn` will call `std::terminate()`.
## See Also
- [`ebd::detail::function`](./detail/function.md) - The underlying implementation
- [`ebd::fn`](./fn.md) - For copyable callables
-- [`ebd::safe_fn`](./safe_fn.md) - For exception-safe callables
+- [`ebd::classic_fn`](./classic_fn.md) - For callables with `std::bad_function_call` on empty
- [`ebd::fn_ref`](./fn_ref.md) - For non-owning views of callables
\ No newline at end of file
diff --git a/docs/release/release_notes.md b/docs/release/release_notes.md
index 9ef7065..369ab4f 100644
--- a/docs/release/release_notes.md
+++ b/docs/release/release_notes.md
@@ -1,17 +1,21 @@
**🔧 Fixed Bugs**
-- None.
+- `operator*` now returns a `noexcept` function pointer when the stored signature includes `noexcept`, instead of returning `nullptr`.
**⚠️ Breaking Changes**
-- None.
+- `ebd::fn` and `ebd::unique_fn` now call `std::terminate()` on empty call instead of throwing `std::bad_function_call`. If you need `std::bad_function_call` on empty call, use the new `ebd::classic_fn`.
+- `make_fn` now preserves the `noexcept` qualifier when deducing signatures for lambdas and functors (it was previously stripped for `ebd::fn`/`ebd::unique_fn`). For example, `make_fn([]() noexcept {})` now yields `fn` instead of `fn` (Since C++17).
+- `ebd::safe_fn` is deprecated. Use `ebd::fn` for terminate-on-empty, or `ebd::basic_fn` directly if you need the `AssertObjectNoThrow` guarantee.
+- The macro `EMBED_FN_CONFIG_USE_BIG_DEFAULT_BUFFER` has been removed.
**✨ New Features**
-- None.
+- Added `ebd::classic_fn`, a copyable wrapper that throws `std::bad_function_call` on empty call (like `std::function`).
+- Added a `make_fn` overload for `noexcept` function pointers (C++17+), which preserves the `noexcept` qualifier in the wrapper's signature.
+- noexcept qualifiers are now propagated through `make_fn` for member function pointers and member object pointers.
**🛠️ Optimizations and Improvements**
-- None.
+- Refactored noexcept propagation: replaced the complex `noexcept_qualify_like` trait with a simpler `get_correct_signature` trait that decides based on wrapper config (`IsView || !IsThrowing`).
+- Removed `sig_with_noexcept` from `get_unique_signature`; `type` now directly includes `noexcept` when applicable.
+- Removed outdated `@todo` and `@experimental` comments.
**📌 Notes**
-- **The macro `EMBED_FN_CONFIG_USE_BIG_DEFAULT_BUFFER` will be removed in v2.2.0**. If you still rely on it, please inform us in the [issues](https://github.com/Kim-J-Smith/Embedded-Function/issues) section.
-- **The function wrapper `ebd::fn_view` and `ebd::safe_fn` will be removed in v2.2.0**. If you still rely on it, please inform us in the [issues](https://github.com/Kim-J-Smith/Embedded-Function/issues) section.
-- **The function wrapper `ebd::fn` and `ebd::unique_fn` will not throw exceptions on empty calls in v2.2.0**.
-- As of v2.1.11, the usage of `std::constant_wrapper` and `std::meta::is_complete_type` remains **experimental**. These features have been officially adopted in the C++26 standard (`ISO/IEC 14882:2026`) and will become fully stable starting from **v2.2.0**.
\ No newline at end of file
+- None.
\ No newline at end of file
diff --git a/example/basic_usage.cpp b/example/basic_usage.cpp
index 22caa1f..93ccbcb 100644
--- a/example/basic_usage.cpp
+++ b/example/basic_usage.cpp
@@ -1,4 +1,4 @@
-/// basic_usage.cpp — four wrapper types: fn, unique_fn, safe_fn, fn_ref
+/// basic_usage.cpp — four wrapper types: fn, unique_fn, classic_fn, fn_ref
#include
#include
#include