Merge branch 'development' into run-tests

This commit is contained in:
John Wellbelove 2026-04-10 14:17:32 +02:00 committed by GitHub
commit 1151da2b30
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 693 additions and 96 deletions

382
docs/format.md Normal file
View File

@ -0,0 +1,382 @@
# ETL Format & Print
## 1. Overview
ETL provides text formatting facilities modelled on C++20 `std::format` and C++23
`std::print`. They allow type-safe, positional formatting of values into strings
or directly to a character output device — without heap allocation.
**Minimum language standard:** C++11 (`ETL_USING_CPP11`).
**Headers:**
| Header | Provides |
|---|---|
| `etl/format.h` | `etl::format_to`, `etl::format_to_n`, `etl::formatted_size` |
| `etl/print.h` | `etl::print`, `etl::println` (includes `etl/format.h`) |
## 2. `etl::format_to`
### Generic output-iterator overload
```cpp
template<typename OutputIt, class... Args>
OutputIt format_to(OutputIt out, format_string<Args...> fmt, Args&&... args);
```
Formats `args` according to the format string `fmt` and writes the result through
the output iterator `out`. Returns an iterator past the last character written.
`OutputIt` can be any output iterator whose dereferenced type is assignable from
`char`, for example `etl::istring::iterator` or
`etl::back_insert_iterator<etl::istring>`.
```cpp
etl::string<100> s;
// Using a raw iterator — you must resize the string yourself
etl::istring::iterator result = etl::format_to(s.begin(), "{0} {1}", 34, 56);
s.uninitialized_resize(static_cast<size_t>(result - s.begin()));
// s == "34 56"
// Using a back_insert_iterator — string grows automatically
s.clear();
etl::back_insert_iterator<etl::istring> it(s);
etl::format_to(it, "{} {}", 65, 34);
// s == "65 34"
```
### `etl::istring&` overload (ETL-specific)
```cpp
template<class... Args>
etl::istring::iterator format_to(etl::istring& out,
format_string<Args...> fmt,
Args&&... args);
```
Convenience overload that writes into an `etl::istring` (or any derived
`etl::string<N>`). The string is automatically resized to the number of
characters written, up to `out.max_size()`. Returns an iterator past the
last character written.
```cpp
etl::string<100> s;
etl::format_to(s, "Hello, {}!", "world");
// s == "Hello, world!"
```
### `etl::format_to_n`
```cpp
template<typename OutputIt, class... Args>
OutputIt format_to_n(OutputIt out, size_t n,
format_string<Args...> fmt, Args&&... args);
```
Like `format_to`, but writes **at most** `n` characters. Characters beyond the
limit are silently discarded.
```cpp
etl::string<10> s = "abcdefghij";
etl::format_to_n(s.begin(), 3, "xy{}", 123);
// s == "xy1defghij" (only 3 chars written)
```
## 3. `etl::formatted_size`
```cpp
template<class... Args>
size_t formatted_size(format_string<Args...> fmt, Args&&... args);
```
Returns the total number of characters that `format_to` would produce, without
actually writing anything. Useful for pre-computing buffer sizes.
```cpp
size_t n;
n = etl::formatted_size(""); // 0
n = etl::formatted_size("{}", ""); // 0
n = etl::formatted_size("xyz{}", 12); // 5
n = etl::formatted_size("{}", "abc"); // 3
```
## 4. `etl::print` and `etl::println`
Declared in `etl/print.h`.
### `etl::print`
```cpp
template<class... Args>
void print(etl::format_string<Args...> fmt, Args&&... args);
```
Formats the arguments and outputs each character by calling `etl_putchar()`.
### `etl::println`
```cpp
// With arguments — prints formatted text followed by '\n'
template<class... Args>
void println(etl::format_string<Args...> fmt, Args&&... args);
// Without arguments — prints a bare newline
void println();
```
### Implementing `etl_putchar`
`etl/print.h` declares (but does not define) the following C-linkage function:
```cpp
extern "C" void etl_putchar(int c);
```
You **must** provide a definition in your project. The `int` parameter follows
the convention of the standard `putchar()` and carries a single `char` value.
Typical implementations forward to a UART, a debug probe, `putchar`, or any
other single-character output sink:
```cpp
// Example: forward to standard putchar
extern "C" void etl_putchar(int c)
{
putchar(c);
}
```
### Example
```cpp
etl::print("x = {}, y = {}\n", 10, 20); // "x = 10, y = 20\n"
etl::println("Hello, {}!", "world"); // "Hello, world!\n"
etl::println(); // "\n"
```
## 5. Format String Syntax
A format string is ordinary text with **replacement fields** delimited by braces:
```
"literal text {} more text {1:>10} end"
```
### Replacement field grammar
```
replacement_field ::= '{' [arg_id] [':' format_spec] '}'
arg_id ::= integer // e.g. 0, 1, 2 …
format_spec ::= [[fill]align] [sign] ['#'] ['0'] [width] ['.' precision] ['L'] [type]
```
| Component | Syntax | Description |
|---|---|---|
| **Argument index** | `{0}`, `{1}`, … | Manual positional indexing. Cannot be mixed with automatic indexing. |
| **Automatic index** | `{}` | Uses the next argument in order. Cannot be mixed with manual indexing. |
| **Fill character** | any character except `{` or `}` | Used together with an alignment specifier. Default is space (` `). |
| **Alignment** | `<` left, `>` right, `^` center | Aligns the formatted value within the given *width*. |
| **Sign** | `+` always, `-` negative only (default), ` ` space for positive | Controls sign display for numeric types. |
| **`#` (alt form)** | `#` | Adds `0x`/`0X` for hex, `0b`/`0B` for binary, `0` for octal. |
| **`0` (zero-pad)** | `0` | Pads the number with leading zeros (after sign/prefix). |
| **Width** | integer, or `{}` / `{n}` | Minimum field width. Supports nested replacement fields for dynamic width. |
| **Precision** | `.` integer, or `.{}` / `.{n}` | For strings: maximum characters to output. For floats: number of decimal digits. Supports nested replacement fields. |
| **`L`** | `L` | Locale-specific flag (parsed but currently ignored). |
| **Type** | see [Presentation Types](#7-presentation-types-per-argument-kind) | Selects the output representation. |
### Examples
```cpp
etl::format_to(s, "{:>10}", 42); // " 42"
etl::format_to(s, "{:*^10}", 42); // "****42****"
etl::format_to(s, "{:+05d}", 67); // "+00067"
etl::format_to(s, "{:#x}", 0x3f4); // "0x3f4"
etl::format_to(s, "{:.3s}", "abcdef"); // "abc"
etl::format_to(s, "{1} {0}", 1, 2); // "2 1"
```
## 6. Supported Argument Types
The core set of formattable types (matching `std::basic_format_arg`):
| Category | Types |
|---|---|
| Boolean | `bool` |
| Character | `char` |
| Signed integer | `int`, `long long int` |
| Unsigned integer | `unsigned int`, `unsigned long long int` |
| Floating-point *(opt-in)* | `float`, `double`, `long double` — requires `ETL_USING_FORMAT_FLOATING_POINT` |
| String | `const char*`, `etl::string_view` |
| Pointer | `const void*` |
### Implicit conversions
Types not listed above are converted automatically before formatting:
| Source type | Stored as |
|---|---|
| `short` | `int` |
| `unsigned short`, `uint16_t` | `unsigned int` |
| `long int` | `int` or `long long int` (platform-dependent) |
| `unsigned long int`, `size_t` | `unsigned int` or `unsigned long long int` |
| `int8_t` (`signed char`) | `char` |
| `uint8_t` (`unsigned char`) | `char` |
| `int16_t` | `int` |
| `uint32_t` | `unsigned int` |
| `int32_t` | `int` |
| `etl::string<N>` | `etl::string_view` (lifetime of the temporary is guaranteed) |
| any pointer `T*` | `const void*` |
## 7. Presentation Types per Argument Kind
### Integers (`int`, `unsigned int`, `long long int`, `unsigned long long int`)
| Type | Meaning | Example |
|---|---|---|
| `d` *(default)* | Decimal | `134``"134"` |
| `x` | Lowercase hexadecimal | `0x3f4``"3f4"` |
| `X` | Uppercase hexadecimal | `0x3f4``"3F4"` |
| `o` | Octal | `034``"34"` |
| `b` | Lowercase binary | `0b1010``"1010"` |
| `B` | Uppercase binary | `0b1010``"1010"` |
| `c` | Character (value as char) | `67``"C"` |
With `#`: prefixes `0x`/`0X`, `0b`/`0B`, or leading `0` for octal.
### Characters (`char`, `signed char`, `unsigned char`)
| Type | Meaning | Example |
|---|---|---|
| `c` *(default)* | Character itself | `'s'``"s"` |
| `?` | Debug / escaped | `'\n'``"'\\n'"` |
| `d` | Decimal code point | `'a'``"97"` |
| `x` / `X` | Hex code point | `'a'``"61"` |
### Booleans (`bool`)
| Type | Meaning | Example |
|---|---|---|
| *(default)* | `false` / `true` | `true``"true"` |
| `s` | Same as default | `true``"true"` |
| `d` | `0` / `1` | `true``"1"` |
| `x` / `X` | Hex `0` / `1` | `true``"1"` |
| `o` | Octal (with `#`: `01`) | `true``"01"` |
### Strings (`const char*`, `etl::string_view`, `etl::string<N>`)
| Type | Meaning | Example |
|---|---|---|
| `s` *(default)* | String output | `"data1"``"data1"` |
| `?` | Debug / escaped | `"data1\n"``"\"data1\\n\""` |
Width and precision apply: width sets the minimum field width; precision (`.N`)
truncates the string to at most *N* characters.
```cpp
etl::format_to(s, "{:>10s}", "data1"); // " data1"
etl::format_to(s, "{:.3s}", "abcdef"); // "abc"
etl::format_to(s, ".{:^8.3s}!", "data1"); // ". dat !"
```
### Pointers (`const void*`)
| Type | Meaning | Example |
|---|---|---|
| `p` *(default)* | Lowercase hex with `0x` prefix | `nullptr``"0x0"` |
| `P` | Uppercase hex with `0X` prefix | `nullptr``"0X0"` |
### Floating-point (`float`, `double`, `long double`)
Requires `ETL_USING_FORMAT_FLOATING_POINT`.
| Type | Meaning | Example |
|---|---|---|
| *(default)* | Shortest representation | `1.5f``"1.5"` |
| `e` / `E` | Scientific notation | `1.0f``"1.000000e+00"` |
| `f` / `F` | Fixed-point notation | `1.125f``"1.125000"` |
| `g` / `G` | General (fixed or scientific) | `1e10f``"1.000000e+10"` |
| `a` / `A` | Hexadecimal floating-point | `1.5f``"0x1.8p+0"` |
`nan`, `inf` (lowercase for `e`/`f`/`g`/`a`, uppercase for `E`/`F`/`G`/`A`).
## 8. Escape Sequences and Literal Braces
### Literal braces
Because `{` and `}` delimit replacement fields, they must be escaped by
doubling:
| Input | Output |
|---|---|
| `{{` | `{` |
| `}}` | `}` |
```cpp
etl::format_to(s, "abc{{def"); // "abc{def"
etl::format_to(s, "}}abc"); // "}abc"
```
### Debug / escaped presentation (`?`)
The `?` type specifier produces a debug representation:
- **Characters** are wrapped in single quotes with C-style escape sequences:
| Character | Output |
|---|---|
| `\t` | `'\\t'` |
| `\n` | `'\\n'` |
| `\r` | `'\\r'` |
| `"` | `'\\\"'` |
| `'` | `'\\''` |
| `\\` | `'\\\\'` |
- **Strings** are wrapped in double quotes with the same escape sequences:
```cpp
etl::format_to(s, "{:?}", "data1\n"); // "\"data1\\n\""
```
## 9. Error Handling
Invalid format strings cause an `etl::bad_format_string_exception` (derived from
`etl::format_exception`, which is derived from `etl::exception`).
Common error conditions:
| Condition | Example |
|---|---|
| Missing closing brace | `"a{b"` |
| Unescaped `}` without matching `{` | `"a}b"` |
| Invalid characters inside `{}` | `"a{b}"` |
| Argument index out of range | `"{1}"` with only one argument |
| Mixing manual and automatic indexing | `"{0} {}"` |
| Invalid type specifier for the argument | `"{:d}"` on a `string_view` |
| Double colon in format spec | `"{::}"` |
| Precision on an integer | `"{:+#05.5X}"` on an `int` |
```cpp
etl::string<100> s;
// These all throw etl::bad_format_string_exception:
etl::format_to(s, "a{b}", 1); // bad index spec
etl::format_to(s, "a{b", 1); // closing brace missing
etl::format_to(s, "a}b"); // unescaped }
etl::format_to(s, "{:d}", sv); // invalid type for string_view
```
> **Note:** On C++20 and later, compile-time format string validation through
> `consteval` is planned but not yet fully implemented.
## 10. Differences from `std::format`
| Area | `std::format` (C++20/23) | ETL |
|---|---|---|
| **Output target** | Returns `std::string` | Writes through an output iterator or into `etl::istring&` — no heap allocation. |
| **`etl::istring&` overload** | Not available | `format_to(etl::istring&, ...)` automatically resizes the string. |
| **`print` / `println` output** | Writes to `FILE*` / `stdout` | Writes character-by-character via user-defined `etl_putchar(int)`. |
| **Floating-point support** | Always available | Opt-in via `ETL_USING_FORMAT_FLOATING_POINT`. |
| **User-defined formatters** | `std::formatter<T>` specialisations | Not yet supported. |
| **Locale** | `L` flag uses `std::locale` | `L` flag is parsed but has no effect. |
| **Compile-time validation** | Enforced via `consteval` on C++20 | Planned; currently validates at run time and throws `etl::bad_format_string_exception`. |
| **`format_to_n` return type** | `std::format_to_n_result` | Returns the underlying `OutputIt` directly. |

View File

@ -33,53 +33,62 @@ SOFTWARE.
#include "platform.h"
#include "delegate.h"
#include "invoke.h"
#include "tuple.h"
#include "type_list.h"
#include "utility.h"
namespace etl
{
#if ETL_USING_CPP11 && !defined(ETL_CLOSURE_FORCE_CPP03_IMPLEMENTATION)
//*************************************************************************
/// Base template for closure.
/// \tparam TSignature The callback signature.
/// \tparam TCallback The callback type, defaults to etl::delegate<TSignature>.
//*************************************************************************
template <typename>
template <typename TSignature, typename TCallback = etl::delegate<TSignature> >
class closure;
#if ETL_USING_CPP11 && !defined(ETL_CLOSURE_FORCE_CPP03_IMPLEMENTATION)
//*************************************************************************
/// Closure for binding arguments to a delegate and invoking it later.
/// Stores a delegate and its arguments, allowing deferred execution.
/// Closure for binding arguments to a callback and invoking it later.
/// Stores a callback and its arguments, allowing deferred execution.
/// Arguments are stored in a tuple and can be rebound using bind().
/// Example usage:
/// \code
/// etl::closure<void(int, int)> c(my_delegate, 1, 2);
/// c(); // Invokes my_delegate(1, 2)
/// \endcode
/// \tparam TReturn The return type of the delegate.
/// \tparam TArgs The argument types of the delegate.
/// \tparam TReturn The return type of the callback.
/// \tparam TArgs The argument types of the callback.
/// \tparam TCallback The callback type.
//*************************************************************************
template <typename TReturn, typename... TArgs>
class closure<TReturn(TArgs...)>
template <typename TReturn, typename... TArgs, typename TCallback>
class closure<TReturn(TArgs...), TCallback>
{
public:
using delegate_type = etl::delegate<TReturn(TArgs...)>; ///< The delegate type to be invoked.
using argument_types = etl::type_list<TArgs...>; ///< The type list of arguments.
ETL_DEPRECATED_REASON("Use callback_type") using delegate_type =
TCallback; ///< The callback type to be invoked. Deprecated, use callback_type instead.
using callback_type = TCallback; ///< The callback type to be invoked.
using argument_types = etl::type_list<TArgs...>; ///< The type list of arguments.
static_assert(etl::is_invocable_r<TReturn, TCallback, TArgs...>::value, "Callback is not invocable with the specified arguments");
static_assert(etl::is_copy_constructible<TCallback>::value, "Callback type must be copy constructible");
//*********************************************************************
/// Construct a closure with a delegate and its arguments.
/// \param f The delegate to be invoked.
/// \param args The arguments to bind to the delegate.
/// Construct a closure with a callback and its arguments.
/// \param f The callback to be invoked.
/// \param args The arguments to bind to the callback.
//*********************************************************************
ETL_CONSTEXPR14 closure(const delegate_type& f, const TArgs... args)
ETL_CONSTEXPR14 closure(const callback_type& f, const TArgs... args)
: m_f(f)
, m_args(args...)
{
}
//*********************************************************************
/// Invoke the stored delegate with the bound arguments.
/// \return The result of the delegate invocation.
/// Invoke the stored callback with the bound arguments.
/// \return The result of the callback invocation.
//*********************************************************************
ETL_CONSTEXPR14 TReturn operator()() const
{
@ -131,7 +140,7 @@ namespace etl
//*********************************************************************
/// Execute the closure with the stored arguments.
/// \tparam idx Index sequence for tuple unpacking.
/// \return The result of the delegate invocation.
/// \return The result of the callback invocation.
//*********************************************************************
template <size_t... Indexes>
ETL_CONSTEXPR14 TReturn execute(etl::index_sequence<Indexes...>) const
@ -139,43 +148,38 @@ namespace etl
return m_f(etl::get<Indexes>(m_args)...);
}
delegate_type m_f; ///< The delegate to invoke.
callback_type m_f; ///< The callback to invoke.
etl::tuple<TArgs...> m_args; ///< The bound arguments.
};
#else
//*************************************************************************
/// Base template for closure.
//*************************************************************************
template <typename>
class closure;
//*************************************************************************
/// Closure for binding one argument to a delegate and invoking it later.
/// \tparam TReturn The return type of the delegate.
/// Closure for binding one argument to a callback and invoking it later.
/// \tparam TReturn The return type of the callback.
/// \tparam TArg0 The type of the argument.
/// \tparam TCallback The callback type.
//*************************************************************************
template <typename TReturn, typename TArg0>
class closure<TReturn(TArg0)>
template <typename TReturn, typename TArg0, typename TCallback>
class closure<TReturn(TArg0), TCallback>
{
public:
/// The delegate type to be invoked.
typedef etl::delegate<TReturn(TArg0)> delegate_type;
/// The callback type to be invoked.
typedef TCallback callback_type;
//*********************************************************************
/// Construct a closure with a delegate and its argument.
/// \param f The delegate to be invoked.
/// \param arg0 The argument to bind to the delegate.
/// Construct a closure with a callback and its argument.
/// \param f The callback to be invoked.
/// \param arg0 The argument to bind to the callback.
//*********************************************************************
closure(const delegate_type& f, const TArg0 arg0)
closure(const callback_type& f, const TArg0 arg0)
: m_f(f)
, m_arg0(arg0)
{
}
//*********************************************************************
/// Invoke the stored delegate with the bound argument.
/// \return The result of the delegate invocation.
/// Invoke the stored callback with the bound argument.
/// \return The result of the callback invocation.
//*********************************************************************
TReturn operator()() const
{
@ -184,30 +188,31 @@ namespace etl
private:
delegate_type m_f; ///< The delegate to invoke.
callback_type m_f; ///< The callback to invoke.
TArg0 m_arg0;
};
//*************************************************************************
/// Closure for binding two arguments to a delegate and invoking it later.
/// \tparam TReturn The return type of the delegate.
/// Closure for binding two arguments to a callback and invoking it later.
/// \tparam TReturn The return type of the callback.
/// \tparam TArg0 The type of the first argument.
/// \tparam TArg1 The type of the second argument.
/// \tparam TCallback The callback type.
//*************************************************************************
template <typename TReturn, typename TArg0, typename TArg1>
class closure<TReturn(TArg0, TArg1)>
template <typename TReturn, typename TArg0, typename TArg1, typename TCallback>
class closure<TReturn(TArg0, TArg1), TCallback>
{
public:
typedef etl::delegate<TReturn(TArg0, TArg1)> delegate_type;
typedef TCallback callback_type;
//*********************************************************************
/// Construct a closure with a delegate and its arguments.
/// \param f The delegate to be invoked.
/// Construct a closure with a callback and its arguments.
/// \param f The callback to be invoked.
/// \param arg0 The first argument to bind.
/// \param arg1 The second argument to bind.
//*********************************************************************
closure(const delegate_type& f, const TArg0 arg0, const TArg1 arg1)
closure(const callback_type& f, const TArg0 arg0, const TArg1 arg1)
: m_f(f)
, m_arg0(arg0)
, m_arg1(arg1)
@ -215,8 +220,8 @@ namespace etl
}
//*********************************************************************
/// Invoke the stored delegate with the bound arguments.
/// \return The result of the delegate invocation.
/// Invoke the stored callback with the bound arguments.
/// \return The result of the callback invocation.
//*********************************************************************
TReturn operator()() const
{
@ -225,33 +230,34 @@ namespace etl
private:
delegate_type m_f; ///< The delegate to invoke.
callback_type m_f; ///< The callback to invoke.
TArg0 m_arg0;
TArg1 m_arg1;
};
//*************************************************************************
/// Closure for binding three arguments to a delegate and invoking it later.
/// \tparam TReturn The return type of the delegate.
/// Closure for binding three arguments to a callback and invoking it later.
/// \tparam TReturn The return type of the callback.
/// \tparam TArg0 The type of the first argument.
/// \tparam TArg1 The type of the second argument.
/// \tparam TArg2 The type of the third argument.
/// \tparam TCallback The callback type.
//*************************************************************************
template <typename TReturn, typename TArg0, typename TArg1, typename TArg2>
class closure<TReturn(TArg0, TArg1, TArg2)>
template <typename TReturn, typename TArg0, typename TArg1, typename TArg2, typename TCallback>
class closure<TReturn(TArg0, TArg1, TArg2), TCallback>
{
public:
typedef etl::delegate<TReturn(TArg0, TArg1, TArg2)> delegate_type;
typedef TCallback callback_type;
//*********************************************************************
/// Construct a closure with a delegate and its arguments.
/// \param f The delegate to be invoked.
/// Construct a closure with a callback and its arguments.
/// \param f The callback to be invoked.
/// \param arg0 The first argument to bind.
/// \param arg1 The second argument to bind.
/// \param arg2 The third argument to bind.
//*********************************************************************
closure(const delegate_type& f, const TArg0 arg0, const TArg1 arg1, const TArg2 arg2)
closure(const callback_type& f, const TArg0 arg0, const TArg1 arg1, const TArg2 arg2)
: m_f(f)
, m_arg0(arg0)
, m_arg1(arg1)
@ -260,8 +266,8 @@ namespace etl
}
//*********************************************************************
/// Invoke the stored delegate with the bound arguments.
/// \return The result of the delegate invocation.
/// Invoke the stored callback with the bound arguments.
/// \return The result of the callback invocation.
//*********************************************************************
TReturn operator()() const
{
@ -270,36 +276,37 @@ namespace etl
private:
delegate_type m_f; ///< The delegate to invoke.
callback_type m_f; ///< The callback to invoke.
TArg0 m_arg0;
TArg1 m_arg1;
TArg2 m_arg2;
};
//*************************************************************************
/// Closure for binding four arguments to a delegate and invoking it later.
/// \tparam TReturn The return type of the delegate.
/// Closure for binding four arguments to a callback and invoking it later.
/// \tparam TReturn The return type of the callback.
/// \tparam TArg0 The type of the first argument.
/// \tparam TArg1 The type of the second argument.
/// \tparam TArg2 The type of the third argument.
/// \tparam TArg3 The type of the fourth argument.
/// \tparam TCallback The callback type.
//*************************************************************************
template <typename TReturn, typename TArg0, typename TArg1, typename TArg2, typename TArg3>
class closure<TReturn(TArg0, TArg1, TArg2, TArg3)>
template <typename TReturn, typename TArg0, typename TArg1, typename TArg2, typename TArg3, typename TCallback>
class closure<TReturn(TArg0, TArg1, TArg2, TArg3), TCallback>
{
public:
typedef etl::delegate<TReturn(TArg0, TArg1, TArg2, TArg3)> delegate_type;
typedef TCallback callback_type;
//*********************************************************************
/// Construct a closure with a delegate and its arguments.
/// \param f The delegate to be invoked.
/// Construct a closure with a callback and its arguments.
/// \param f The callback to be invoked.
/// \param arg0 The first argument to bind.
/// \param arg1 The second argument to bind.
/// \param arg2 The third argument to bind.
/// \param arg3 The fourth argument to bind.
//*********************************************************************
closure(const delegate_type& f, const TArg0 arg0, const TArg1 arg1, const TArg2 arg2, const TArg3 arg3)
closure(const callback_type& f, const TArg0 arg0, const TArg1 arg1, const TArg2 arg2, const TArg3 arg3)
: m_f(f)
, m_arg0(arg0)
, m_arg1(arg1)
@ -309,8 +316,8 @@ namespace etl
}
//*********************************************************************
/// Invoke the stored delegate with the bound arguments.
/// \return The result of the delegate invocation.
/// Invoke the stored callback with the bound arguments.
/// \return The result of the callback invocation.
//*********************************************************************
TReturn operator()() const
{
@ -319,7 +326,7 @@ namespace etl
private:
delegate_type m_f; ///< The delegate to invoke.
callback_type m_f; ///< The callback to invoke.
TArg0 m_arg0;
TArg1 m_arg1;
TArg2 m_arg2;
@ -327,31 +334,32 @@ namespace etl
};
//*************************************************************************
/// Closure for binding five arguments to a delegate and invoking it later.
/// \tparam TReturn The return type of the delegate.
/// Closure for binding five arguments to a callback and invoking it later.
/// \tparam TReturn The return type of the callback.
/// \tparam TArg0 The type of the first argument.
/// \tparam TArg1 The type of the second argument.
/// \tparam TArg2 The type of the third argument.
/// \tparam TArg3 The type of the fourth argument.
/// \tparam TArg4 The type of the fifth argument.
/// \tparam TCallback The callback type.
//*************************************************************************
template <typename TReturn, typename TArg0, typename TArg1, typename TArg2, typename TArg3, typename TArg4>
class closure<TReturn(TArg0, TArg1, TArg2, TArg3, TArg4)>
template <typename TReturn, typename TArg0, typename TArg1, typename TArg2, typename TArg3, typename TArg4, typename TCallback>
class closure<TReturn(TArg0, TArg1, TArg2, TArg3, TArg4), TCallback>
{
public:
typedef etl::delegate<TReturn(TArg0, TArg1, TArg2, TArg3, TArg4)> delegate_type;
typedef TCallback callback_type;
//*********************************************************************
/// Construct a closure with a delegate and its arguments.
/// \param f The delegate to be invoked.
/// Construct a closure with a callback and its arguments.
/// \param f The callback to be invoked.
/// \param arg0 The first argument to bind.
/// \param arg1 The second argument to bind.
/// \param arg2 The third argument to bind.
/// \param arg3 The fourth argument to bind.
/// \param arg4 The fifth argument to bind.
//*********************************************************************
closure(const delegate_type& f, const TArg0 arg0, const TArg1 arg1, const TArg2 arg2, const TArg3 arg3, const TArg4 arg4)
closure(const callback_type& f, const TArg0 arg0, const TArg1 arg1, const TArg2 arg2, const TArg3 arg3, const TArg4 arg4)
: m_f(f)
, m_arg0(arg0)
, m_arg1(arg1)
@ -362,8 +370,8 @@ namespace etl
}
//*********************************************************************
/// Invoke the stored delegate with the bound arguments.
/// \return The result of the delegate invocation.
/// Invoke the stored callback with the bound arguments.
/// \return The result of the callback invocation.
//*********************************************************************
TReturn operator()() const
{
@ -372,7 +380,7 @@ namespace etl
private:
delegate_type m_f; ///< The delegate to invoke.
callback_type m_f; ///< The callback to invoke.
TArg0 m_arg0;
TArg1 m_arg1;
TArg2 m_arg2;

View File

@ -83,8 +83,9 @@ add_executable(etl_tests
test_circular_buffer.cpp
test_circular_buffer_external_buffer.cpp
test_circular_iterator.cpp
test_closure.cpp
test_closure_constexpr.cpp
test_closure_with_default_delegate.cpp
test_closure_with_default_delegate_constexpr.cpp
test_closure_with_inplace_function.cpp
test_compare.cpp
test_concepts.cpp
test_constant.cpp
@ -524,7 +525,7 @@ if ((CMAKE_CXX_COMPILER_ID MATCHES "GNU") OR (CMAKE_CXX_COMPILER_ID MATCHES "Cla
target_link_options(etl_tests
PRIVATE
-fsanitize=address,undefined,bounds
)
)
endif()
endif ()
endif ()

View File

@ -34,7 +34,7 @@ SOFTWARE.
namespace
{
SUITE(test_closure)
SUITE(test_closure_with_default_delegate)
{
int f1(int a1)
{

View File

@ -34,7 +34,7 @@ SOFTWARE.
namespace
{
SUITE(test_closure)
SUITE(test_closure_with_default_delegate_constexpr)
{
static constexpr int f1(int a1)
{

View File

@ -0,0 +1,208 @@
/******************************************************************************
The MIT License(MIT)
Embedded Template Library.
https://github.com/ETLCPP/etl
https://www.etlcpp.com
Copyright(c) 2025 BMW AG, John Wellbelove
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files(the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions :
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
******************************************************************************/
#include "unit_test_framework.h"
#include "etl/closure.h"
#include "etl/inplace_function.h"
#include <stdexcept>
namespace
{
SUITE(test_closure_with_inplace_function)
{
int f1(int a1)
{
return a1 * 3;
}
int f1_throwing(int)
{
throw std::runtime_error("throwing function");
}
void f1_void(int) {}
int f1_ref(int& a1)
{
return a1 * 3;
}
int f2(int a1, int a2)
{
return a1 * 3 + a2;
}
int f3(int a1, int a2, int a3)
{
return a1 * 3 + a2 * a3;
}
int f4(int a1, int a2, int a3, int a4)
{
return a1 * 3 + a2 * a3 + a4;
}
int f5(int a1, int a2, int a3, int a4, int a5)
{
return a1 * 3 + a2 * a3 + a4 * a5;
}
using f1_type = int(int);
using f1_ref_type = int(int&);
using f1_void_type = void(int);
using f2_type = int(int, int);
using f3_type = int(int, int, int);
using f4_type = int(int, int, int, int);
using f5_type = int(int, int, int, int, int);
using ipf1_type = etl::inplace_function<f1_type>;
using ipf1_ref_type = etl::inplace_function<f1_ref_type>;
using ipf1_void_type = etl::inplace_function<f1_void_type>;
using ipf2_type = etl::inplace_function<f2_type>;
using ipf3_type = etl::inplace_function<f3_type>;
using ipf4_type = etl::inplace_function<f4_type>;
using ipf5_type = etl::inplace_function<f5_type>;
ipf1_type ipf1 = ipf1_type::create<&f1>();
ipf1_type ipf1_throwing = ipf1_type::create<&f1_throwing>();
ipf1_ref_type ipf1_ref = ipf1_ref_type::create<&f1_ref>();
ipf1_void_type ipf1_void = ipf1_void_type::create<&f1_void>();
ipf2_type ipf2 = ipf2_type::create<&f2>();
ipf3_type ipf3 = ipf3_type::create<&f3>();
ipf4_type ipf4 = ipf4_type::create<&f4>();
ipf5_type ipf5 = ipf5_type::create<&f5>();
//*************************************************************************
TEST(test_1_arg)
{
etl::closure<f1_type, ipf1_type> c1(ipf1, 4);
CHECK_EQUAL(12, c1());
}
//*************************************************************************
TEST(test_1_arg_reference)
{
int v1 = 4;
etl::closure<f1_ref_type, ipf1_ref_type> c1_ref(ipf1_ref, v1);
CHECK_EQUAL(12, c1_ref());
v1 = 5;
CHECK_EQUAL(15, c1_ref());
}
//*************************************************************************
TEST(test_1_arg_lambda)
{
auto l = [](int a)
{
return a + 11;
};
ipf1_type ipf1_lambda(l);
etl::closure<f1_type, ipf1_type> c1_lambda(ipf1_lambda, 5);
CHECK_EQUAL(16, c1_lambda());
}
//*************************************************************************
TEST(test_throwing)
{
etl::closure<f1_type, ipf1_type> c1(ipf1_throwing, 4);
CHECK_THROW(c1(), std::runtime_error);
}
//*************************************************************************
TEST(test_void)
{
etl::closure<f1_void_type, ipf1_void_type> c1(ipf1_void, 4);
c1();
}
//*************************************************************************
TEST(test_2_args)
{
etl::closure<f2_type, ipf2_type> c2(ipf2, 4, 3);
CHECK_EQUAL(15, c2());
}
#if ETL_USING_CPP11 && !defined(ETL_CLOSURE_FORCE_CPP03_IMPLEMENTATION)
//*************************************************************************
TEST(test_2_args_bind)
{
etl::closure<f2_type, ipf2_type> c2(ipf2, 4, 3);
CHECK_EQUAL(15, c2());
c2.bind<0>(7);
c2.bind<1>(8);
CHECK_EQUAL(29, c2());
}
//*************************************************************************
TEST(test_2_args_bind_all)
{
etl::closure<f2_type, ipf2_type> c2(ipf2, 4, 3);
CHECK_EQUAL(15, c2());
c2.bind(7, 8);
CHECK_EQUAL(29, c2());
}
#endif
//*************************************************************************
TEST(test_3_args)
{
etl::closure<f3_type, ipf3_type> c3(ipf3, 4, 3, 2);
CHECK_EQUAL(18, c3());
}
//*************************************************************************
TEST(test_4_args)
{
etl::closure<f4_type, ipf4_type> c4(ipf4, 4, 3, 2, 1);
CHECK_EQUAL(19, c4());
}
//*************************************************************************
TEST(test_5_args)
{
etl::closure<f5_type, ipf5_type> c5(ipf5, 4, 3, 2, 1, 5);
CHECK_EQUAL(23, c5());
}
//*************************************************************************
TEST(test_bind_static_assert)
{
etl::closure<f2_type, ipf2_type> c(ipf2, 1, 2);
// Uncomment to generate static_assert errors.
// c.bind(1); // Argument count mismatch
// c.bind(1, 2, 3); // Argument count mismatch
// c.bind(1, std::string()); // Argument is not convertible
}
}
} // namespace

View File

@ -10273,8 +10273,9 @@
<ClCompile Include="..\test_circular_buffer.cpp" />
<ClCompile Include="..\test_circular_buffer_external_buffer.cpp" />
<ClCompile Include="..\test_circular_iterator.cpp" />
<ClCompile Include="..\test_closure.cpp" />
<ClCompile Include="..\test_closure_constexpr.cpp" />
<ClCompile Include="..\test_closure_with_default_delegate.cpp" />
<ClCompile Include="..\test_closure_with_default_delegate_constexpr.cpp" />
<ClCompile Include="..\test_closure_with_inplace_function.cpp" />
<ClCompile Include="..\test_concepts.cpp" />
<ClCompile Include="..\test_const_map.cpp" />
<ClCompile Include="..\test_const_map_constexpr.cpp" />

View File

@ -3647,7 +3647,7 @@
<ClCompile Include="..\test_chrono_literals.cpp">
<Filter>Tests\Chrono</Filter>
</ClCompile>
<ClCompile Include="..\test_closure.cpp">
<ClCompile Include="..\test_closure_with_default_delegate.cpp">
<Filter>Tests\Callbacks &amp; Delegates</Filter>
</ClCompile>
<ClCompile Include="..\syntax_check\closure.h.t.cpp">
@ -3704,7 +3704,7 @@
<ClCompile Include="..\test_crc8_nrsc5.cpp">
<Filter>Tests\CRC</Filter>
</ClCompile>
<ClCompile Include="..\test_closure_constexpr.cpp">
<ClCompile Include="..\test_closure_with_default_delegate_constexpr.cpp">
<Filter>Tests\Callbacks &amp; Delegates</Filter>
</ClCompile>
<ClCompile Include="..\test_crc8_opensafety.cpp">
@ -3800,6 +3800,9 @@
<ClCompile Include="..\test_print.cpp">
<Filter>Tests\Strings</Filter>
</ClCompile>
<ClCompile Include="..\test_closure_with_inplace_function.cpp">
<Filter>Tests\Callbacks &amp; Delegates</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<None Include="..\..\library.properties">
@ -3979,12 +3982,6 @@
<None Include="..\..\.github\workflows\gcc-c++23.yml">
<Filter>Resource Files\CI\Github</Filter>
</None>
<None Include="..\..\docs\_index.md">
<Filter>Documentation</Filter>
</None>
<None Include="..\..\docs\custom.css">
<Filter>Documentation</Filter>
</None>
<None Include="..\..\.github\workflows\clang-format.yaml" />
<None Include="..\..\.github\workflows\clang-format_update.yaml" />
<None Include="..\run-coverage.sh">