Merge branch 'development' into span-add-difference-type

This commit is contained in:
John Wellbelove 2026-07-16 09:24:45 +01:00 committed by GitHub
commit 85b3af4af0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 180 additions and 162 deletions

View File

@ -249,3 +249,21 @@ template <typename T, typename... Types>
ETL_CONSTEXPR14
const T&& get(const tuple<Types...>&&);
```
---
```cpp
template <typename TFunction, typename TTuple>
ETL_CONSTEXPR14
auto apply(TFunction&& f, TTuple&& t);
```
Invokes the callable object `f` (via `etl::invoke`) with the elements of the tuple `t` expanded as its arguments. Equivalent to `std::apply`.
```cpp
int add(int a, int b, int c) { return a + b + c; }
auto t = etl::make_tuple(1, 2, 3);
int sum = etl::apply(add, t); // sum == 6
```

View File

@ -1,112 +0,0 @@
---
title: "type_id"
---
{{< callout type="info">}}
Header: `type_id.h`
Since: `TBC`
{{< /callout >}}
Below is a concise, technical overview of the `etl::type_id` facility, suitable for design documentation or code review context.
## Overview:
`etl::type_id` is a lightweight, RTTI-free mechanism for uniquely identifying C++ types at runtime. It is designed for environments where standard RTTI (`typeid`, `std::type_info`) is unavailable, undesirable, or too costly—such as embedded systems or performance-constrained platforms.
The implementation guarantees uniqueness per type within a program image while remaining constexpr-friendly and requiring no dynamic allocation.
## Design Concept
The core idea is to associate each distinct type `T` with the address of a unique static object. That address serves as the types identifier.
This is achieved through:
```cpp
template <typename T>
struct type_id_anchor
{
static char value;
};
```
Each instantiation of `type_id_anchor<T>` owns its own `static char value`, and the address of that variable is guaranteed to be unique for each distinct `T`. The address is stable for the lifetime of the program.
## Key Components
### Type Normalization
When requesting a type ID via `type_id::get<T>()`, the type is first normalised:
```cpp
using type = typename etl::remove_cvref<T>::type;
```
This ensures that:
* `T`, `const T`, `T&`, and `T&&` all map to the same `type_id`
* Only the underlying type identity matters
### Type ID Generation
```cpp
template <typename T>
static ETL_CONSTEXPR type_id get() ETL_NOEXCEPT;
```
* Returns a `type_id` that uniquely represents `T`
* Internally stores the address of `type_id_anchor<T>::value`
* Requires no RTTI, no dynamic memory, and no runtime registration
### Invalid Type ID
```cpp
static ETL_CONSTEXPR type_id invalid_id() ETL_NOEXCEPT;
```
* Represents an invalid or uninitialised type ID
* Implemented as a null pointer (`id == nullptr`)
* Default constructor produces this state
## `type_id` Class Semantics
### Storage
```cpp
const void* id;
```
* The identifier is stored as a pointer
* Comparison is purely pointer comparison (fast and deterministic)
### Supported Operations
| Operation | Behavior |
| ------------------ | -------------------------------------------------- |
| Equality (`==`) | True if both type IDs refer to the same type |
| Inequality (`!=`) | Logical negation of equality |
| Ordering (`<`) | Pointer comparison (useful for ordered containers) |
| Boolean conversion | `true` if valid, `false` if invalid |
| Integer conversion | Explicit conversion to `intptr_t` / `uintptr_t` |
### Copy and Assignment
* Copyable and assignable
* Copy semantics preserve identity (pointer value)
## Guarantees and Properties
* **Uniqueness**: Each distinct type has exactly one unique `type_id`
* **Stability**: Type IDs remain valid for the entire program lifetime
* **Zero overhead**: No dynamic allocation, no lookup tables
* **RTTI-free**: Suitable for builds with RTTI disabled
* **Comparable**: Can be used as keys in associative containers
## Typical Use Cases
* Type-safe containers (e.g., variant, any, message buses)
* Plugin or component systems
* Compile-time / runtime type discrimination in embedded systems
* Replacement for `std::type_index` in restricted environments
## Summary
`etl::type_id` provides a robust, minimal, and efficient alternative to C++ RTTI by encoding type identity as the address of a unique static object. Its design is particularly well-suited to embedded and low-level systems where determinism, performance, and binary size are critical.

View File

@ -80,7 +80,7 @@ namespace etl
/// Constructor.
//*************************************************************************
string()
: istring(reinterpret_cast<value_type*>(&buffer), MAX_SIZE)
: istring(buffer, MAX_SIZE)
{
this->initialise();
}
@ -90,7 +90,7 @@ namespace etl
///\param other The other string.
//*************************************************************************
string(const etl::string<MAX_SIZE_>& other)
: istring(reinterpret_cast<value_type*>(&buffer), MAX_SIZE)
: istring(buffer, MAX_SIZE)
{
this->assign(other);
}
@ -100,7 +100,7 @@ namespace etl
///\param other The other istring.
//*************************************************************************
string(const etl::istring& other)
: istring(reinterpret_cast<value_type*>(&buffer), MAX_SIZE)
: istring(buffer, MAX_SIZE)
{
this->assign(other);
}
@ -112,7 +112,7 @@ namespace etl
///\param length The number of characters. Default = npos.
//*************************************************************************
string(const etl::istring& other, size_t position, size_t length = npos)
: istring(reinterpret_cast<value_type*>(&buffer), MAX_SIZE)
: istring(buffer, MAX_SIZE)
{
ETL_ASSERT(position < other.size(), ETL_ERROR(string_out_of_bounds));
@ -124,7 +124,7 @@ namespace etl
///\param text The initial text of the string.
//*************************************************************************
ETL_EXPLICIT_STRING_FROM_CHAR string(const value_type* text)
: istring(reinterpret_cast<value_type*>(&buffer), MAX_SIZE)
: istring(buffer, MAX_SIZE)
{
this->assign(text);
}
@ -135,7 +135,7 @@ namespace etl
///\param count The number of characters to copy.
//*************************************************************************
string(const value_type* text, size_t count)
: istring(reinterpret_cast<value_type*>(&buffer), MAX_SIZE)
: istring(buffer, MAX_SIZE)
{
this->assign(text, text + count);
}
@ -146,7 +146,7 @@ namespace etl
///\param value The value to fill the string with.
//*************************************************************************
string(size_type count, value_type c)
: istring(reinterpret_cast<value_type*>(&buffer), MAX_SIZE)
: istring(buffer, MAX_SIZE)
{
this->initialise();
this->resize(count, c);
@ -160,7 +160,7 @@ namespace etl
//*************************************************************************
template <typename TIterator>
string(TIterator first, TIterator last, typename etl::enable_if<!etl::is_integral<TIterator>::value, int>::type = 0)
: istring(reinterpret_cast<value_type*>(&buffer), MAX_SIZE)
: istring(buffer, MAX_SIZE)
{
this->assign(first, last);
}
@ -170,7 +170,7 @@ namespace etl
/// Construct from initializer_list.
//*************************************************************************
string(std::initializer_list<value_type> init)
: istring(reinterpret_cast<value_type*>(&buffer), MAX_SIZE)
: istring(buffer, MAX_SIZE)
{
this->assign(init.begin(), init.end());
}
@ -181,7 +181,7 @@ namespace etl
///\param view The string_view.
//*************************************************************************
explicit string(const etl::string_view& view)
: istring(reinterpret_cast<value_type*>(&buffer), MAX_SIZE)
: istring(buffer, MAX_SIZE)
{
this->assign(view.begin(), view.end());
}

View File

@ -39,6 +39,7 @@ SOFTWARE.
#endif
#include "functional.h"
#include "invoke.h"
#include "nth_type.h"
#include "type_list.h"
#include "type_traits.h"
@ -934,6 +935,35 @@ namespace etl
return tuple<TTypes&&...>(etl::forward<TTypes>(args)...);
}
namespace private_tuple
{
//**********************************
// Helper for apply.
// Invokes the callable with the tuple elements expanded via the index sequence.
// Uses an unqualified 'get' so that argument-dependent lookup finds the
// appropriate overload for etl::tuple, etl::array, std::tuple, std::pair, etc.
//**********************************
template <typename TFunction, typename TTuple, size_t... Indices>
ETL_CONSTEXPR14 auto apply_impl(TFunction&& f, TTuple&& t, etl::index_sequence<Indices...>)
-> decltype(etl::invoke(etl::forward<TFunction>(f), get<Indices>(etl::forward<TTuple>(t))...))
{
return etl::invoke(etl::forward<TFunction>(f), get<Indices>(etl::forward<TTuple>(t))...);
}
} // namespace private_tuple
//***************************************************************************
/// Invokes the callable object 'f' with the elements of the tuple 't' as
/// arguments. Equivalent to std::apply.
//***************************************************************************
template <typename TFunction, typename TTuple>
ETL_CONSTEXPR14 auto apply(TFunction&& f, TTuple&& t)
-> decltype(private_tuple::apply_impl(etl::forward<TFunction>(f), etl::forward<TTuple>(t),
etl::make_index_sequence<etl::tuple_size<etl::remove_reference_t<TTuple>>::value>{}))
{
return private_tuple::apply_impl(etl::forward<TFunction>(f), etl::forward<TTuple>(t),
etl::make_index_sequence<etl::tuple_size<etl::remove_reference_t<TTuple>>::value>{});
}
namespace private_tuple
{
//**********************************

View File

@ -77,7 +77,7 @@ namespace etl
/// Constructor.
//*************************************************************************
u16string()
: iu16string(reinterpret_cast<value_type*>(&buffer), MAX_SIZE)
: iu16string(buffer, MAX_SIZE)
{
this->initialise();
}
@ -87,7 +87,7 @@ namespace etl
///\param other The other string.
//*************************************************************************
u16string(const etl::u16string<MAX_SIZE_>& other)
: iu16string(reinterpret_cast<value_type*>(&buffer), MAX_SIZE)
: iu16string(buffer, MAX_SIZE)
{
this->assign(other);
}
@ -97,7 +97,7 @@ namespace etl
///\param other The other iu16string.
//*************************************************************************
u16string(const etl::iu16string& other)
: iu16string(reinterpret_cast<value_type*>(&buffer), MAX_SIZE)
: iu16string(buffer, MAX_SIZE)
{
this->assign(other);
}
@ -109,7 +109,7 @@ namespace etl
///\param length The number of characters. Default = npos.
//*************************************************************************
u16string(const etl::iu16string& other, size_type position, size_type length = npos)
: iu16string(reinterpret_cast<value_type*>(&buffer), MAX_SIZE)
: iu16string(buffer, MAX_SIZE)
{
ETL_ASSERT(position < other.size(), ETL_ERROR(string_out_of_bounds));
@ -121,7 +121,7 @@ namespace etl
///\param text The initial text of the u16string.
//*************************************************************************
ETL_EXPLICIT_STRING_FROM_CHAR u16string(const value_type* text)
: iu16string(reinterpret_cast<value_type*>(&buffer), MAX_SIZE)
: iu16string(buffer, MAX_SIZE)
{
this->assign(text);
}
@ -132,7 +132,7 @@ namespace etl
///\param count The number of characters to copy.
//*************************************************************************
u16string(const value_type* text, size_type count)
: iu16string(reinterpret_cast<value_type*>(&buffer), MAX_SIZE)
: iu16string(buffer, MAX_SIZE)
{
this->assign(text, text + count);
}
@ -143,7 +143,7 @@ namespace etl
///\param value The value to fill the u16string with.
//*************************************************************************
u16string(size_type count, value_type c)
: iu16string(reinterpret_cast<value_type*>(&buffer), MAX_SIZE)
: iu16string(buffer, MAX_SIZE)
{
this->initialise();
this->resize(count, c);
@ -157,7 +157,7 @@ namespace etl
//*************************************************************************
template <typename TIterator>
u16string(TIterator first, TIterator last, typename etl::enable_if<!etl::is_integral<TIterator>::value, int>::type = 0)
: iu16string(reinterpret_cast<value_type*>(&buffer), MAX_SIZE)
: iu16string(buffer, MAX_SIZE)
{
this->assign(first, last);
}
@ -167,7 +167,7 @@ namespace etl
/// Construct from initializer_list.
//*************************************************************************
u16string(std::initializer_list<value_type> init)
: iu16string(reinterpret_cast<value_type*>(&buffer), MAX_SIZE)
: iu16string(buffer, MAX_SIZE)
{
this->assign(init.begin(), init.end());
}
@ -178,7 +178,7 @@ namespace etl
///\param view The string_view.
//*************************************************************************
explicit u16string(const etl::u16string_view& view)
: iu16string(reinterpret_cast<value_type*>(&buffer), MAX_SIZE)
: iu16string(buffer, MAX_SIZE)
{
this->assign(view.begin(), view.end());
}

View File

@ -77,7 +77,7 @@ namespace etl
/// Constructor.
//*************************************************************************
u32string()
: iu32string(reinterpret_cast<value_type*>(&buffer), MAX_SIZE)
: iu32string(buffer, MAX_SIZE)
{
this->initialise();
}
@ -87,7 +87,7 @@ namespace etl
///\param other The other string.
//*************************************************************************
u32string(const etl::u32string<MAX_SIZE_>& other)
: iu32string(reinterpret_cast<value_type*>(&buffer), MAX_SIZE)
: iu32string(buffer, MAX_SIZE)
{
this->assign(other);
}
@ -97,7 +97,7 @@ namespace etl
///\param other The other iu32string.
//*************************************************************************
u32string(const etl::iu32string& other)
: iu32string(reinterpret_cast<value_type*>(&buffer), MAX_SIZE)
: iu32string(buffer, MAX_SIZE)
{
this->assign(other);
}
@ -109,7 +109,7 @@ namespace etl
///\param length The number of characters. Default = npos.
//*************************************************************************
u32string(const etl::iu32string& other, size_type position, size_type length = npos)
: iu32string(reinterpret_cast<value_type*>(&buffer), MAX_SIZE)
: iu32string(buffer, MAX_SIZE)
{
ETL_ASSERT(position < other.size(), ETL_ERROR(string_out_of_bounds));
@ -121,7 +121,7 @@ namespace etl
///\param text The initial text of the u32string.
//*************************************************************************
ETL_EXPLICIT_STRING_FROM_CHAR u32string(const value_type* text)
: iu32string(reinterpret_cast<value_type*>(&buffer), MAX_SIZE)
: iu32string(buffer, MAX_SIZE)
{
this->assign(text);
}
@ -132,7 +132,7 @@ namespace etl
///\param count The number of characters to copy.
//*************************************************************************
u32string(const value_type* text, size_type count)
: iu32string(reinterpret_cast<value_type*>(&buffer), MAX_SIZE)
: iu32string(buffer, MAX_SIZE)
{
this->assign(text, text + count);
}
@ -143,7 +143,7 @@ namespace etl
///\param value The value to fill the u32string with.
//*************************************************************************
u32string(size_type count, value_type c)
: iu32string(reinterpret_cast<value_type*>(&buffer), MAX_SIZE)
: iu32string(buffer, MAX_SIZE)
{
this->initialise();
this->resize(count, c);
@ -157,7 +157,7 @@ namespace etl
//*************************************************************************
template <typename TIterator>
u32string(TIterator first, TIterator last, typename etl::enable_if<!etl::is_integral<TIterator>::value, int>::type = 0)
: iu32string(reinterpret_cast<value_type*>(&buffer), MAX_SIZE)
: iu32string(buffer, MAX_SIZE)
{
this->assign(first, last);
}
@ -167,7 +167,7 @@ namespace etl
/// Construct from initializer_list.
//*************************************************************************
u32string(std::initializer_list<value_type> init)
: iu32string(reinterpret_cast<value_type*>(&buffer), MAX_SIZE)
: iu32string(buffer, MAX_SIZE)
{
this->assign(init.begin(), init.end());
}
@ -178,7 +178,7 @@ namespace etl
///\param view The string_view.
//*************************************************************************
explicit u32string(const etl::u32string_view& view)
: iu32string(reinterpret_cast<value_type*>(&buffer), MAX_SIZE)
: iu32string(buffer, MAX_SIZE)
{
this->assign(view.begin(), view.end());
}

View File

@ -80,7 +80,7 @@ namespace etl
/// Constructor.
//*************************************************************************
u8string()
: iu8string(reinterpret_cast<value_type*>(&buffer), MAX_SIZE)
: iu8string(buffer, MAX_SIZE)
{
this->initialise();
}
@ -90,7 +90,7 @@ namespace etl
///\param other The other u8string.
//*************************************************************************
u8string(const etl::u8string<MAX_SIZE_>& other)
: iu8string(reinterpret_cast<value_type*>(&buffer), MAX_SIZE)
: iu8string(buffer, MAX_SIZE)
{
this->assign(other);
}
@ -100,7 +100,7 @@ namespace etl
///\param other The other iu8string.
//*************************************************************************
u8string(const etl::iu8string& other)
: iu8string(reinterpret_cast<value_type*>(&buffer), MAX_SIZE)
: iu8string(buffer, MAX_SIZE)
{
this->assign(other);
}
@ -112,7 +112,7 @@ namespace etl
///\param length The number of characters. Default = npos.
//*************************************************************************
u8string(const etl::iu8string& other, size_t position, size_t length = npos)
: iu8string(reinterpret_cast<value_type*>(&buffer), MAX_SIZE)
: iu8string(buffer, MAX_SIZE)
{
ETL_ASSERT(position < other.size(), ETL_ERROR(string_out_of_bounds));
@ -124,7 +124,7 @@ namespace etl
///\param text The initial text of the u8string.
//*************************************************************************
ETL_EXPLICIT_STRING_FROM_CHAR u8string(const value_type* text)
: iu8string(reinterpret_cast<value_type*>(&buffer), MAX_SIZE)
: iu8string(buffer, MAX_SIZE)
{
this->assign(text);
}
@ -135,7 +135,7 @@ namespace etl
///\param count The number of characters to copy.
//*************************************************************************
u8string(const value_type* text, size_t count)
: iu8string(reinterpret_cast<value_type*>(&buffer), MAX_SIZE)
: iu8string(buffer, MAX_SIZE)
{
this->assign(text, text + count);
}
@ -146,7 +146,7 @@ namespace etl
///\param value The value to fill the u8string with.
//*************************************************************************
u8string(size_type count, value_type c)
: iu8string(reinterpret_cast<value_type*>(&buffer), MAX_SIZE)
: iu8string(buffer, MAX_SIZE)
{
this->initialise();
this->resize(count, c);
@ -160,7 +160,7 @@ namespace etl
//*************************************************************************
template <typename TIterator>
u8string(TIterator first, TIterator last, typename etl::enable_if<!etl::is_integral<TIterator>::value, int>::type = 0)
: iu8string(reinterpret_cast<value_type*>(&buffer), MAX_SIZE)
: iu8string(buffer, MAX_SIZE)
{
this->assign(first, last);
}
@ -170,7 +170,7 @@ namespace etl
/// Construct from initializer_list.
//*************************************************************************
u8string(std::initializer_list<value_type> init)
: iu8string(reinterpret_cast<value_type*>(&buffer), MAX_SIZE)
: iu8string(buffer, MAX_SIZE)
{
this->assign(init.begin(), init.end());
}
@ -181,7 +181,7 @@ namespace etl
///\param view The string_view.
//*************************************************************************
explicit u8string(const etl::u8string_view& view)
: iu8string(reinterpret_cast<value_type*>(&buffer), MAX_SIZE)
: iu8string(buffer, MAX_SIZE)
{
this->assign(view.begin(), view.end());
}

View File

@ -77,7 +77,7 @@ namespace etl
/// Constructor.
//*************************************************************************
wstring()
: iwstring(reinterpret_cast<value_type*>(&buffer), MAX_SIZE)
: iwstring(buffer, MAX_SIZE)
{
this->initialise();
}
@ -87,7 +87,7 @@ namespace etl
///\param other The other string.
//*************************************************************************
wstring(const etl::wstring<MAX_SIZE_>& other)
: iwstring(reinterpret_cast<value_type*>(&buffer), MAX_SIZE)
: iwstring(buffer, MAX_SIZE)
{
this->assign(other);
}
@ -97,7 +97,7 @@ namespace etl
///\param other The other iwstring.
//*************************************************************************
wstring(const etl::iwstring& other)
: iwstring(reinterpret_cast<value_type*>(&buffer), MAX_SIZE)
: iwstring(buffer, MAX_SIZE)
{
this->assign(other);
}
@ -109,7 +109,7 @@ namespace etl
///\param length The number of characters. Default = npos.
//*************************************************************************
wstring(const etl::iwstring& other, size_type position, size_type length = npos)
: iwstring(reinterpret_cast<value_type*>(&buffer), MAX_SIZE)
: iwstring(buffer, MAX_SIZE)
{
ETL_ASSERT(position < other.size(), ETL_ERROR(string_out_of_bounds));
@ -121,7 +121,7 @@ namespace etl
///\param text The initial text of the wstring.
//*************************************************************************
ETL_EXPLICIT_STRING_FROM_CHAR wstring(const value_type* text)
: iwstring(reinterpret_cast<value_type*>(&buffer), MAX_SIZE)
: iwstring(buffer, MAX_SIZE)
{
this->assign(text);
}
@ -132,7 +132,7 @@ namespace etl
///\param count The number of characters to copy.
//*************************************************************************
wstring(const value_type* text, size_type count)
: iwstring(reinterpret_cast<value_type*>(&buffer), MAX_SIZE)
: iwstring(buffer, MAX_SIZE)
{
this->assign(text, text + count);
}
@ -143,7 +143,7 @@ namespace etl
///\param value The value to fill the wstring with.
//*************************************************************************
wstring(size_type count, value_type c)
: iwstring(reinterpret_cast<value_type*>(&buffer), MAX_SIZE)
: iwstring(buffer, MAX_SIZE)
{
this->initialise();
this->resize(count, c);
@ -157,7 +157,7 @@ namespace etl
//*************************************************************************
template <typename TIterator>
wstring(TIterator first, TIterator last, typename etl::enable_if<!etl::is_integral<TIterator>::value, int>::type = 0)
: iwstring(reinterpret_cast<value_type*>(&buffer), MAX_SIZE)
: iwstring(buffer, MAX_SIZE)
{
this->assign(first, last);
}
@ -167,7 +167,7 @@ namespace etl
/// Construct from initializer_list.
//*************************************************************************
wstring(std::initializer_list<value_type> init)
: iwstring(reinterpret_cast<value_type*>(&buffer), MAX_SIZE)
: iwstring(buffer, MAX_SIZE)
{
this->assign(init.begin(), init.end());
}
@ -178,7 +178,7 @@ namespace etl
///\param view The string_view.
//*************************************************************************
explicit wstring(const etl::wstring_view& view)
: iwstring(reinterpret_cast<value_type*>(&buffer), MAX_SIZE)
: iwstring(buffer, MAX_SIZE)
{
this->assign(view.begin(), view.end());
}

View File

@ -76,6 +76,32 @@ namespace
int i;
};
//*********************************
int apply_sum(int a, int b, int c)
{
return a + b + c;
}
//*********************************
struct Summer
{
int operator()(int a, int b) const
{
return a + b;
}
};
//*********************************
struct Accumulator
{
int total;
void add(int a, int b)
{
total = a + b;
}
};
SUITE(test_tuple)
{
//*************************************************************************
@ -930,5 +956,61 @@ namespace
CHECK_EQUAL(1, i);
CHECK_EQUAL(std::string("2"), d.value);
}
//*************************************************************************
TEST(test_apply_free_function)
{
etl::tuple<int, int, int> t(1, 2, 3);
CHECK_EQUAL(6, etl::apply(apply_sum, t));
}
//*************************************************************************
TEST(test_apply_function_object)
{
etl::tuple<int, int> t(10, 20);
CHECK_EQUAL(30, etl::apply(Summer(), t));
}
//*************************************************************************
#if ETL_USING_CPP11
TEST(test_apply_lambda)
{
etl::tuple<int, int, int> t(2, 3, 4);
int result = etl::apply([](int a, int b, int c) { return a * b * c; }, t);
CHECK_EQUAL(24, result);
}
#endif
//*************************************************************************
TEST(test_apply_member_function)
{
Accumulator acc = {0};
etl::apply(&Accumulator::add, etl::make_tuple(&acc, 5, 7));
CHECK_EQUAL(12, acc.total);
}
//*************************************************************************
TEST(test_apply_rvalue_tuple)
{
CHECK_EQUAL(6, etl::apply(apply_sum, etl::make_tuple(1, 2, 3)));
}
//*************************************************************************
#if ETL_USING_CPP11
TEST(test_apply_empty_tuple)
{
etl::tuple<> t;
int result = etl::apply([]() { return 42; }, t);
CHECK_EQUAL(42, result);
}
#endif
}
} // namespace