mirror of
https://github.com/ETLCPP/etl.git
synced 2026-10-28 18:07:10 +08:00
Merge branch 'development' into error-handler-fix-returns-after-throw
This commit is contained in:
commit
9d930e5607
32
.github/workflows/msvc.yml
vendored
32
.github/workflows/msvc.yml
vendored
@ -36,8 +36,12 @@ concurrency:
|
||||
|
||||
jobs:
|
||||
build-windows-msvc-stl:
|
||||
name: Windows - STL
|
||||
name: Windows - STL - C++${{ matrix.cxx_standard }}
|
||||
runs-on: [windows-2022]
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
cxx_standard: [14, 17, 20]
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
@ -50,7 +54,7 @@ jobs:
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
cmake -G "Visual Studio 17 2022" -AWin32 -DBUILD_TESTS=ON -DNO_STL=OFF -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF -DETL_CXX_STANDARD=17 ./
|
||||
cmake -G "Visual Studio 17 2022" -AWin32 -DBUILD_TESTS=ON -DNO_STL=OFF -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF -DETL_CXX_STANDARD=${{ matrix.cxx_standard }} ./
|
||||
MSBuild.exe -version
|
||||
MSBuild.exe .\etl.sln
|
||||
|
||||
@ -58,8 +62,12 @@ jobs:
|
||||
run: test/Debug/etl_tests.exe -v
|
||||
|
||||
build-windows-msvc-no-stl:
|
||||
name: Windows - No STL
|
||||
name: Windows - No STL - C++${{ matrix.cxx_standard }}
|
||||
runs-on: [windows-2022]
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
cxx_standard: [14, 17, 20]
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6.0.3
|
||||
@ -71,7 +79,7 @@ jobs:
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
cmake -G "Visual Studio 17 2022" -AWin32 -DBUILD_TESTS=ON -DNO_STL=ON -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF -DETL_CXX_STANDARD=17 ./
|
||||
cmake -G "Visual Studio 17 2022" -AWin32 -DBUILD_TESTS=ON -DNO_STL=ON -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF -DETL_CXX_STANDARD=${{ matrix.cxx_standard }} ./
|
||||
MSBuild.exe -version
|
||||
MSBuild.exe .\etl.sln
|
||||
|
||||
@ -79,8 +87,12 @@ jobs:
|
||||
run: test/Debug/etl_tests.exe -v
|
||||
|
||||
build-windows-msvc-stl-force-cpp03:
|
||||
name: Windows - STL - Force C++03
|
||||
name: Windows - STL - Force C++03 - C++${{ matrix.cxx_standard }}
|
||||
runs-on: [windows-2022]
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
cxx_standard: [14, 17, 20]
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
@ -93,7 +105,7 @@ jobs:
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
cmake -G "Visual Studio 17 2022" -AWin32 -DBUILD_TESTS=ON -DNO_STL=OFF -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=ON -DETL_CXX_STANDARD=17 ./
|
||||
cmake -G "Visual Studio 17 2022" -AWin32 -DBUILD_TESTS=ON -DNO_STL=OFF -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=ON -DETL_CXX_STANDARD=${{ matrix.cxx_standard }} ./
|
||||
MSBuild.exe -version
|
||||
MSBuild.exe .\etl.sln
|
||||
|
||||
@ -101,8 +113,12 @@ jobs:
|
||||
run: test/Debug/etl_tests.exe -v
|
||||
|
||||
build-windows-msvc-no-stl-force-cpp03:
|
||||
name: Windows - No STL - Force C++03
|
||||
name: Windows - No STL - Force C++03 - C++${{ matrix.cxx_standard }}
|
||||
runs-on: [windows-2022]
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
cxx_standard: [14, 17, 20]
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
@ -115,7 +131,7 @@ jobs:
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
cmake -G "Visual Studio 17 2022" -AWin32 -DBUILD_TESTS=ON -DNO_STL=ON -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=ON -DETL_CXX_STANDARD=17 ./
|
||||
cmake -G "Visual Studio 17 2022" -AWin32 -DBUILD_TESTS=ON -DNO_STL=ON -DETL_USE_TYPE_TRAITS_BUILTINS=OFF -DETL_USER_DEFINED_TYPE_TRAITS=OFF -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=ON -DETL_CXX_STANDARD=${{ matrix.cxx_standard }} ./
|
||||
MSBuild.exe -version
|
||||
MSBuild.exe .\etl.sln
|
||||
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@ -423,3 +423,4 @@ include/etl/iterator.h~RFfd5eda.TMP
|
||||
test/vs2022/Build
|
||||
docs/Messaging
|
||||
test/vs2026/Build
|
||||
test/include
|
||||
|
||||
@ -166,6 +166,50 @@ Constructs with the supplied value.
|
||||
|
||||
---
|
||||
|
||||
```cpp
|
||||
unaligned_type(const void* address)
|
||||
```
|
||||
**Description**
|
||||
Constructs from the `Size` bytes at the address.
|
||||
From: `20.39.5`
|
||||
|
||||
---
|
||||
|
||||
```cpp
|
||||
unaligned_type(const void* address, size_t buffer_size)
|
||||
```
|
||||
**Description**
|
||||
Constructs from the `Size` bytes at the address.
|
||||
`buffer_size` must be greater than or equal to `Size`. This is a precondition,
|
||||
checked by `ETL_ASSERT`. If checks are disabled then a smaller buffer size
|
||||
results in a read beyond the end of the buffer.
|
||||
From: `20.39.5`
|
||||
|
||||
---
|
||||
|
||||
```cpp
|
||||
ETL_CONSTEXPR14 unaligned_type(const unsigned char* address)
|
||||
```
|
||||
**Description**
|
||||
Constructs from the `Size` bytes at the address.
|
||||
Unlike the `const void*` overload, this requires no `reinterpret_cast` and so
|
||||
may be used in a constexpr context.
|
||||
From: `20.48.1`
|
||||
|
||||
---
|
||||
|
||||
```cpp
|
||||
ETL_CONSTEXPR14 unaligned_type(const unsigned char* address, size_t buffer_size)
|
||||
```
|
||||
**Description**
|
||||
Constructs from the `Size` bytes at the address.
|
||||
`buffer_size` must be greater than or equal to `Size`. This is a precondition,
|
||||
checked by `ETL_ASSERT`. If checks are disabled then a smaller buffer size
|
||||
results in a read beyond the end of the buffer.
|
||||
From: `20.48.1`
|
||||
|
||||
---
|
||||
|
||||
```cpp
|
||||
template <int Endian_Other>
|
||||
unaligned_type(const unaligned_type<T, Endian_Other>& other)
|
||||
@ -176,10 +220,11 @@ The endianness is converted, if necessary.
|
||||
|
||||
## Member types
|
||||
```cpp
|
||||
using pointer = char*;
|
||||
using const_pointer = const char*;
|
||||
using iterator = char*;
|
||||
using const_iterator = const char*;
|
||||
using storage_type = unsigned char;
|
||||
using pointer = unsigned char*;
|
||||
using const_pointer = const unsigned char*;
|
||||
using iterator = unsigned char*;
|
||||
using const_iterator = const unsigned char*;
|
||||
using reverse_iterator = etl::reverse_iterator<iterator>;
|
||||
using const_reverse_iterator = etl::reverse_iterator<const_iterator>;
|
||||
```
|
||||
@ -306,7 +351,7 @@ Const reverse iterator to the end of the storage.
|
||||
---
|
||||
|
||||
```cpp
|
||||
char& operator[](int i)
|
||||
storage_type& operator[](int i)
|
||||
```
|
||||
**Description**
|
||||
Index operator.
|
||||
@ -314,7 +359,7 @@ Index operator.
|
||||
---
|
||||
|
||||
```cpp
|
||||
ETL_CONSTEXPR14 const char& operator[](int i) const
|
||||
ETL_CONSTEXPR14 const storage_type& operator[](int i) const
|
||||
```
|
||||
**Description**
|
||||
Const index operator.
|
||||
@ -345,6 +390,15 @@ Gets the value.
|
||||
|
||||
---
|
||||
|
||||
```cpp
|
||||
static ETL_CONSTEXPR14 T value_from(const_pointer address)
|
||||
```
|
||||
**Description**
|
||||
Gets the value of the `Size` bytes at the address.
|
||||
Unlike construction from an address, the bytes are not copied to storage first.
|
||||
|
||||
---
|
||||
|
||||
```cpp
|
||||
template <int Endian_Other>
|
||||
ETL_CONSTEXPR14 unaligned_type& operator =(const unaligned_type<T, Endian_Other>& other)
|
||||
@ -405,3 +459,317 @@ ETL_CONSTEXPR14 bool operator !=(T lhs, const unaligned_type& rhs)
|
||||
**Description**
|
||||
Inequality operator.
|
||||
Removed: `20.39.3`
|
||||
|
||||
---
|
||||
|
||||
# Arbitrary width integers
|
||||
|
||||
{{< callout type="info">}}
|
||||
From: `20.50.0`
|
||||
{{< /callout >}}
|
||||
|
||||
`etl::unaligned_type` is templated on a fundamental type, so it can only store
|
||||
integers whose width is that of a standard integer type. For protocols that use
|
||||
odd widths, such as 24 or 48 bit integers, the following types are templated on
|
||||
the **number of bytes of storage** instead.
|
||||
|
||||
```cpp
|
||||
template <size_t Size_, int Endian_>
|
||||
class unaligned_uint_type
|
||||
|
||||
template <size_t Size_, int Endian_>
|
||||
class unaligned_int_type
|
||||
```
|
||||
|
||||
Where `Size_` is the number of bytes of storage and `Endian_` is
|
||||
`etl::endian::big` or `etl::endian::little`.
|
||||
|
||||
`unaligned_uint_type` stores an unsigned integer.
|
||||
`unaligned_int_type` stores a signed, two's complement integer, which is sign
|
||||
extended when read.
|
||||
|
||||
The value is interfaced to via `value_type`: the smallest standard integer type
|
||||
that can hold `Size_` bytes. For example, `unaligned_uint_type<3U, ...>` has a
|
||||
`value_type` of `uint32_t`.
|
||||
|
||||
Values that do not fit in `Size_` bytes are truncated, in the same way as a
|
||||
narrowing `static_cast`.
|
||||
|
||||
Marked as `packed` for MSVC, GCC and Clang.
|
||||
|
||||
## Constants
|
||||
`int Endian`
|
||||
 The endianness of the type.
|
||||
`size_t Size`
|
||||
 The size, in bytes, of the type.
|
||||
`value_type Min_Value`
|
||||
 The lowest value that may be stored.
|
||||
`value_type Max_Value`
|
||||
 The highest value that may be stored.
|
||||
|
||||
## Predefined types
|
||||
|
||||
The 40, 48 and 56 bit types are only defined if `ETL_USING_64BIT_TYPES`.
|
||||
The host order types are only defined if `ETL_HAS_CONSTEXPR_ENDIANNESS`.
|
||||
|
||||
### Little Endian
|
||||
```cpp
|
||||
le_uint24_t
|
||||
le_int24_t
|
||||
le_uint40_t ETL_USING_64BIT_TYPES
|
||||
le_int40_t ETL_USING_64BIT_TYPES
|
||||
le_uint48_t ETL_USING_64BIT_TYPES
|
||||
le_int48_t ETL_USING_64BIT_TYPES
|
||||
le_uint56_t ETL_USING_64BIT_TYPES
|
||||
le_int56_t ETL_USING_64BIT_TYPES
|
||||
```
|
||||
|
||||
### Big Endian
|
||||
```cpp
|
||||
be_uint24_t
|
||||
be_int24_t
|
||||
be_uint40_t ETL_USING_64BIT_TYPES
|
||||
be_int40_t ETL_USING_64BIT_TYPES
|
||||
be_uint48_t ETL_USING_64BIT_TYPES
|
||||
be_int48_t ETL_USING_64BIT_TYPES
|
||||
be_uint56_t ETL_USING_64BIT_TYPES
|
||||
be_int56_t ETL_USING_64BIT_TYPES
|
||||
```
|
||||
|
||||
### Network Order
|
||||
Synonyms for Big Endian.
|
||||
```cpp
|
||||
using net_uint24_t = be_uint24_t
|
||||
using net_int24_t = be_int24_t
|
||||
using net_uint40_t = be_uint40_t
|
||||
using net_int40_t = be_int40_t
|
||||
using net_uint48_t = be_uint48_t
|
||||
using net_int48_t = be_int48_t
|
||||
using net_uint56_t = be_uint56_t
|
||||
using net_int56_t = be_int56_t
|
||||
```
|
||||
|
||||
### Host order
|
||||
```cpp
|
||||
host_uint24_t
|
||||
host_int24_t
|
||||
host_uint40_t
|
||||
host_int40_t
|
||||
host_uint48_t
|
||||
host_int48_t
|
||||
host_uint56_t
|
||||
host_int56_t
|
||||
```
|
||||
|
||||
## Constructors
|
||||
|
||||
```cpp
|
||||
unaligned_uint_type()
|
||||
unaligned_int_type()
|
||||
```
|
||||
**Description**
|
||||
Constructs an uninitialised object.
|
||||
|
||||
---
|
||||
|
||||
```cpp
|
||||
ETL_CONSTEXPR14 unaligned_uint_type(value_type value)
|
||||
ETL_CONSTEXPR14 unaligned_int_type(value_type value)
|
||||
```
|
||||
**Description**
|
||||
Constructs with the supplied value.
|
||||
Values that do not fit in `Size` bytes are truncated.
|
||||
|
||||
---
|
||||
|
||||
```cpp
|
||||
unaligned_uint_type(const void* address)
|
||||
unaligned_int_type(const void* address)
|
||||
```
|
||||
**Description**
|
||||
Constructs from the `Size` bytes at the address.
|
||||
|
||||
---
|
||||
|
||||
```cpp
|
||||
unaligned_uint_type(const void* address, size_t buffer_size)
|
||||
unaligned_int_type(const void* address, size_t buffer_size)
|
||||
```
|
||||
**Description**
|
||||
Constructs from the `Size` bytes at the address.
|
||||
`buffer_size` must be greater than or equal to `Size`. This is a precondition,
|
||||
checked by `ETL_ASSERT`. If checks are disabled then a smaller buffer size
|
||||
results in a read beyond the end of the buffer.
|
||||
|
||||
---
|
||||
|
||||
```cpp
|
||||
ETL_CONSTEXPR14 unaligned_uint_type(const unsigned char* address)
|
||||
ETL_CONSTEXPR14 unaligned_int_type(const unsigned char* address)
|
||||
```
|
||||
**Description**
|
||||
Constructs from the `Size` bytes at the address.
|
||||
Unlike the `const void*` overload, this requires no `reinterpret_cast` and so
|
||||
may be used in a constexpr context.
|
||||
|
||||
---
|
||||
|
||||
```cpp
|
||||
ETL_CONSTEXPR14 unaligned_uint_type(const unsigned char* address, size_t buffer_size)
|
||||
ETL_CONSTEXPR14 unaligned_int_type(const unsigned char* address, size_t buffer_size)
|
||||
```
|
||||
**Description**
|
||||
Constructs from the `Size` bytes at the address.
|
||||
`buffer_size` must be greater than or equal to `Size`. This is a precondition,
|
||||
checked by `ETL_ASSERT`.
|
||||
|
||||
---
|
||||
|
||||
```cpp
|
||||
template <int Endian_Other>
|
||||
unaligned_uint_type(const unaligned_uint_type<Size, Endian_Other>& other)
|
||||
```
|
||||
**Description**
|
||||
Constructs from another type of the same size.
|
||||
The endianness is converted, if necessary.
|
||||
|
||||
## Member functions
|
||||
|
||||
The storage interface (`data()`, `size()`, the iterators and `operator[]`) is
|
||||
the same as that of `etl::unaligned_type`.
|
||||
|
||||
```cpp
|
||||
ETL_CONSTEXPR14 unaligned_uint_type& operator =(value_type value)
|
||||
```
|
||||
**Description**
|
||||
Assignment operator.
|
||||
|
||||
---
|
||||
|
||||
```cpp
|
||||
ETL_CONSTEXPR14 operator value_type() const
|
||||
```
|
||||
**Description**
|
||||
Conversion operator.
|
||||
For `unaligned_int_type` the stored value is sign extended.
|
||||
|
||||
---
|
||||
|
||||
```cpp
|
||||
ETL_CONSTEXPR14 value_type value() const
|
||||
```
|
||||
**Description**
|
||||
Gets the value.
|
||||
For `unaligned_int_type` the stored value is sign extended.
|
||||
|
||||
---
|
||||
|
||||
```cpp
|
||||
static ETL_CONSTEXPR14 value_type value_from(const_pointer address)
|
||||
```
|
||||
**Description**
|
||||
Gets the value of the `Size` bytes at the address.
|
||||
Unlike construction from an address, the bytes are not copied to storage first.
|
||||
|
||||
---
|
||||
|
||||
```cpp
|
||||
template <int Endian_Other>
|
||||
ETL_CONSTEXPR14 unaligned_uint_type& operator =(const unaligned_uint_type<Size, Endian_Other>& other)
|
||||
```
|
||||
**Description**
|
||||
Assignment operator from other endianness.
|
||||
|
||||
---
|
||||
|
||||
# Arbitrary width integers with an external buffer
|
||||
|
||||
{{< callout type="info">}}
|
||||
From: `20.50.0`
|
||||
{{< /callout >}}
|
||||
|
||||
```cpp
|
||||
template <size_t Size_, int Endian_>
|
||||
class unaligned_uint_type_ext
|
||||
|
||||
template <size_t Size_, int Endian_>
|
||||
class unaligned_int_type_ext
|
||||
```
|
||||
|
||||
These are the equivalents of `unaligned_uint_type` and `unaligned_int_type`
|
||||
that do not own their storage, but reference an externally supplied buffer.
|
||||
They have the same `value_type`, constants and value interface.
|
||||
|
||||
The default constructor is deleted: the storage must always be supplied.
|
||||
|
||||
### Predefined types
|
||||
As for the internal storage versions, but with an `_ext_t` suffix.
|
||||
```cpp
|
||||
le_uint24_ext_t be_uint24_ext_t net_uint24_ext_t host_uint24_ext_t
|
||||
le_int24_ext_t be_int24_ext_t net_int24_ext_t host_int24_ext_t
|
||||
le_uint40_ext_t be_uint40_ext_t net_uint40_ext_t host_uint40_ext_t
|
||||
le_int40_ext_t be_int40_ext_t net_int40_ext_t host_int40_ext_t
|
||||
le_uint48_ext_t be_uint48_ext_t net_uint48_ext_t host_uint48_ext_t
|
||||
le_int48_ext_t be_int48_ext_t net_int48_ext_t host_int48_ext_t
|
||||
le_uint56_ext_t be_uint56_ext_t net_uint56_ext_t host_uint56_ext_t
|
||||
le_int56_ext_t be_int56_ext_t net_int56_ext_t host_int56_ext_t
|
||||
```
|
||||
|
||||
## Constructors
|
||||
|
||||
```cpp
|
||||
unaligned_uint_type_ext() ETL_DELETE
|
||||
```
|
||||
**Description**
|
||||
Deleted. The storage must always be supplied.
|
||||
|
||||
---
|
||||
|
||||
```cpp
|
||||
unaligned_uint_type_ext(pointer storage)
|
||||
```
|
||||
**Description**
|
||||
Constructs, referencing the supplied storage.
|
||||
The storage is not modified.
|
||||
|
||||
---
|
||||
|
||||
```cpp
|
||||
unaligned_uint_type_ext(value_type value, pointer storage)
|
||||
```
|
||||
**Description**
|
||||
Constructs, referencing the supplied storage, and writes the value to it.
|
||||
|
||||
---
|
||||
|
||||
```cpp
|
||||
template <int Endian_Other>
|
||||
unaligned_uint_type_ext(const unaligned_uint_type_ext<Size, Endian_Other>& other, pointer storage)
|
||||
```
|
||||
**Description**
|
||||
Constructs, referencing the supplied storage, and copies the value of `other`
|
||||
to it, converting the endianness if necessary.
|
||||
**Note**
|
||||
The storage of `other` must either be the same storage as that supplied, or
|
||||
completely separate. Partially overlapping storage is undefined behaviour.
|
||||
|
||||
## Member functions
|
||||
|
||||
```cpp
|
||||
void set_storage(pointer storage)
|
||||
```
|
||||
**Description**
|
||||
Sets the storage for the type.
|
||||
The storage is not modified.
|
||||
|
||||
---
|
||||
|
||||
```cpp
|
||||
template <int Endian_Other>
|
||||
unaligned_uint_type_ext& operator =(const unaligned_uint_type_ext<Size, Endian_Other>& other)
|
||||
```
|
||||
**Description**
|
||||
Assignment operator from other endianness.
|
||||
**Note**
|
||||
The storage of `other` must either be the same storage as this object's, or
|
||||
completely separate. Partially overlapping storage is undefined behaviour.
|
||||
|
||||
@ -26,7 +26,8 @@ including the header to override automatic detection.
|
||||
|
||||
When `ETL_HAS_FLOATING_POINT_CLAMPED_VALUE` is `0`, `T` must be an integral
|
||||
type. When it is `1`, `float`, `double`, and `long double` may also be used with
|
||||
runtime bounds.
|
||||
runtime bounds. `clamped_value_range<T>` and `referenced_clamped_value<T>` are
|
||||
available in C++11 and later.
|
||||
|
||||
```cpp
|
||||
// C++11 and later
|
||||
@ -48,6 +49,20 @@ etl::clamped_value<float, -1.5f, 2.5f> float_ct(0.5f);
|
||||
#endif
|
||||
```
|
||||
|
||||
In C++11 and later, multiple values can share one immutable runtime range:
|
||||
|
||||
```cpp
|
||||
const etl::clamped_value_range<float> range(-1.5f, 2.5f);
|
||||
|
||||
etl::referenced_clamped_value<float> value1(range, 0.5f);
|
||||
etl::referenced_clamped_value<float> value2(range, 1.5f);
|
||||
```
|
||||
|
||||
`referenced_clamped_value<T>` stores an
|
||||
`etl::reference_wrapper<const clamped_value_range<T>>`. The range must outlive
|
||||
every value that refers to it. Construction from a temporary range is deleted.
|
||||
Copying or swapping a referenced value also copies or swaps its range reference.
|
||||
|
||||
## Types
|
||||
|
||||
`difference_type` is the signed counterpart of `T` for integral values. For
|
||||
@ -55,6 +70,9 @@ floating-point values, `difference_type` is `T`, allowing fractional steps.
|
||||
`limits_type` and `difference_limits_type` provide the corresponding
|
||||
`etl::numeric_limits` specializations.
|
||||
|
||||
`referenced_clamped_value<T>` also defines `range_type` as
|
||||
`clamped_value_range<T>`.
|
||||
|
||||
## Constructors
|
||||
|
||||
```cpp
|
||||
@ -76,6 +94,23 @@ runtime-bound specialization. Supplying `Min` and `Max` selects fixed
|
||||
compile-time bounds. NaN bounds and initial values are rejected. Infinite
|
||||
bounds and values are permitted.
|
||||
|
||||
### Referenced ranges
|
||||
|
||||
```cpp
|
||||
clamped_value_range<T> range(min, max);
|
||||
referenced_clamped_value<T> value(range);
|
||||
referenced_clamped_value<T> value(range, initial);
|
||||
```
|
||||
|
||||
The one-argument value constructor initializes to `range.min()`. The
|
||||
two-argument form clamps `initial` to the shared range. A range is immutable
|
||||
after construction, so every referring value observes stable bounds. A
|
||||
default-constructed range uses the full representable range of `T`.
|
||||
|
||||
`referenced_clamped_value<T>` is non-owning. The referenced range must outlive
|
||||
the value and any copies of it. The constructors taking `range_type&&` are
|
||||
deleted to prevent direct construction from a temporary range.
|
||||
|
||||
## Modifiers
|
||||
|
||||
```cpp
|
||||
@ -93,6 +128,10 @@ counterpart of `T` as its step type. For floating-point values, the step type
|
||||
is `T` and may be fractional. Steps saturate in constant time at the minimum
|
||||
or maximum rather than wrapping. NaN steps are rejected.
|
||||
|
||||
`referenced_clamped_value<T>` supports `set(T)`, `to_min()`, `to_max()`, and
|
||||
`advance(difference_type)`. It does not provide `set(min, max)` because its
|
||||
range is immutable and shared.
|
||||
|
||||
Positive steps move toward `max`; negative steps move toward `min`. Infinite
|
||||
steps saturate at a finite bound. Infinite values and bounds remain valid.
|
||||
|
||||
@ -122,6 +161,14 @@ ETL_NODISCARD T max() const noexcept;
|
||||
Gets the current value and its bounds. Both compile-time and runtime
|
||||
specializations provide these accessors as const-qualified member functions.
|
||||
|
||||
`referenced_clamped_value<T>` additionally provides:
|
||||
|
||||
```cpp
|
||||
ETL_NODISCARD const range_type& range() const noexcept;
|
||||
```
|
||||
|
||||
This returns the exact `clamped_value_range<T>` object referenced by the value.
|
||||
|
||||
## Operators
|
||||
|
||||
```cpp
|
||||
@ -143,6 +190,8 @@ floating-point values, assignment and compound-assignment reject NaN.
|
||||
Prefix increment and decrement return a reference to the updated object.
|
||||
Postfix increment and decrement return a copy of the value before it was
|
||||
updated. All four operations move by one and saturate at the applicable bound.
|
||||
`referenced_clamped_value<T>` provides the same conversion, assignment,
|
||||
increment, decrement, and compound-assignment operators.
|
||||
|
||||
## Operations
|
||||
|
||||
@ -153,6 +202,10 @@ ETL_CONSTEXPR14 void swap(clamped_value& lhs, clamped_value& rhs);
|
||||
|
||||
Swaps clamped values. Runtime values also swap their bounds.
|
||||
|
||||
Swapping two `referenced_clamped_value<T>` objects swaps both their current
|
||||
values and range references. Copy construction and copy assignment preserve
|
||||
the source's range identity; they do not copy the range itself.
|
||||
|
||||
Equality, inequality, and relational comparisons are provided. Comparisons
|
||||
against arithmetic values use the implicit conversion to `T`. Equality
|
||||
compares the current values; runtime bounds do not affect equality. Runtime
|
||||
@ -169,3 +222,124 @@ assignments, and steps also trigger an ETL assertion. Depending on the ETL
|
||||
error-handler configuration, an assertion may throw `etl::exception`, invoke a
|
||||
configured handler, or terminate. Operations that validate input are only
|
||||
`noexcept` when exceptions are disabled.
|
||||
|
||||
The same validation applies when constructing `clamped_value_range<T>` and
|
||||
when assigning or advancing a `referenced_clamped_value<T>`.
|
||||
|
||||
## Shared Referenced Ranges
|
||||
|
||||
`clamped_value_range<T>` and `referenced_clamped_value<T>` are available in
|
||||
C++11 and later. They let independent values share immutable runtime bounds.
|
||||
|
||||
### `clamped_value_range<T>`
|
||||
|
||||
```cpp
|
||||
template <typename T>
|
||||
class clamped_value_range;
|
||||
```
|
||||
|
||||
#### Constructors
|
||||
|
||||
```cpp
|
||||
clamped_value_range();
|
||||
clamped_value_range(T min, T max);
|
||||
```
|
||||
|
||||
The default constructor uses the full representable range of `T`. The
|
||||
two-argument constructor stores immutable bounds and rejects reversed or NaN
|
||||
bounds.
|
||||
|
||||
#### Accessors
|
||||
|
||||
```cpp
|
||||
ETL_NODISCARD T min() const noexcept;
|
||||
ETL_NODISCARD T max() const noexcept;
|
||||
```
|
||||
|
||||
### `referenced_clamped_value<T>`
|
||||
|
||||
```cpp
|
||||
template <typename T>
|
||||
class referenced_clamped_value;
|
||||
```
|
||||
|
||||
The class stores a current value and an
|
||||
`etl::reference_wrapper<const clamped_value_range<T>>`. It does not own the
|
||||
range, which must outlive the value and all copies of it.
|
||||
|
||||
Member types are `range_type`, `difference_type`, `limits_type`, and
|
||||
`difference_limits_type`, with the same numeric meanings as `clamped_value<T>`.
|
||||
|
||||
#### Constructors
|
||||
|
||||
```cpp
|
||||
explicit referenced_clamped_value(const range_type& range);
|
||||
referenced_clamped_value(const range_type& range, T initial);
|
||||
referenced_clamped_value(const referenced_clamped_value& other);
|
||||
|
||||
referenced_clamped_value(range_type&&) = delete;
|
||||
referenced_clamped_value(range_type&&, T) = delete;
|
||||
```
|
||||
|
||||
The one-argument constructor initializes to `range.min()`. The two-argument
|
||||
constructor clamps `initial`. Temporary ranges are rejected to prevent an
|
||||
immediately dangling reference. Copy construction preserves the source value
|
||||
and range identity.
|
||||
|
||||
#### Modifiers
|
||||
|
||||
```cpp
|
||||
void set(T value);
|
||||
void to_min() noexcept;
|
||||
void to_max() noexcept;
|
||||
void advance(difference_type n);
|
||||
```
|
||||
|
||||
These operations mirror `clamped_value<T>`. There is no `set(min, max)` because
|
||||
the referenced range is immutable and shared.
|
||||
|
||||
#### Accessors
|
||||
|
||||
```cpp
|
||||
ETL_NODISCARD T get() const noexcept;
|
||||
ETL_NODISCARD T min() const noexcept;
|
||||
ETL_NODISCARD T max() const noexcept;
|
||||
ETL_NODISCARD const range_type& range() const noexcept;
|
||||
ETL_NODISCARD operator T() const noexcept;
|
||||
```
|
||||
|
||||
`range()` returns the exact range object held by reference.
|
||||
|
||||
#### Operators
|
||||
|
||||
```cpp
|
||||
referenced_clamped_value& operator=(const referenced_clamped_value& other) & noexcept;
|
||||
referenced_clamped_value& operator=(T value) &;
|
||||
referenced_clamped_value& operator++() & noexcept;
|
||||
referenced_clamped_value operator++(int) noexcept;
|
||||
referenced_clamped_value& operator--() & noexcept;
|
||||
referenced_clamped_value operator--(int) noexcept;
|
||||
referenced_clamped_value& operator+=(difference_type n) &;
|
||||
referenced_clamped_value& operator-=(difference_type n) &;
|
||||
```
|
||||
|
||||
Copy assignment copies both the current value and range reference. Arithmetic
|
||||
and value assignment clamp to the referenced bounds. Comparisons compare only
|
||||
current values, not range identity or bounds.
|
||||
|
||||
#### Swap
|
||||
|
||||
```cpp
|
||||
void swap(referenced_clamped_value& other) noexcept;
|
||||
void swap(referenced_clamped_value& lhs,
|
||||
referenced_clamped_value& rhs) noexcept;
|
||||
```
|
||||
|
||||
Swap exchanges both current values and range references, keeping each value
|
||||
associated with its original bounds.
|
||||
|
||||
#### Errors
|
||||
|
||||
Invalid or NaN range bounds trigger an ETL assertion. NaN initial values,
|
||||
assignments, and steps are also rejected. Infinite values follow the same
|
||||
behavior as `clamped_value<T>`.
|
||||
|
||||
@ -7,23 +7,32 @@ title: "compare"
|
||||
Since: `TBC`
|
||||
{{< /callout >}}
|
||||
|
||||
A helper class that defines `<=`, `>`, `>=` in terms of `etl::less` or a user supplied compare type.
|
||||
A helper for deriving comparison operations from a strict weak ordering.
|
||||
`etl::compare<T, TLess>` exposes boolean predicates and a three-state comparison result.
|
||||
`etl::three_way_compare` returns `-1`, `0`, or `1` using the same comparison policy.
|
||||
|
||||
```cpp
|
||||
template <typename T, typename TLess = etl::less<T> >
|
||||
struct compare
|
||||
```
|
||||
|
||||
```cpp
|
||||
template <typename T, typename TLess = etl::less<T> >
|
||||
ETL_CONSTEXPR14 int three_way_compare(const T& lhs, const T& rhs)
|
||||
```
|
||||
|
||||
## Types
|
||||
```cpp
|
||||
first_argument_type;
|
||||
second_argument_type;
|
||||
result_type;
|
||||
cmp_result;
|
||||
```
|
||||
|
||||
`cmp_result` has the values `Less`, `Equal`, and `Greater`.
|
||||
|
||||
## Members
|
||||
```cpp
|
||||
static result_type lt(first_argument_type lhs, second_argument_type rhs)
|
||||
static bool lt(first_argument_type lhs, second_argument_type rhs)
|
||||
```
|
||||
**Return**
|
||||
`true` if `lhs < rhs`, otherwise `false`.
|
||||
@ -31,7 +40,7 @@ static result_type lt(first_argument_type lhs, second_argument_type rhs)
|
||||
---
|
||||
|
||||
```cpp
|
||||
static result_type lte(first_argument_type lhs, second_argument_type rhs)
|
||||
static bool lte(first_argument_type lhs, second_argument_type rhs)
|
||||
```
|
||||
**Return**
|
||||
`true` if `lhs <= rhs`, otherwise `false`.
|
||||
@ -39,7 +48,7 @@ static result_type lte(first_argument_type lhs, second_argument_type rhs)
|
||||
---
|
||||
|
||||
```cpp
|
||||
static result_type gt(first_argument_type lhs, second_argument_type rhs)
|
||||
static bool gt(first_argument_type lhs, second_argument_type rhs)
|
||||
```
|
||||
**Return**
|
||||
`true` if `lhs > rhs`, otherwise `false`.
|
||||
@ -47,14 +56,74 @@ static result_type gt(first_argument_type lhs, second_argument_type rhs)
|
||||
---
|
||||
|
||||
```cpp
|
||||
static result_type gte(first_argument_type lhs, second_argument_type rhs)
|
||||
static bool gte(first_argument_type lhs, second_argument_type rhs)
|
||||
```
|
||||
**Return**
|
||||
`true` if `lhs >= rhs`, otherwise `false`.
|
||||
|
||||
---
|
||||
|
||||
```cpp
|
||||
static bool eq(first_argument_type lhs, second_argument_type rhs)
|
||||
```
|
||||
**Return**
|
||||
`true` if `lhs == rhs`, otherwise `false`.
|
||||
|
||||
---
|
||||
|
||||
```cpp
|
||||
static bool ne(first_argument_type lhs, second_argument_type rhs)
|
||||
```
|
||||
**Return**
|
||||
`true` if `lhs != rhs`, otherwise `false`.
|
||||
|
||||
---
|
||||
|
||||
```cpp
|
||||
static cmp_result cmp(first_argument_type lhs, second_argument_type rhs)
|
||||
```
|
||||
**Return**
|
||||
`Less`, `Equal`, or `Greater`.
|
||||
|
||||
## Three-way comparison
|
||||
|
||||
`etl::three_way_compare(lhs, rhs)` returns the same ordering as `compare<T, TLess>::cmp(lhs, rhs)`,
|
||||
but as an `int` with the values `-1`, `0`, or `1`.
|
||||
|
||||
## C++20 comparison categories
|
||||
|
||||
When compiling as C++20, `compare.h` makes the comparison categories available in the `etl`
|
||||
namespace, so ETL and user code can name them regardless of the STL configuration.
|
||||
|
||||
```cpp
|
||||
class strong_ordering;
|
||||
class weak_ordering;
|
||||
class partial_ordering;
|
||||
```
|
||||
|
||||
* When the STL is available these are aliases for `std::strong_ordering`, `std::weak_ordering`
|
||||
and `std::partial_ordering`.
|
||||
* When the STL is not available, `compare.h` provides its own minimal implementations that
|
||||
follow the standard semantics: the named values `less`, `equivalent`, `greater`
|
||||
(plus `equal` for `strong_ordering` and `unordered` for `partial_ordering`), comparison
|
||||
against the literal `0` only, and the implicit conversions
|
||||
`strong_ordering` → `weak_ordering` → `partial_ordering`.
|
||||
|
||||
An `unordered` result compares `false` against `0` for all of `<`, `<=`, `==`, `>=` and `>`.
|
||||
|
||||
```cpp
|
||||
ETL_CONSTEXPR strong_ordering make_strong_ordering(int value) ETL_NOEXCEPT;
|
||||
ETL_CONSTEXPR weak_ordering make_weak_ordering(int value) ETL_NOEXCEPT;
|
||||
ETL_CONSTEXPR partial_ordering make_partial_ordering(int value) ETL_NOEXCEPT;
|
||||
```
|
||||
|
||||
These helpers convert a three-way comparison result, such as the one returned by
|
||||
`etl::three_way_compare`, into a comparison category. Negative values map to `less`, positive
|
||||
values to `greater`, and zero to `equal`/`equivalent`. `unordered` cannot be expressed as an
|
||||
integer result, so use `etl::partial_ordering::unordered` directly for that case.
|
||||
|
||||
## Example 1
|
||||
```cpp
|
||||
using std::less
|
||||
struct Test
|
||||
{
|
||||
int a;
|
||||
@ -84,6 +153,11 @@ bool operator >=(const Test& lhs, const Test& rhs)
|
||||
{
|
||||
return Compare::gte(lhs, rhs);
|
||||
}
|
||||
|
||||
int operator <=>(const Test& lhs, const Test& rhs)
|
||||
{
|
||||
return etl::three_way_compare(lhs, rhs);
|
||||
}
|
||||
```
|
||||
|
||||
## Example 2
|
||||
@ -124,6 +198,11 @@ bool operator >=(const Test& lhs, const Test& rhs)
|
||||
{
|
||||
return Compare::gte(lhs, rhs);
|
||||
}
|
||||
|
||||
int operator <=>(const Test& lhs, const Test& rhs)
|
||||
{
|
||||
return etl::three_way_compare<Test, LessThan>(lhs, rhs);
|
||||
}
|
||||
```
|
||||
|
||||
## Example 3
|
||||
@ -158,3 +237,13 @@ bool operator >=(const Test& lhs, const Test& rhs)
|
||||
return Test::gte(lhs, rhs);
|
||||
}
|
||||
```
|
||||
|
||||
## Example 4
|
||||
C++20, with or without the STL.
|
||||
|
||||
```cpp
|
||||
etl::strong_ordering operator <=>(const Test& lhs, const Test& rhs)
|
||||
{
|
||||
return etl::make_strong_ordering(etl::three_way_compare(lhs, rhs));
|
||||
}
|
||||
```
|
||||
|
||||
@ -137,7 +137,7 @@ namespace etl
|
||||
/// Returns a reference to the value at index 'i'.
|
||||
///\param i The index of the element to access.
|
||||
//*************************************************************************
|
||||
ETL_NODISCARD ETL_CONSTEXPR14 reference operator[](size_t i) ETL_NOEXCEPT_EXPR(ETL_NOT_USING_EXCEPTIONS || ETL_NOT_CHECKING_INDEX_OPERATOR)
|
||||
ETL_NODISCARD ETL_CONSTEXPR14 reference operator[](size_t i) ETL_NOEXCEPT_EXPR(ETL_NOT_CHECKING_INDEX_OPERATOR)
|
||||
{
|
||||
ETL_ASSERT_CHECK_INDEX_OPERATOR(i < SIZE, ETL_ERROR(array_out_of_range));
|
||||
|
||||
@ -149,7 +149,7 @@ namespace etl
|
||||
/// Returns a const reference to the value at index 'i'.
|
||||
///\param i The index of the element to access.
|
||||
//*************************************************************************
|
||||
ETL_NODISCARD ETL_CONSTEXPR const_reference operator[](size_t i) const ETL_NOEXCEPT_EXPR(ETL_NOT_USING_EXCEPTIONS || ETL_NOT_CHECKING_INDEX_OPERATOR)
|
||||
ETL_NODISCARD ETL_CONSTEXPR const_reference operator[](size_t i) const ETL_NOEXCEPT_EXPR(ETL_NOT_CHECKING_INDEX_OPERATOR)
|
||||
{
|
||||
// Throwing from c++11 constexpr requires special syntax
|
||||
#if ETL_USING_CPP11 && ETL_NOT_USING_CPP14 && ETL_USING_EXCEPTIONS && ETL_CHECKING_INDEX_OPERATOR
|
||||
|
||||
@ -518,7 +518,7 @@ namespace etl
|
||||
/// If asserts or exceptions are enabled, throws an etl::array_view_bounds
|
||||
/// if the index is out of bounds.
|
||||
//*************************************************************************
|
||||
reference operator[](const size_t i) ETL_NOEXCEPT_EXPR(ETL_NOT_USING_EXCEPTIONS || ETL_NOT_CHECKING_INDEX_OPERATOR)
|
||||
reference operator[](const size_t i) ETL_NOEXCEPT_EXPR(ETL_NOT_CHECKING_INDEX_OPERATOR)
|
||||
{
|
||||
ETL_ASSERT_CHECK_INDEX_OPERATOR(i < size(), ETL_ERROR(array_view_bounds));
|
||||
|
||||
@ -531,7 +531,7 @@ namespace etl
|
||||
/// If asserts or exceptions are enabled, throws an etl::array_view_bounds
|
||||
/// if the index is out of bounds.
|
||||
//*************************************************************************
|
||||
const_reference operator[](const size_t i) const ETL_NOEXCEPT_EXPR(ETL_NOT_USING_EXCEPTIONS || ETL_NOT_CHECKING_INDEX_OPERATOR)
|
||||
const_reference operator[](const size_t i) const ETL_NOEXCEPT_EXPR(ETL_NOT_CHECKING_INDEX_OPERATOR)
|
||||
{
|
||||
ETL_ASSERT_CHECK_INDEX_OPERATOR(i < size(), ETL_ERROR(array_view_bounds));
|
||||
|
||||
|
||||
@ -271,7 +271,7 @@ namespace etl
|
||||
/// If asserts or exceptions are enabled, throws an
|
||||
/// etl::array_wrapper_bounds if the index is out of bounds.
|
||||
//*************************************************************************
|
||||
reference operator[](size_t i) ETL_NOEXCEPT_EXPR(ETL_NOT_USING_EXCEPTIONS || ETL_NOT_CHECKING_INDEX_OPERATOR)
|
||||
reference operator[](size_t i) ETL_NOEXCEPT_EXPR(ETL_NOT_CHECKING_INDEX_OPERATOR)
|
||||
{
|
||||
ETL_ASSERT_CHECK_INDEX_OPERATOR(i < SIZE, ETL_ERROR(etl::array_wrapper_bounds));
|
||||
return ARRAY_[i];
|
||||
@ -282,7 +282,7 @@ namespace etl
|
||||
/// If asserts or exceptions are enabled, throws an
|
||||
/// etl::array_wrapper_bounds if the index is out of bounds.
|
||||
//*************************************************************************
|
||||
ETL_CONSTEXPR const_reference operator[](size_t i) const ETL_NOEXCEPT_EXPR(ETL_NOT_USING_EXCEPTIONS || ETL_NOT_CHECKING_INDEX_OPERATOR)
|
||||
ETL_CONSTEXPR const_reference operator[](size_t i) const ETL_NOEXCEPT_EXPR(ETL_NOT_CHECKING_INDEX_OPERATOR)
|
||||
{
|
||||
// Throwing from c++11 constexpr requires special syntax
|
||||
#if ETL_USING_CPP11 && ETL_NOT_USING_CPP14 && ETL_USING_EXCEPTIONS && ETL_CHECKING_INDEX_OPERATOR
|
||||
|
||||
@ -411,6 +411,16 @@ namespace etl
|
||||
return false;
|
||||
}
|
||||
|
||||
//*******************************************
|
||||
/// Returns true if an attempt was ever made to insert a timer into the
|
||||
/// active list when it was already there. Such an insert is rejected, as
|
||||
/// it would create a circular reference in the list.
|
||||
//*******************************************
|
||||
bool duplicate_insert_detected() const
|
||||
{
|
||||
return active_list.duplicate_insert_detected();
|
||||
}
|
||||
|
||||
//*******************************************
|
||||
/// Check if there is an active timer.
|
||||
//*******************************************
|
||||
@ -630,6 +640,7 @@ namespace etl
|
||||
: head(etl::timer::id::NO_TIMER)
|
||||
, tail(etl::timer::id::NO_TIMER)
|
||||
, ptimers(ptimers_)
|
||||
, duplicate_insert(false)
|
||||
{
|
||||
}
|
||||
|
||||
@ -639,6 +650,15 @@ namespace etl
|
||||
return head == etl::timer::id::NO_TIMER;
|
||||
}
|
||||
|
||||
//*******************************
|
||||
/// Returns true if an attempt was ever made to insert a timer that was
|
||||
/// already in the list.
|
||||
//*******************************
|
||||
bool duplicate_insert_detected() const
|
||||
{
|
||||
return duplicate_insert;
|
||||
}
|
||||
|
||||
//*******************************
|
||||
// Inserts the timer at the correct delta position
|
||||
//*******************************
|
||||
@ -646,6 +666,16 @@ namespace etl
|
||||
{
|
||||
timer_data& timer = ptimers[id_];
|
||||
|
||||
// A timer is in this list exactly when it is the head or has a previous
|
||||
// link, as remove() clears both links. Inserting one that is already
|
||||
// here would link it to itself and create a circular reference, so
|
||||
// record the attempt and abandon the insert to keep the list intact.
|
||||
if ((head == id_) || (timer.previous != etl::timer::id::NO_TIMER))
|
||||
{
|
||||
duplicate_insert = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (head == etl::timer::id::NO_TIMER)
|
||||
{
|
||||
// No entries yet.
|
||||
@ -788,6 +818,10 @@ namespace etl
|
||||
etl::timer::id::type tail;
|
||||
|
||||
timer_data* const ptimers;
|
||||
|
||||
// Set if an attempt was ever made to insert a timer that was already
|
||||
// in the list.
|
||||
bool duplicate_insert;
|
||||
};
|
||||
|
||||
// The array of timer data structures.
|
||||
|
||||
@ -174,12 +174,9 @@ namespace etl
|
||||
active_list.remove(timer.id, true);
|
||||
remove_callback.call_if(timer.id);
|
||||
|
||||
if (timer.callback.is_valid())
|
||||
{
|
||||
// Call the delegate callback.
|
||||
timer.callback();
|
||||
}
|
||||
|
||||
// The timer is reinserted before the callback is called, so that
|
||||
// a callback that restarts the timer replaces this entry instead
|
||||
// of the timer being inserted into the active list twice.
|
||||
if (timer.repeating)
|
||||
{
|
||||
// Reinsert the timer.
|
||||
@ -188,6 +185,11 @@ namespace etl
|
||||
insert_callback.call_if(timer.id);
|
||||
}
|
||||
|
||||
if (timer.callback.is_valid())
|
||||
{
|
||||
timer.callback();
|
||||
}
|
||||
|
||||
has_active = !active_list.empty();
|
||||
}
|
||||
|
||||
@ -301,6 +303,16 @@ namespace etl
|
||||
return false;
|
||||
}
|
||||
|
||||
//*******************************************
|
||||
/// Returns true if an attempt was ever made to insert a timer into the
|
||||
/// active list when it was already there. Such an insert is rejected, as
|
||||
/// it would create a circular reference in the list.
|
||||
//*******************************************
|
||||
bool duplicate_insert_detected() const
|
||||
{
|
||||
return active_list.duplicate_insert_detected();
|
||||
}
|
||||
|
||||
//*******************************************
|
||||
/// Check if there is an active timer.
|
||||
//*******************************************
|
||||
@ -486,6 +498,7 @@ namespace etl
|
||||
: head(etl::timer::id::NO_TIMER)
|
||||
, tail(etl::timer::id::NO_TIMER)
|
||||
, ptimers(ptimers_)
|
||||
, duplicate_insert(false)
|
||||
{
|
||||
}
|
||||
|
||||
@ -495,6 +508,15 @@ namespace etl
|
||||
return head == etl::timer::id::NO_TIMER;
|
||||
}
|
||||
|
||||
//*******************************
|
||||
/// Returns true if an attempt was ever made to insert a timer that was
|
||||
/// already in the list.
|
||||
//*******************************
|
||||
bool duplicate_insert_detected() const
|
||||
{
|
||||
return duplicate_insert;
|
||||
}
|
||||
|
||||
//*******************************
|
||||
// Inserts the timer at the correct delta position
|
||||
//*******************************
|
||||
@ -502,6 +524,16 @@ namespace etl
|
||||
{
|
||||
timer_data& timer = ptimers[id_];
|
||||
|
||||
// A timer is in this list exactly when it is the head or has a previous
|
||||
// link, as remove() clears both links. Inserting one that is already
|
||||
// here would link it to itself and create a circular reference, so
|
||||
// record the attempt and abandon the insert to keep the list intact.
|
||||
if ((head == id_) || (timer.previous != etl::timer::id::NO_TIMER))
|
||||
{
|
||||
duplicate_insert = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (head == etl::timer::id::NO_TIMER)
|
||||
{
|
||||
// No entries yet.
|
||||
@ -644,6 +676,10 @@ namespace etl
|
||||
etl::timer::id::type tail;
|
||||
|
||||
timer_data* const ptimers;
|
||||
|
||||
// Set if an attempt was ever made to insert a timer that was already
|
||||
// in the list.
|
||||
bool duplicate_insert;
|
||||
};
|
||||
|
||||
// The array of timer data structures.
|
||||
|
||||
@ -178,11 +178,9 @@ namespace etl
|
||||
active_list.remove(timer.id, true);
|
||||
remove_callback.call_if(timer.id);
|
||||
|
||||
if (timer.callback.is_valid())
|
||||
{
|
||||
timer.callback();
|
||||
}
|
||||
|
||||
// The timer is reinserted before the callback is called, so that
|
||||
// a callback that restarts the timer replaces this entry instead
|
||||
// of the timer being inserted into the active list twice.
|
||||
if (timer.repeating)
|
||||
{
|
||||
// Reinsert the timer.
|
||||
@ -191,6 +189,11 @@ namespace etl
|
||||
insert_callback.call_if(timer.id);
|
||||
}
|
||||
|
||||
if (timer.callback.is_valid())
|
||||
{
|
||||
timer.callback();
|
||||
}
|
||||
|
||||
has_active = !active_list.empty();
|
||||
}
|
||||
|
||||
@ -305,6 +308,16 @@ namespace etl
|
||||
return false;
|
||||
}
|
||||
|
||||
//*******************************************
|
||||
/// Returns true if an attempt was ever made to insert a timer into the
|
||||
/// active list when it was already there. Such an insert is rejected, as
|
||||
/// it would create a circular reference in the list.
|
||||
//*******************************************
|
||||
bool duplicate_insert_detected() const
|
||||
{
|
||||
return active_list.duplicate_insert_detected();
|
||||
}
|
||||
|
||||
//*******************************************
|
||||
/// Check if there is an active timer.
|
||||
//*******************************************
|
||||
@ -487,6 +500,7 @@ namespace etl
|
||||
: head(etl::timer::id::NO_TIMER)
|
||||
, tail(etl::timer::id::NO_TIMER)
|
||||
, ptimers(ptimers_)
|
||||
, duplicate_insert(false)
|
||||
{
|
||||
}
|
||||
|
||||
@ -496,6 +510,15 @@ namespace etl
|
||||
return head == etl::timer::id::NO_TIMER;
|
||||
}
|
||||
|
||||
//*******************************
|
||||
/// Returns true if an attempt was ever made to insert a timer that was
|
||||
/// already in the list.
|
||||
//*******************************
|
||||
bool duplicate_insert_detected() const
|
||||
{
|
||||
return duplicate_insert;
|
||||
}
|
||||
|
||||
//*******************************
|
||||
// Inserts the timer at the correct delta position
|
||||
//*******************************
|
||||
@ -503,6 +526,16 @@ namespace etl
|
||||
{
|
||||
timer_data& timer = ptimers[id_];
|
||||
|
||||
// A timer is in this list exactly when it is the head or has a previous
|
||||
// link, as remove() clears both links. Inserting one that is already
|
||||
// here would link it to itself and create a circular reference, so
|
||||
// record the attempt and abandon the insert to keep the list intact.
|
||||
if ((head == id_) || (timer.previous != etl::timer::id::NO_TIMER))
|
||||
{
|
||||
duplicate_insert = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (head == etl::timer::id::NO_TIMER)
|
||||
{
|
||||
// No entries yet.
|
||||
@ -645,6 +678,10 @@ namespace etl
|
||||
etl::timer::id::type tail;
|
||||
|
||||
timer_data* const ptimers;
|
||||
|
||||
// Set if an attempt was ever made to insert a timer that was already
|
||||
// in the list.
|
||||
bool duplicate_insert;
|
||||
};
|
||||
|
||||
// The array of timer data structures.
|
||||
|
||||
@ -264,6 +264,16 @@ namespace etl
|
||||
unlock = unlock_;
|
||||
}
|
||||
|
||||
//*******************************************
|
||||
/// Returns true if an attempt was ever made to insert a timer into the
|
||||
/// active list when it was already there. Such an insert is rejected, as
|
||||
/// it would create a circular reference in the list.
|
||||
//*******************************************
|
||||
bool duplicate_insert_detected() const
|
||||
{
|
||||
return active_list.duplicate_insert_detected();
|
||||
}
|
||||
|
||||
//*******************************************
|
||||
/// Check if there is an active timer.
|
||||
//*******************************************
|
||||
@ -460,6 +470,7 @@ namespace etl
|
||||
: head(etl::timer::id::NO_TIMER)
|
||||
, tail(etl::timer::id::NO_TIMER)
|
||||
, ptimers(ptimers_)
|
||||
, duplicate_insert(false)
|
||||
{
|
||||
}
|
||||
|
||||
@ -469,6 +480,15 @@ namespace etl
|
||||
return head == etl::timer::id::NO_TIMER;
|
||||
}
|
||||
|
||||
//*******************************
|
||||
/// Returns true if an attempt was ever made to insert a timer that was
|
||||
/// already in the list.
|
||||
//*******************************
|
||||
bool duplicate_insert_detected() const
|
||||
{
|
||||
return duplicate_insert;
|
||||
}
|
||||
|
||||
//*******************************
|
||||
// Inserts the timer at the correct delta position
|
||||
//*******************************
|
||||
@ -476,6 +496,16 @@ namespace etl
|
||||
{
|
||||
timer_data& timer = ptimers[id_];
|
||||
|
||||
// A timer is in this list exactly when it is the head or has a previous
|
||||
// link, as remove() clears both links. Inserting one that is already
|
||||
// here would link it to itself and create a circular reference, so
|
||||
// record the attempt and abandon the insert to keep the list intact.
|
||||
if ((head == id_) || (timer.previous != etl::timer::id::NO_TIMER))
|
||||
{
|
||||
duplicate_insert = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (head == etl::timer::id::NO_TIMER)
|
||||
{
|
||||
// No entries yet.
|
||||
@ -618,6 +648,10 @@ namespace etl
|
||||
etl::timer::id::type tail;
|
||||
|
||||
timer_data* const ptimers;
|
||||
|
||||
// Set if an attempt was ever made to insert a timer that was already
|
||||
// in the list.
|
||||
bool duplicate_insert;
|
||||
};
|
||||
|
||||
//*******************************************
|
||||
@ -716,11 +750,9 @@ namespace etl
|
||||
active_list.remove(timer.id, true);
|
||||
remove_callback.call_if(timer.id);
|
||||
|
||||
if (timer.callback.is_valid())
|
||||
{
|
||||
timer.callback();
|
||||
}
|
||||
|
||||
// The timer is reinserted before the callback is called, so that
|
||||
// a callback that restarts the timer replaces this entry instead
|
||||
// of the timer being inserted into the active list twice.
|
||||
if (timer.repeating)
|
||||
{
|
||||
// Reinsert the timer.
|
||||
@ -729,6 +761,11 @@ namespace etl
|
||||
insert_callback.call_if(timer.id);
|
||||
}
|
||||
|
||||
if (timer.callback.is_valid())
|
||||
{
|
||||
timer.callback();
|
||||
}
|
||||
|
||||
has_active = !active_list.empty();
|
||||
}
|
||||
|
||||
|
||||
@ -37,6 +37,7 @@ SOFTWARE.
|
||||
|
||||
#if ETL_USING_CPP11
|
||||
|
||||
#include "compare.h"
|
||||
#include "hash.h"
|
||||
#include "integral_limits.h"
|
||||
#include "type_traits.h"
|
||||
|
||||
@ -35,6 +35,7 @@ SOFTWARE.
|
||||
#include "absolute.h"
|
||||
#include "algorithm.h"
|
||||
#include "error_handler.h"
|
||||
#include "functional.h"
|
||||
#include "limits.h"
|
||||
#include "math.h"
|
||||
#include "static_assert.h"
|
||||
@ -978,6 +979,400 @@ namespace etl
|
||||
T min_value;
|
||||
T max_value;
|
||||
};
|
||||
|
||||
#if ETL_USING_CPP11
|
||||
//***************************************************************************
|
||||
/// An immutable pair of runtime limits that may be shared by multiple
|
||||
/// referenced_clamped_value objects.
|
||||
///@tparam T An integral type, or a floating-point type when
|
||||
/// ETL_HAS_FLOATING_POINT_CLAMPED_VALUE is `1`.
|
||||
///\ingroup clamped_value
|
||||
//***************************************************************************
|
||||
template <typename T>
|
||||
class clamped_value_range
|
||||
{
|
||||
public:
|
||||
|
||||
typedef typename private_clamped_value::traits<T>::limits_type limits_type;
|
||||
|
||||
//*************************************************************************
|
||||
/// Constructs the full representable range of T.
|
||||
//*************************************************************************
|
||||
ETL_CONSTEXPR14 clamped_value_range()
|
||||
: min_value(limits_type::lowest())
|
||||
, max_value(limits_type::max())
|
||||
{
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
/// Constructs a range with the supplied limits.
|
||||
/// Reversed bounds and NaN bounds are rejected.
|
||||
///\param min_ The minimum value.
|
||||
///\param max_ The maximum value.
|
||||
//*************************************************************************
|
||||
ETL_CONSTEXPR14 clamped_value_range(T min_, T max_)
|
||||
: min_value(min_)
|
||||
, max_value(max_)
|
||||
{
|
||||
private_clamped_value::validate(min_);
|
||||
private_clamped_value::validate(max_);
|
||||
ETL_ASSERT(min_ <= max_, ETL_ERROR_GENERIC("clamped_value_range: invalid range"));
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
/// Gets the minimum value.
|
||||
//*************************************************************************
|
||||
ETL_NODISCARD ETL_CONSTEXPR T min() const ETL_NOEXCEPT
|
||||
{
|
||||
return min_value;
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
/// Gets the maximum value.
|
||||
//*************************************************************************
|
||||
ETL_NODISCARD ETL_CONSTEXPR T max() const ETL_NOEXCEPT
|
||||
{
|
||||
return max_value;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
const T min_value;
|
||||
const T max_value;
|
||||
};
|
||||
|
||||
//***************************************************************************
|
||||
/// Provides a value clamped to a shared immutable runtime range.
|
||||
/// The referenced range must outlive this object.
|
||||
///@tparam T An integral type, or a floating-point type when
|
||||
/// ETL_HAS_FLOATING_POINT_CLAMPED_VALUE is `1`.
|
||||
///\ingroup clamped_value
|
||||
//***************************************************************************
|
||||
template <typename T>
|
||||
class referenced_clamped_value
|
||||
{
|
||||
public:
|
||||
|
||||
/// The referenced range type.
|
||||
typedef clamped_value_range<T> range_type;
|
||||
|
||||
/// The type used to advance or subtract from the stored value.
|
||||
typedef typename private_clamped_value::traits<T>::difference_type difference_type;
|
||||
|
||||
/// Numeric limits for the stored value type.
|
||||
typedef typename private_clamped_value::traits<T>::limits_type limits_type;
|
||||
|
||||
/// Numeric limits for difference_type.
|
||||
typedef typename private_clamped_value::traits<T>::difference_limits_type difference_limits_type;
|
||||
|
||||
//*************************************************************************
|
||||
/// Constructs a value initialized to the minimum of the referenced range.
|
||||
///\param range_ The range to reference. It must outlive this object.
|
||||
//*************************************************************************
|
||||
explicit referenced_clamped_value(const range_type& range_)
|
||||
: value(range_.min())
|
||||
, value_range(etl::cref(range_))
|
||||
{
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
/// Constructs a value clamped to the referenced range.
|
||||
///\param range_ The range to reference. It must outlive this object.
|
||||
///\param initial The initial value.
|
||||
//*************************************************************************
|
||||
referenced_clamped_value(const range_type& range_, T initial)
|
||||
: value(private_clamped_value::validated_clamp(initial, range_.min(), range_.max()))
|
||||
, value_range(etl::cref(range_))
|
||||
{
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
/// Prevents construction with a temporary range.
|
||||
//*************************************************************************
|
||||
referenced_clamped_value(range_type&&) = delete;
|
||||
referenced_clamped_value(range_type&&, T) = delete;
|
||||
|
||||
//*************************************************************************
|
||||
/// Copies the value and its range reference.
|
||||
///\param other The value to copy.
|
||||
//*************************************************************************
|
||||
referenced_clamped_value(const referenced_clamped_value& other) ETL_NOEXCEPT
|
||||
: value(other.value)
|
||||
, value_range(other.value_range)
|
||||
{
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
/// Copy assignment. Copies the value and its range reference.
|
||||
///\param other The value and range reference to copy.
|
||||
///\return A reference to this value.
|
||||
//*************************************************************************
|
||||
referenced_clamped_value& operator=(const referenced_clamped_value& other) ETL_LVALUE_REF_QUALIFIER ETL_NOEXCEPT
|
||||
{
|
||||
if (this != &other)
|
||||
{
|
||||
value = other.value;
|
||||
value_range = other.value_range;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
/// Sets the value and clamps it to the referenced range.
|
||||
///\param value_ The value to assign.
|
||||
//*************************************************************************
|
||||
void set(T value_)
|
||||
{
|
||||
value = private_clamped_value::validated_clamp(value_, min(), max());
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
/// Resets the value to the minimum of the referenced range.
|
||||
//*************************************************************************
|
||||
void to_min() ETL_NOEXCEPT
|
||||
{
|
||||
value = min();
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
/// Resets the value to the maximum of the referenced range.
|
||||
//*************************************************************************
|
||||
void to_max() ETL_NOEXCEPT
|
||||
{
|
||||
value = max();
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
/// Advances the value and saturates it at the referenced bounds.
|
||||
///\param n The difference to add.
|
||||
//*************************************************************************
|
||||
void advance(difference_type n)
|
||||
{
|
||||
private_clamped_value::validate(n);
|
||||
value = private_clamped_value::advance(value, min(), max(), n);
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
/// Converts to the underlying value type.
|
||||
///\return The current value.
|
||||
//*************************************************************************
|
||||
ETL_NODISCARD operator T() const ETL_NOEXCEPT
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
/// Advances by one and saturates at the maximum.
|
||||
///\return A reference to this value.
|
||||
//*************************************************************************
|
||||
referenced_clamped_value& operator++() ETL_LVALUE_REF_QUALIFIER ETL_NOEXCEPT
|
||||
{
|
||||
value = private_clamped_value::advance(value, min(), max(), difference_type(1));
|
||||
return *this;
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
/// Advances by one and returns the previous value.
|
||||
///\return The value before incrementing.
|
||||
//*************************************************************************
|
||||
referenced_clamped_value operator++(int) ETL_NOEXCEPT
|
||||
{
|
||||
referenced_clamped_value temp(*this);
|
||||
++(*this);
|
||||
return temp;
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
/// Subtracts one and saturates at the minimum.
|
||||
///\return A reference to this value.
|
||||
//*************************************************************************
|
||||
referenced_clamped_value& operator--() ETL_LVALUE_REF_QUALIFIER ETL_NOEXCEPT
|
||||
{
|
||||
value = private_clamped_value::advance(value, min(), max(), difference_type(-1));
|
||||
return *this;
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
/// Subtracts one and returns the previous value.
|
||||
///\return The value before decrementing.
|
||||
//*************************************************************************
|
||||
referenced_clamped_value operator--(int) ETL_NOEXCEPT
|
||||
{
|
||||
referenced_clamped_value temp(*this);
|
||||
--(*this);
|
||||
return temp;
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
/// Assigns a value and clamps it to the referenced range.
|
||||
///\param value_ The value to assign.
|
||||
///\return A reference to this value.
|
||||
//*************************************************************************
|
||||
referenced_clamped_value& operator=(T value_) ETL_LVALUE_REF_QUALIFIER
|
||||
{
|
||||
set(value_);
|
||||
return *this;
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
/// Adds a difference and clamps to the referenced range.
|
||||
///\param n The difference to add.
|
||||
///\return A reference to this value.
|
||||
//*************************************************************************
|
||||
referenced_clamped_value& operator+=(difference_type n) ETL_LVALUE_REF_QUALIFIER
|
||||
{
|
||||
advance(n);
|
||||
return *this;
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
/// Subtracts a difference and clamps to the referenced range.
|
||||
///\param n The difference to subtract.
|
||||
///\return A reference to this value.
|
||||
//*************************************************************************
|
||||
referenced_clamped_value& operator-=(difference_type n) ETL_LVALUE_REF_QUALIFIER
|
||||
{
|
||||
private_clamped_value::validate(n);
|
||||
value = private_clamped_value::subtract(value, min(), max(), n);
|
||||
return *this;
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
/// Gets the current value.
|
||||
///\return The current value.
|
||||
//*************************************************************************
|
||||
ETL_NODISCARD
|
||||
T get() const ETL_NOEXCEPT
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
/// Gets the minimum of the referenced range.
|
||||
///\return The minimum value.
|
||||
//*************************************************************************
|
||||
ETL_NODISCARD
|
||||
T min() const ETL_NOEXCEPT
|
||||
{
|
||||
return value_range.get().min();
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
/// Gets the maximum of the referenced range.
|
||||
///\return The maximum value.
|
||||
//*************************************************************************
|
||||
ETL_NODISCARD
|
||||
T max() const ETL_NOEXCEPT
|
||||
{
|
||||
return value_range.get().max();
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
/// Gets the referenced range.
|
||||
///\return The referenced range.
|
||||
//*************************************************************************
|
||||
ETL_NODISCARD
|
||||
const range_type& range() const ETL_NOEXCEPT
|
||||
{
|
||||
return value_range.get();
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
/// Swaps the values and range references.
|
||||
///\param other The value to swap with.
|
||||
//*************************************************************************
|
||||
void swap(referenced_clamped_value& other) ETL_NOEXCEPT
|
||||
{
|
||||
using ETL_OR_STD::swap;
|
||||
swap(value, other.value);
|
||||
swap(value_range, other.value_range);
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
/// Swaps the values and range references.
|
||||
///\param lhs The first value.
|
||||
///\param rhs The second value.
|
||||
//*************************************************************************
|
||||
friend void swap(referenced_clamped_value& lhs, referenced_clamped_value& rhs) ETL_NOEXCEPT
|
||||
{
|
||||
lhs.swap(rhs);
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
/// Compares current values for equality.
|
||||
///\param lhs The left-hand value.
|
||||
///\param rhs The right-hand value.
|
||||
///\return `true` if the current values are equal.
|
||||
//*************************************************************************
|
||||
friend bool operator==(const referenced_clamped_value& lhs, const referenced_clamped_value& rhs) ETL_NOEXCEPT
|
||||
{
|
||||
#include "private/diagnostic_float_equal_push.h"
|
||||
return lhs.value == rhs.value;
|
||||
#include "private/diagnostic_pop.h"
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
/// Compares current values for inequality.
|
||||
///\param lhs The left-hand value.
|
||||
///\param rhs The right-hand value.
|
||||
///\return `true` if the current values differ.
|
||||
//*************************************************************************
|
||||
friend bool operator!=(const referenced_clamped_value& lhs, const referenced_clamped_value& rhs) ETL_NOEXCEPT
|
||||
{
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
/// Compares current values using less-than.
|
||||
///\param lhs The left-hand value.
|
||||
///\param rhs The right-hand value.
|
||||
///\return `true` if lhs is less than rhs.
|
||||
//*************************************************************************
|
||||
friend bool operator<(const referenced_clamped_value& lhs, const referenced_clamped_value& rhs) ETL_NOEXCEPT
|
||||
{
|
||||
return lhs.value < rhs.value;
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
/// Compares current values using less-than-or-equal.
|
||||
///\param lhs The left-hand value.
|
||||
///\param rhs The right-hand value.
|
||||
///\return `true` if lhs is less than or equal to rhs.
|
||||
//*************************************************************************
|
||||
friend bool operator<=(const referenced_clamped_value& lhs, const referenced_clamped_value& rhs) ETL_NOEXCEPT
|
||||
{
|
||||
return !(rhs < lhs);
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
/// Compares current values using greater-than.
|
||||
///\param lhs The left-hand value.
|
||||
///\param rhs The right-hand value.
|
||||
///\return `true` if lhs is greater than rhs.
|
||||
//*************************************************************************
|
||||
friend bool operator>(const referenced_clamped_value& lhs, const referenced_clamped_value& rhs) ETL_NOEXCEPT
|
||||
{
|
||||
return rhs < lhs;
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
/// Compares current values using greater-than-or-equal.
|
||||
///\param lhs The left-hand value.
|
||||
///\param rhs The right-hand value.
|
||||
///\return `true` if lhs is greater than or equal to rhs.
|
||||
//*************************************************************************
|
||||
friend bool operator>=(const referenced_clamped_value& lhs, const referenced_clamped_value& rhs) ETL_NOEXCEPT
|
||||
{
|
||||
return !(lhs < rhs);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
T value;
|
||||
etl::reference_wrapper<const range_type> value_range;
|
||||
};
|
||||
#endif
|
||||
} // namespace etl
|
||||
|
||||
#endif
|
||||
|
||||
@ -35,6 +35,10 @@ SOFTWARE.
|
||||
#include "functional.h"
|
||||
#include "parameter_type.h"
|
||||
|
||||
#if ETL_USING_CPP20 && ETL_USING_STL
|
||||
#include <compare>
|
||||
#endif
|
||||
|
||||
//*****************************************************************************
|
||||
///\defgroup compare compare
|
||||
/// Comparisons only using less than operator
|
||||
@ -42,6 +46,295 @@ SOFTWARE.
|
||||
//*****************************************************************************
|
||||
namespace etl
|
||||
{
|
||||
#if ETL_USING_CPP20
|
||||
#if ETL_USING_STL
|
||||
//***************************************************************************
|
||||
/// The STL supplies the C++20 comparison categories, so simply expose them
|
||||
/// through the etl namespace. This allows ETL code and user code to refer to
|
||||
/// etl::strong_ordering (etc.) regardless of the STL configuration.
|
||||
//***************************************************************************
|
||||
using std::partial_ordering;
|
||||
using std::strong_ordering;
|
||||
using std::weak_ordering;
|
||||
#else
|
||||
namespace private_compare
|
||||
{
|
||||
//*************************************************************************
|
||||
/// A type that can only be constructed from the literal 0.
|
||||
/// Used to constrain the comparison-category operators to comparisons
|
||||
/// against the literal 0, as required by the standard.
|
||||
//*************************************************************************
|
||||
struct only_literal_zero
|
||||
{
|
||||
ETL_CONSTEXPR only_literal_zero(only_literal_zero*) ETL_NOEXCEPT {}
|
||||
};
|
||||
|
||||
typedef signed char ordering_value_type;
|
||||
|
||||
inline constexpr ordering_value_type ordering_less = -1;
|
||||
inline constexpr ordering_value_type ordering_equivalent = 0;
|
||||
inline constexpr ordering_value_type ordering_greater = 1;
|
||||
inline constexpr ordering_value_type ordering_unordered = 2;
|
||||
} // namespace private_compare
|
||||
|
||||
//***************************************************************************
|
||||
/// Minimal partial-ordering category for C++20 three-way comparisons when the
|
||||
/// STL is unavailable.
|
||||
/// Unlike the other categories this one has an 'unordered' state, for which
|
||||
/// every comparison against 0 is false.
|
||||
//***************************************************************************
|
||||
class partial_ordering
|
||||
{
|
||||
public:
|
||||
|
||||
static const partial_ordering less;
|
||||
static const partial_ordering equivalent;
|
||||
static const partial_ordering greater;
|
||||
static const partial_ordering unordered;
|
||||
|
||||
friend ETL_CONSTEXPR bool operator==(partial_ordering lhs, partial_ordering rhs) ETL_NOEXCEPT = default;
|
||||
|
||||
friend ETL_CONSTEXPR bool operator==(partial_ordering v, private_compare::only_literal_zero) ETL_NOEXCEPT
|
||||
{
|
||||
return v.value == private_compare::ordering_equivalent;
|
||||
}
|
||||
friend ETL_CONSTEXPR bool operator<(partial_ordering v, private_compare::only_literal_zero) ETL_NOEXCEPT
|
||||
{
|
||||
return v.value == private_compare::ordering_less;
|
||||
}
|
||||
friend ETL_CONSTEXPR bool operator>(partial_ordering v, private_compare::only_literal_zero) ETL_NOEXCEPT
|
||||
{
|
||||
return v.value == private_compare::ordering_greater;
|
||||
}
|
||||
friend ETL_CONSTEXPR bool operator<=(partial_ordering v, private_compare::only_literal_zero) ETL_NOEXCEPT
|
||||
{
|
||||
return (v.value == private_compare::ordering_less) || (v.value == private_compare::ordering_equivalent);
|
||||
}
|
||||
friend ETL_CONSTEXPR bool operator>=(partial_ordering v, private_compare::only_literal_zero) ETL_NOEXCEPT
|
||||
{
|
||||
return (v.value == private_compare::ordering_greater) || (v.value == private_compare::ordering_equivalent);
|
||||
}
|
||||
|
||||
friend ETL_CONSTEXPR bool operator<(private_compare::only_literal_zero, partial_ordering v) ETL_NOEXCEPT
|
||||
{
|
||||
return v > 0;
|
||||
}
|
||||
friend ETL_CONSTEXPR bool operator>(private_compare::only_literal_zero, partial_ordering v) ETL_NOEXCEPT
|
||||
{
|
||||
return v < 0;
|
||||
}
|
||||
friend ETL_CONSTEXPR bool operator<=(private_compare::only_literal_zero, partial_ordering v) ETL_NOEXCEPT
|
||||
{
|
||||
return v >= 0;
|
||||
}
|
||||
friend ETL_CONSTEXPR bool operator>=(private_compare::only_literal_zero, partial_ordering v) ETL_NOEXCEPT
|
||||
{
|
||||
return v <= 0;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
explicit ETL_CONSTEXPR partial_ordering(private_compare::ordering_value_type value_) ETL_NOEXCEPT
|
||||
: value(value_)
|
||||
{
|
||||
}
|
||||
|
||||
friend class weak_ordering;
|
||||
friend class strong_ordering;
|
||||
|
||||
private_compare::ordering_value_type value;
|
||||
};
|
||||
|
||||
inline constexpr partial_ordering partial_ordering::less(private_compare::ordering_less);
|
||||
inline constexpr partial_ordering partial_ordering::equivalent(private_compare::ordering_equivalent);
|
||||
inline constexpr partial_ordering partial_ordering::greater(private_compare::ordering_greater);
|
||||
inline constexpr partial_ordering partial_ordering::unordered(private_compare::ordering_unordered);
|
||||
|
||||
//***************************************************************************
|
||||
/// Minimal weak-ordering category for C++20 three-way comparisons when the
|
||||
/// STL is unavailable.
|
||||
//***************************************************************************
|
||||
class weak_ordering
|
||||
{
|
||||
public:
|
||||
|
||||
static const weak_ordering less;
|
||||
static const weak_ordering equivalent;
|
||||
static const weak_ordering greater;
|
||||
|
||||
ETL_CONSTEXPR operator partial_ordering() const ETL_NOEXCEPT
|
||||
{
|
||||
return partial_ordering(value);
|
||||
}
|
||||
|
||||
friend ETL_CONSTEXPR bool operator==(weak_ordering lhs, weak_ordering rhs) ETL_NOEXCEPT = default;
|
||||
|
||||
friend ETL_CONSTEXPR bool operator==(weak_ordering v, private_compare::only_literal_zero) ETL_NOEXCEPT
|
||||
{
|
||||
return v.value == private_compare::ordering_equivalent;
|
||||
}
|
||||
friend ETL_CONSTEXPR bool operator<(weak_ordering v, private_compare::only_literal_zero) ETL_NOEXCEPT
|
||||
{
|
||||
return v.value < private_compare::ordering_equivalent;
|
||||
}
|
||||
friend ETL_CONSTEXPR bool operator>(weak_ordering v, private_compare::only_literal_zero) ETL_NOEXCEPT
|
||||
{
|
||||
return v.value > private_compare::ordering_equivalent;
|
||||
}
|
||||
friend ETL_CONSTEXPR bool operator<=(weak_ordering v, private_compare::only_literal_zero) ETL_NOEXCEPT
|
||||
{
|
||||
return v.value <= private_compare::ordering_equivalent;
|
||||
}
|
||||
friend ETL_CONSTEXPR bool operator>=(weak_ordering v, private_compare::only_literal_zero) ETL_NOEXCEPT
|
||||
{
|
||||
return v.value >= private_compare::ordering_equivalent;
|
||||
}
|
||||
|
||||
friend ETL_CONSTEXPR bool operator<(private_compare::only_literal_zero, weak_ordering v) ETL_NOEXCEPT
|
||||
{
|
||||
return v > 0;
|
||||
}
|
||||
friend ETL_CONSTEXPR bool operator>(private_compare::only_literal_zero, weak_ordering v) ETL_NOEXCEPT
|
||||
{
|
||||
return v < 0;
|
||||
}
|
||||
friend ETL_CONSTEXPR bool operator<=(private_compare::only_literal_zero, weak_ordering v) ETL_NOEXCEPT
|
||||
{
|
||||
return v >= 0;
|
||||
}
|
||||
friend ETL_CONSTEXPR bool operator>=(private_compare::only_literal_zero, weak_ordering v) ETL_NOEXCEPT
|
||||
{
|
||||
return v <= 0;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
explicit ETL_CONSTEXPR weak_ordering(private_compare::ordering_value_type value_) ETL_NOEXCEPT
|
||||
: value(value_)
|
||||
{
|
||||
}
|
||||
|
||||
friend class strong_ordering;
|
||||
|
||||
private_compare::ordering_value_type value;
|
||||
};
|
||||
|
||||
inline constexpr weak_ordering weak_ordering::less(private_compare::ordering_less);
|
||||
inline constexpr weak_ordering weak_ordering::equivalent(private_compare::ordering_equivalent);
|
||||
inline constexpr weak_ordering weak_ordering::greater(private_compare::ordering_greater);
|
||||
|
||||
//***************************************************************************
|
||||
/// Minimal strong-ordering category for C++20 three-way comparisons when the
|
||||
/// STL is unavailable.
|
||||
//***************************************************************************
|
||||
class strong_ordering
|
||||
{
|
||||
public:
|
||||
|
||||
static const strong_ordering less;
|
||||
static const strong_ordering equal;
|
||||
static const strong_ordering equivalent;
|
||||
static const strong_ordering greater;
|
||||
|
||||
ETL_CONSTEXPR operator partial_ordering() const ETL_NOEXCEPT
|
||||
{
|
||||
return partial_ordering(value);
|
||||
}
|
||||
|
||||
ETL_CONSTEXPR operator weak_ordering() const ETL_NOEXCEPT
|
||||
{
|
||||
return weak_ordering(value);
|
||||
}
|
||||
|
||||
friend ETL_CONSTEXPR bool operator==(strong_ordering lhs, strong_ordering rhs) ETL_NOEXCEPT = default;
|
||||
|
||||
friend ETL_CONSTEXPR bool operator==(strong_ordering v, private_compare::only_literal_zero) ETL_NOEXCEPT
|
||||
{
|
||||
return v.value == private_compare::ordering_equivalent;
|
||||
}
|
||||
friend ETL_CONSTEXPR bool operator<(strong_ordering v, private_compare::only_literal_zero) ETL_NOEXCEPT
|
||||
{
|
||||
return v.value < private_compare::ordering_equivalent;
|
||||
}
|
||||
friend ETL_CONSTEXPR bool operator>(strong_ordering v, private_compare::only_literal_zero) ETL_NOEXCEPT
|
||||
{
|
||||
return v.value > private_compare::ordering_equivalent;
|
||||
}
|
||||
friend ETL_CONSTEXPR bool operator<=(strong_ordering v, private_compare::only_literal_zero) ETL_NOEXCEPT
|
||||
{
|
||||
return v.value <= private_compare::ordering_equivalent;
|
||||
}
|
||||
friend ETL_CONSTEXPR bool operator>=(strong_ordering v, private_compare::only_literal_zero) ETL_NOEXCEPT
|
||||
{
|
||||
return v.value >= private_compare::ordering_equivalent;
|
||||
}
|
||||
|
||||
friend ETL_CONSTEXPR bool operator<(private_compare::only_literal_zero, strong_ordering v) ETL_NOEXCEPT
|
||||
{
|
||||
return v > 0;
|
||||
}
|
||||
friend ETL_CONSTEXPR bool operator>(private_compare::only_literal_zero, strong_ordering v) ETL_NOEXCEPT
|
||||
{
|
||||
return v < 0;
|
||||
}
|
||||
friend ETL_CONSTEXPR bool operator<=(private_compare::only_literal_zero, strong_ordering v) ETL_NOEXCEPT
|
||||
{
|
||||
return v >= 0;
|
||||
}
|
||||
friend ETL_CONSTEXPR bool operator>=(private_compare::only_literal_zero, strong_ordering v) ETL_NOEXCEPT
|
||||
{
|
||||
return v <= 0;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
explicit ETL_CONSTEXPR strong_ordering(private_compare::ordering_value_type value_) ETL_NOEXCEPT
|
||||
: value(value_)
|
||||
{
|
||||
}
|
||||
|
||||
private_compare::ordering_value_type value;
|
||||
};
|
||||
|
||||
inline constexpr strong_ordering strong_ordering::less(private_compare::ordering_less);
|
||||
inline constexpr strong_ordering strong_ordering::equal(private_compare::ordering_equivalent);
|
||||
inline constexpr strong_ordering strong_ordering::equivalent(private_compare::ordering_equivalent);
|
||||
inline constexpr strong_ordering strong_ordering::greater(private_compare::ordering_greater);
|
||||
#endif
|
||||
|
||||
//***************************************************************************
|
||||
/// Converts a three-way comparison result to an etl::strong_ordering.
|
||||
/// Negative values map to 'less', positive values to 'greater' and zero to
|
||||
/// 'equal'.
|
||||
//***************************************************************************
|
||||
ETL_NODISCARD ETL_CONSTEXPR inline etl::strong_ordering make_strong_ordering(int value) ETL_NOEXCEPT
|
||||
{
|
||||
return (value < 0) ? etl::strong_ordering::less : (value > 0) ? etl::strong_ordering::greater : etl::strong_ordering::equal;
|
||||
}
|
||||
|
||||
//***************************************************************************
|
||||
/// Converts a three-way comparison result to an etl::weak_ordering.
|
||||
/// Negative values map to 'less', positive values to 'greater' and zero to
|
||||
/// 'equivalent'.
|
||||
//***************************************************************************
|
||||
ETL_NODISCARD ETL_CONSTEXPR inline etl::weak_ordering make_weak_ordering(int value) ETL_NOEXCEPT
|
||||
{
|
||||
return (value < 0) ? etl::weak_ordering::less : (value > 0) ? etl::weak_ordering::greater : etl::weak_ordering::equivalent;
|
||||
}
|
||||
|
||||
//***************************************************************************
|
||||
/// Converts a three-way comparison result to an etl::partial_ordering.
|
||||
/// Negative values map to 'less', positive values to 'greater' and zero to
|
||||
/// 'equivalent'.
|
||||
/// Note that 'unordered' cannot be expressed by an integer comparison result,
|
||||
/// so use etl::partial_ordering::unordered directly for that case.
|
||||
//***************************************************************************
|
||||
ETL_NODISCARD ETL_CONSTEXPR inline etl::partial_ordering make_partial_ordering(int value) ETL_NOEXCEPT
|
||||
{
|
||||
return (value < 0) ? etl::partial_ordering::less : (value > 0) ? etl::partial_ordering::greater : etl::partial_ordering::equivalent;
|
||||
}
|
||||
#endif
|
||||
|
||||
//***************************************************************************
|
||||
/// Defines <=, >, >=, ==, !=, <=> in terms of <
|
||||
/// Default implementation of TLess is etl::less
|
||||
@ -105,7 +398,7 @@ namespace etl
|
||||
#endif
|
||||
ETL_CONSTEXPR14 int three_way_compare(const T& lhs, const T& rhs)
|
||||
{
|
||||
return etl::compare<T>::cmp(lhs, rhs);
|
||||
return etl::compare<T, TLess>::cmp(lhs, rhs);
|
||||
}
|
||||
} // namespace etl
|
||||
|
||||
|
||||
@ -509,7 +509,8 @@ namespace etl
|
||||
struct basic_format_string
|
||||
{
|
||||
inline ETL_CONSTEVAL basic_format_string(const char* fmt)
|
||||
: _sv(fmt)
|
||||
: _str(fmt)
|
||||
, _length(etl::char_traits<char>::length(fmt))
|
||||
{
|
||||
#if ETL_USING_CPP20
|
||||
// Compile-time validation: check_format runs at compile time via consteval.
|
||||
@ -527,12 +528,22 @@ namespace etl
|
||||
|
||||
ETL_CONSTEXPR string_view get() const
|
||||
{
|
||||
return _sv;
|
||||
return string_view(_str, _length);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
string_view _sv;
|
||||
// The format string is deliberately stored as a pointer plus a length, and
|
||||
// not as a string_view.
|
||||
//
|
||||
// A string_view holds a pair of pointers, so constructing one here would
|
||||
// initialise a member with the pointer arithmetic 'fmt + length(fmt)'.
|
||||
// Microsoft's compiler (19.44) miscompiles exactly that case in a consteval
|
||||
// constructor in debug builds: the resulting end pointer, and therefore the
|
||||
// size of the view, is wrong. Storing the length as an integer instead
|
||||
// computes correctly, and the view is formed on demand in get().
|
||||
const char* _str;
|
||||
size_t _length;
|
||||
};
|
||||
|
||||
template <class... Args>
|
||||
|
||||
@ -158,6 +158,7 @@ namespace etl
|
||||
: head(etl::timer::id::NO_TIMER)
|
||||
, tail(etl::timer::id::NO_TIMER)
|
||||
, ptimers(ptimers_)
|
||||
, duplicate_insert(false)
|
||||
{
|
||||
}
|
||||
|
||||
@ -167,6 +168,15 @@ namespace etl
|
||||
return head == etl::timer::id::NO_TIMER;
|
||||
}
|
||||
|
||||
//*******************************
|
||||
/// Returns true if an attempt was ever made to insert a timer that was
|
||||
/// already in the list.
|
||||
//*******************************
|
||||
bool duplicate_insert_detected() const
|
||||
{
|
||||
return duplicate_insert;
|
||||
}
|
||||
|
||||
//*******************************
|
||||
// Inserts the timer at the correct delta position
|
||||
//*******************************
|
||||
@ -174,6 +184,16 @@ namespace etl
|
||||
{
|
||||
etl::message_timer_data& timer = ptimers[id_];
|
||||
|
||||
// A timer is in this list exactly when it is the head or has a previous
|
||||
// link, as remove() clears both links. Inserting one that is already
|
||||
// here would link it to itself and create a circular reference, so
|
||||
// record the attempt and abandon the insert to keep the list intact.
|
||||
if ((head == id_) || (timer.previous != etl::timer::id::NO_TIMER))
|
||||
{
|
||||
duplicate_insert = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (head == etl::timer::id::NO_TIMER)
|
||||
{
|
||||
// No entries yet.
|
||||
@ -316,6 +336,10 @@ namespace etl
|
||||
etl::timer::id::type tail;
|
||||
|
||||
etl::message_timer_data* const ptimers;
|
||||
|
||||
// Set if an attempt was ever made to insert a timer that was already
|
||||
// in the list.
|
||||
bool duplicate_insert;
|
||||
};
|
||||
} // namespace private_message_timer
|
||||
|
||||
@ -579,6 +603,16 @@ namespace etl
|
||||
return false;
|
||||
}
|
||||
|
||||
//*******************************************
|
||||
/// Returns true if an attempt was ever made to insert a timer into the
|
||||
/// active list when it was already there. Such an insert is rejected, as
|
||||
/// it would create a circular reference in the list.
|
||||
//*******************************************
|
||||
bool duplicate_insert_detected() const
|
||||
{
|
||||
return active_list.duplicate_insert_detected();
|
||||
}
|
||||
|
||||
//*******************************************
|
||||
/// Check if there is an active timer.
|
||||
//*******************************************
|
||||
|
||||
@ -183,11 +183,9 @@ namespace etl
|
||||
active_list.remove(timer.id, true);
|
||||
remove_callback.call_if(timer.id);
|
||||
|
||||
if (timer.p_router != ETL_NULLPTR)
|
||||
{
|
||||
timer.p_router->receive(timer.destination_router_id, *(timer.p_message));
|
||||
}
|
||||
|
||||
// The timer is reinserted before the message is sent, so that
|
||||
// a receiver that restarts the timer replaces this entry instead
|
||||
// of the timer being inserted into the active list twice.
|
||||
if (timer.repeating)
|
||||
{
|
||||
timer.delta = timer.period;
|
||||
@ -195,6 +193,11 @@ namespace etl
|
||||
insert_callback.call_if(timer.id);
|
||||
}
|
||||
|
||||
if (timer.p_router != ETL_NULLPTR)
|
||||
{
|
||||
timer.p_router->receive(timer.destination_router_id, *(timer.p_message));
|
||||
}
|
||||
|
||||
has_active = !active_list.empty();
|
||||
}
|
||||
|
||||
@ -308,6 +311,16 @@ namespace etl
|
||||
return false;
|
||||
}
|
||||
|
||||
//*******************************************
|
||||
/// Returns true if an attempt was ever made to insert a timer into the
|
||||
/// active list when it was already there. Such an insert is rejected, as
|
||||
/// it would create a circular reference in the list.
|
||||
//*******************************************
|
||||
bool duplicate_insert_detected() const
|
||||
{
|
||||
return active_list.duplicate_insert_detected();
|
||||
}
|
||||
|
||||
//*******************************************
|
||||
/// Check if there is an active timer.
|
||||
//*******************************************
|
||||
@ -467,6 +480,7 @@ namespace etl
|
||||
: head(etl::timer::id::NO_TIMER)
|
||||
, tail(etl::timer::id::NO_TIMER)
|
||||
, ptimers(ptimers_)
|
||||
, duplicate_insert(false)
|
||||
{
|
||||
}
|
||||
|
||||
@ -476,6 +490,15 @@ namespace etl
|
||||
return head == etl::timer::id::NO_TIMER;
|
||||
}
|
||||
|
||||
//*******************************
|
||||
/// Returns true if an attempt was ever made to insert a timer that was
|
||||
/// already in the list.
|
||||
//*******************************
|
||||
bool duplicate_insert_detected() const
|
||||
{
|
||||
return duplicate_insert;
|
||||
}
|
||||
|
||||
//*******************************
|
||||
// Inserts the timer at the correct delta position
|
||||
//*******************************
|
||||
@ -483,6 +506,16 @@ namespace etl
|
||||
{
|
||||
timer_data& timer = ptimers[id_];
|
||||
|
||||
// A timer is in this list exactly when it is the head or has a previous
|
||||
// link, as remove() clears both links. Inserting one that is already
|
||||
// here would link it to itself and create a circular reference, so
|
||||
// record the attempt and abandon the insert to keep the list intact.
|
||||
if ((head == id_) || (timer.previous != etl::timer::id::NO_TIMER))
|
||||
{
|
||||
duplicate_insert = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (head == etl::timer::id::NO_TIMER)
|
||||
{
|
||||
// No entries yet.
|
||||
@ -625,6 +658,10 @@ namespace etl
|
||||
etl::timer::id::type tail;
|
||||
|
||||
timer_data* const ptimers;
|
||||
|
||||
// Set if an attempt was ever made to insert a timer that was already
|
||||
// in the list.
|
||||
bool duplicate_insert;
|
||||
};
|
||||
|
||||
// The array of timer data structures.
|
||||
|
||||
@ -189,11 +189,9 @@ namespace etl
|
||||
active_list.remove(timer.id, true);
|
||||
remove_callback.call_if(timer.id);
|
||||
|
||||
if (timer.p_router != ETL_NULLPTR)
|
||||
{
|
||||
timer.p_router->receive(timer.destination_router_id, *(timer.p_message));
|
||||
}
|
||||
|
||||
// The timer is reinserted before the message is sent, so that
|
||||
// a receiver that restarts the timer replaces this entry instead
|
||||
// of the timer being inserted into the active list twice.
|
||||
if (timer.repeating)
|
||||
{
|
||||
// Reinsert the timer.
|
||||
@ -202,6 +200,11 @@ namespace etl
|
||||
insert_callback.call_if(timer.id);
|
||||
}
|
||||
|
||||
if (timer.p_router != ETL_NULLPTR)
|
||||
{
|
||||
timer.p_router->receive(timer.destination_router_id, *(timer.p_message));
|
||||
}
|
||||
|
||||
has_active = !active_list.empty();
|
||||
}
|
||||
|
||||
@ -316,6 +319,16 @@ namespace etl
|
||||
return false;
|
||||
}
|
||||
|
||||
//*******************************************
|
||||
/// Returns true if an attempt was ever made to insert a timer into the
|
||||
/// active list when it was already there. Such an insert is rejected, as
|
||||
/// it would create a circular reference in the list.
|
||||
//*******************************************
|
||||
bool duplicate_insert_detected() const
|
||||
{
|
||||
return active_list.duplicate_insert_detected();
|
||||
}
|
||||
|
||||
//*******************************************
|
||||
/// Check if there is an active timer.
|
||||
//*******************************************
|
||||
@ -473,6 +486,7 @@ namespace etl
|
||||
: head(etl::timer::id::NO_TIMER)
|
||||
, tail(etl::timer::id::NO_TIMER)
|
||||
, ptimers(ptimers_)
|
||||
, duplicate_insert(false)
|
||||
{
|
||||
}
|
||||
|
||||
@ -482,6 +496,15 @@ namespace etl
|
||||
return head == etl::timer::id::NO_TIMER;
|
||||
}
|
||||
|
||||
//*******************************
|
||||
/// Returns true if an attempt was ever made to insert a timer that was
|
||||
/// already in the list.
|
||||
//*******************************
|
||||
bool duplicate_insert_detected() const
|
||||
{
|
||||
return duplicate_insert;
|
||||
}
|
||||
|
||||
//*******************************
|
||||
// Inserts the timer at the correct delta position
|
||||
//*******************************
|
||||
@ -489,6 +512,16 @@ namespace etl
|
||||
{
|
||||
timer_data& timer = ptimers[id_];
|
||||
|
||||
// A timer is in this list exactly when it is the head or has a previous
|
||||
// link, as remove() clears both links. Inserting one that is already
|
||||
// here would link it to itself and create a circular reference, so
|
||||
// record the attempt and abandon the insert to keep the list intact.
|
||||
if ((head == id_) || (timer.previous != etl::timer::id::NO_TIMER))
|
||||
{
|
||||
duplicate_insert = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (head == etl::timer::id::NO_TIMER)
|
||||
{
|
||||
// No entries yet.
|
||||
@ -631,6 +664,10 @@ namespace etl
|
||||
etl::timer::id::type tail;
|
||||
|
||||
timer_data* const ptimers;
|
||||
|
||||
// Set if an attempt was ever made to insert a timer that was already
|
||||
// in the list.
|
||||
bool duplicate_insert;
|
||||
};
|
||||
|
||||
// The array of timer data structures.
|
||||
|
||||
@ -186,11 +186,9 @@ namespace etl
|
||||
active_list.remove(timer.id, true);
|
||||
remove_callback.call_if(timer.id);
|
||||
|
||||
if (timer.p_router != ETL_NULLPTR)
|
||||
{
|
||||
timer.p_router->receive(timer.destination_router_id, *(timer.p_message));
|
||||
}
|
||||
|
||||
// The timer is reinserted before the message is sent, so that
|
||||
// a receiver that restarts the timer replaces this entry instead
|
||||
// of the timer being inserted into the active list twice.
|
||||
if (timer.repeating)
|
||||
{
|
||||
timer.delta = timer.period;
|
||||
@ -198,6 +196,11 @@ namespace etl
|
||||
insert_callback.call_if(timer.id);
|
||||
}
|
||||
|
||||
if (timer.p_router != ETL_NULLPTR)
|
||||
{
|
||||
timer.p_router->receive(timer.destination_router_id, *(timer.p_message));
|
||||
}
|
||||
|
||||
has_active = !active_list.empty();
|
||||
}
|
||||
|
||||
@ -323,6 +326,16 @@ namespace etl
|
||||
unlock = unlock_;
|
||||
}
|
||||
|
||||
//*******************************************
|
||||
/// Returns true if an attempt was ever made to insert a timer into the
|
||||
/// active list when it was already there. Such an insert is rejected, as
|
||||
/// it would create a circular reference in the list.
|
||||
//*******************************************
|
||||
bool duplicate_insert_detected() const
|
||||
{
|
||||
return active_list.duplicate_insert_detected();
|
||||
}
|
||||
|
||||
//*******************************************
|
||||
/// Check if there is an active timer.
|
||||
//*******************************************
|
||||
@ -481,6 +494,7 @@ namespace etl
|
||||
: head(etl::timer::id::NO_TIMER)
|
||||
, tail(etl::timer::id::NO_TIMER)
|
||||
, ptimers(ptimers_)
|
||||
, duplicate_insert(false)
|
||||
{
|
||||
}
|
||||
|
||||
@ -490,6 +504,15 @@ namespace etl
|
||||
return head == etl::timer::id::NO_TIMER;
|
||||
}
|
||||
|
||||
//*******************************
|
||||
/// Returns true if an attempt was ever made to insert a timer that was
|
||||
/// already in the list.
|
||||
//*******************************
|
||||
bool duplicate_insert_detected() const
|
||||
{
|
||||
return duplicate_insert;
|
||||
}
|
||||
|
||||
//*******************************
|
||||
// Inserts the timer at the correct delta position
|
||||
//*******************************
|
||||
@ -497,6 +520,16 @@ namespace etl
|
||||
{
|
||||
timer_data& timer = ptimers[id_];
|
||||
|
||||
// A timer is in this list exactly when it is the head or has a previous
|
||||
// link, as remove() clears both links. Inserting one that is already
|
||||
// here would link it to itself and create a circular reference, so
|
||||
// record the attempt and abandon the insert to keep the list intact.
|
||||
if ((head == id_) || (timer.previous != etl::timer::id::NO_TIMER))
|
||||
{
|
||||
duplicate_insert = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (head == etl::timer::id::NO_TIMER)
|
||||
{
|
||||
// No entries yet.
|
||||
@ -639,6 +672,10 @@ namespace etl
|
||||
etl::timer::id::type tail;
|
||||
|
||||
timer_data* const ptimers;
|
||||
|
||||
// Set if an attempt was ever made to insert a timer that was already
|
||||
// in the list.
|
||||
bool duplicate_insert;
|
||||
};
|
||||
|
||||
// The array of timer data structures.
|
||||
|
||||
@ -215,7 +215,7 @@ namespace etl
|
||||
[[nodiscard]]
|
||||
inline constexpr auto operator<=>(const etl::chrono::day& d1, const etl::chrono::day& d2) ETL_NOEXCEPT
|
||||
{
|
||||
return (static_cast<unsigned>(d1) <=> static_cast<unsigned>(d2));
|
||||
return etl::make_strong_ordering(d1.compare(d2));
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
@ -492,7 +492,37 @@ namespace etl
|
||||
common_t l = etl::chrono::duration_cast<common_t>(lhs);
|
||||
common_t r = etl::chrono::duration_cast<common_t>(rhs);
|
||||
|
||||
#if ETL_USING_STL
|
||||
return (l.count() <=> r.count());
|
||||
#else
|
||||
// The STL returns the ordering category of the representation type.
|
||||
// Mirror that: a floating point representation is only partially ordered.
|
||||
if constexpr (etl::is_floating_point<typename common_t::rep>::value)
|
||||
{
|
||||
if (l.count() < r.count())
|
||||
{
|
||||
return etl::partial_ordering::less;
|
||||
}
|
||||
else if (r.count() < l.count())
|
||||
{
|
||||
return etl::partial_ordering::greater;
|
||||
}
|
||||
else if ((l.count() <= r.count()) && (r.count() <= l.count()))
|
||||
{
|
||||
// Equivalent. Expressed without '==' so that -Wfloat-equal is not triggered.
|
||||
return etl::partial_ordering::equivalent;
|
||||
}
|
||||
else
|
||||
{
|
||||
// At least one of the values is NaN.
|
||||
return etl::partial_ordering::unordered;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return etl::make_strong_ordering((l.count() < r.count()) ? -1 : (r.count() < l.count()) ? 1 : 0);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
@ -228,7 +228,7 @@ namespace etl
|
||||
[[nodiscard]]
|
||||
inline constexpr auto operator<=>(const etl::chrono::month& d1, const etl::chrono::month& d2) ETL_NOEXCEPT
|
||||
{
|
||||
return (static_cast<unsigned>(d1) <=> static_cast<unsigned>(d2));
|
||||
return etl::make_strong_ordering(d1.compare(d2));
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
@ -302,7 +302,7 @@ namespace etl
|
||||
[[nodiscard]]
|
||||
inline constexpr auto operator<=>(const etl::chrono::month_day_last& mdl1, const etl::chrono::month_day_last& mdl2) ETL_NOEXCEPT
|
||||
{
|
||||
return (static_cast<unsigned>(mdl1.month()) <=> static_cast<unsigned>(mdl2.month()));
|
||||
return mdl1.month() <=> mdl2.month();
|
||||
}
|
||||
#endif
|
||||
} // namespace chrono
|
||||
|
||||
@ -244,7 +244,7 @@ namespace etl
|
||||
[[nodiscard]]
|
||||
inline constexpr auto operator<=>(const etl::chrono::year& y1, const etl::chrono::year& y2) ETL_NOEXCEPT
|
||||
{
|
||||
return (static_cast<int>(y1) <=> static_cast<int>(y2));
|
||||
return etl::make_strong_ordering(y1.compare(y2));
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
@ -644,8 +644,7 @@ namespace etl
|
||||
/// Obtains a span that is a view over the first count elements of this
|
||||
/// span.
|
||||
//*************************************************************************
|
||||
ETL_NODISCARD ETL_CONSTEXPR etl::span<element_type, etl::dynamic_extent> first(size_t count) const
|
||||
ETL_NOEXCEPT_IF(ETL_NOT_USING_EXCEPTIONS || ETL_NOT_CHECKING_EXTRA)
|
||||
ETL_NODISCARD ETL_CONSTEXPR etl::span<element_type, etl::dynamic_extent> first(size_t count) const ETL_NOEXCEPT_IF(ETL_NOT_CHECKING_EXTRA)
|
||||
{
|
||||
#if ETL_USING_CPP11 && ETL_NOT_USING_CPP14 && ETL_USING_EXCEPTIONS && ETL_CHECKING_EXTRA
|
||||
return count <= size() ? etl::span<element_type, etl::dynamic_extent>(pbegin, pbegin + count) : throw(ETL_ERROR(span_out_of_range));
|
||||
@ -672,8 +671,7 @@ namespace etl
|
||||
//*************************************************************************
|
||||
/// Obtains a span that is a view over the last count elements of this span.
|
||||
//*************************************************************************
|
||||
ETL_NODISCARD ETL_CONSTEXPR etl::span<element_type, etl::dynamic_extent> last(size_t count) const
|
||||
ETL_NOEXCEPT_IF(ETL_NOT_USING_EXCEPTIONS || ETL_NOT_CHECKING_EXTRA)
|
||||
ETL_NODISCARD ETL_CONSTEXPR etl::span<element_type, etl::dynamic_extent> last(size_t count) const ETL_NOEXCEPT_IF(ETL_NOT_CHECKING_EXTRA)
|
||||
{
|
||||
#if ETL_USING_CPP11 && ETL_NOT_USING_CPP14 && ETL_USING_EXCEPTIONS && ETL_CHECKING_EXTRA
|
||||
return count <= size() ? etl::span<element_type, etl::dynamic_extent>((pbegin + Extent) - count, (pbegin + Extent))
|
||||
@ -735,7 +733,7 @@ namespace etl
|
||||
/// elements of this span.
|
||||
//*************************************************************************
|
||||
ETL_NODISCARD ETL_CONSTEXPR etl::span<element_type, etl::dynamic_extent> subspan(size_t offset, size_t count = etl::dynamic_extent) const
|
||||
ETL_NOEXCEPT_IF(ETL_NOT_USING_EXCEPTIONS || ETL_NOT_CHECKING_EXTRA)
|
||||
ETL_NOEXCEPT_IF(ETL_NOT_CHECKING_EXTRA)
|
||||
{
|
||||
#if ETL_USING_CPP11 && ETL_NOT_USING_CPP14 && ETL_USING_EXCEPTIONS && ETL_CHECKING_EXTRA
|
||||
return (offset <= size()) && (count != etl::dynamic_extent ? count <= (size() - offset) : true)
|
||||
@ -1003,7 +1001,7 @@ namespace etl
|
||||
//*************************************************************************
|
||||
/// Returns a reference to the first element.
|
||||
//*************************************************************************
|
||||
ETL_NODISCARD ETL_CONSTEXPR reference front() const ETL_NOEXCEPT_IF(ETL_NOT_USING_EXCEPTIONS || ETL_NOT_CHECKING_EXTRA)
|
||||
ETL_NODISCARD ETL_CONSTEXPR reference front() const ETL_NOEXCEPT_IF(ETL_NOT_CHECKING_EXTRA)
|
||||
{
|
||||
#if ETL_USING_CPP11 && ETL_NOT_USING_CPP14 && ETL_USING_EXCEPTIONS && ETL_CHECKING_EXTRA
|
||||
return size() > 0 ? *pbegin : throw(ETL_ERROR(span_out_of_range));
|
||||
@ -1017,7 +1015,7 @@ namespace etl
|
||||
//*************************************************************************
|
||||
/// Returns a reference to the last element.
|
||||
//*************************************************************************
|
||||
ETL_NODISCARD ETL_CONSTEXPR reference back() const ETL_NOEXCEPT_IF(ETL_NOT_USING_EXCEPTIONS || ETL_NOT_CHECKING_EXTRA)
|
||||
ETL_NODISCARD ETL_CONSTEXPR reference back() const ETL_NOEXCEPT_IF(ETL_NOT_CHECKING_EXTRA)
|
||||
{
|
||||
#if ETL_USING_CPP11 && ETL_NOT_USING_CPP14 && ETL_USING_EXCEPTIONS && ETL_CHECKING_EXTRA
|
||||
return size() > 0 ? *(pend - 1) : throw(ETL_ERROR(span_out_of_range));
|
||||
@ -1197,7 +1195,7 @@ namespace etl
|
||||
/// span.
|
||||
//*************************************************************************
|
||||
template <size_t COUNT>
|
||||
ETL_NODISCARD ETL_CONSTEXPR etl::span<element_type, COUNT> first() const ETL_NOEXCEPT_IF(ETL_NOT_USING_EXCEPTIONS || ETL_NOT_CHECKING_EXTRA)
|
||||
ETL_NODISCARD ETL_CONSTEXPR etl::span<element_type, COUNT> first() const ETL_NOEXCEPT_IF(ETL_NOT_CHECKING_EXTRA)
|
||||
{
|
||||
#if ETL_USING_CPP11 && ETL_NOT_USING_CPP14 && ETL_USING_EXCEPTIONS && ETL_CHECKING_EXTRA
|
||||
return COUNT <= size() ? etl::span<element_type, COUNT>(pbegin, pbegin + COUNT) : throw(ETL_ERROR(span_out_of_range));
|
||||
@ -1212,8 +1210,7 @@ namespace etl
|
||||
/// Obtains a span that is a view over the first count elements of this
|
||||
/// span.
|
||||
//*************************************************************************
|
||||
ETL_NODISCARD ETL_CONSTEXPR etl::span<element_type, etl::dynamic_extent> first(size_t count) const
|
||||
ETL_NOEXCEPT_IF(ETL_NOT_USING_EXCEPTIONS || ETL_NOT_CHECKING_EXTRA)
|
||||
ETL_NODISCARD ETL_CONSTEXPR etl::span<element_type, etl::dynamic_extent> first(size_t count) const ETL_NOEXCEPT_IF(ETL_NOT_CHECKING_EXTRA)
|
||||
{
|
||||
#if ETL_USING_CPP11 && ETL_NOT_USING_CPP14 && ETL_USING_EXCEPTIONS && ETL_CHECKING_EXTRA
|
||||
return count <= size() ? etl::span<element_type, etl::dynamic_extent>(pbegin, pbegin + count) : throw(ETL_ERROR(span_out_of_range));
|
||||
@ -1228,7 +1225,7 @@ namespace etl
|
||||
/// Obtains a span that is a view over the last COUNT elements of this span.
|
||||
//*************************************************************************
|
||||
template <size_t COUNT>
|
||||
ETL_NODISCARD ETL_CONSTEXPR etl::span<element_type, COUNT> last() const ETL_NOEXCEPT_IF(ETL_NOT_USING_EXCEPTIONS || ETL_NOT_CHECKING_EXTRA)
|
||||
ETL_NODISCARD ETL_CONSTEXPR etl::span<element_type, COUNT> last() const ETL_NOEXCEPT_IF(ETL_NOT_CHECKING_EXTRA)
|
||||
{
|
||||
#if ETL_USING_CPP11 && ETL_NOT_USING_CPP14 && ETL_USING_EXCEPTIONS && ETL_CHECKING_EXTRA
|
||||
return COUNT <= size() ? etl::span<element_type, COUNT>(pend - COUNT, pend) : throw(ETL_ERROR(span_out_of_range));
|
||||
@ -1242,8 +1239,7 @@ namespace etl
|
||||
//*************************************************************************
|
||||
/// Obtains a span that is a view over the last count elements of this span.
|
||||
//*************************************************************************
|
||||
ETL_NODISCARD ETL_CONSTEXPR etl::span<element_type, etl::dynamic_extent> last(size_t count) const
|
||||
ETL_NOEXCEPT_IF(ETL_NOT_USING_EXCEPTIONS || ETL_NOT_CHECKING_EXTRA)
|
||||
ETL_NODISCARD ETL_CONSTEXPR etl::span<element_type, etl::dynamic_extent> last(size_t count) const ETL_NOEXCEPT_IF(ETL_NOT_CHECKING_EXTRA)
|
||||
{
|
||||
#if ETL_USING_CPP11 && ETL_NOT_USING_CPP14 && ETL_USING_EXCEPTIONS && ETL_CHECKING_EXTRA
|
||||
return count <= size() ? etl::span<element_type, etl::dynamic_extent>(pend - count, pend) : throw(ETL_ERROR(span_out_of_range));
|
||||
@ -1261,7 +1257,7 @@ namespace etl
|
||||
//*************************************************************************
|
||||
template <size_t OFFSET, size_t COUNT = etl::dynamic_extent>
|
||||
ETL_NODISCARD ETL_CONSTEXPR etl::span< element_type, COUNT != etl::dynamic_extent ? COUNT : etl::dynamic_extent> subspan() const
|
||||
ETL_NOEXCEPT_IF(ETL_NOT_USING_EXCEPTIONS || ETL_NOT_CHECKING_EXTRA)
|
||||
ETL_NOEXCEPT_IF(ETL_NOT_CHECKING_EXTRA)
|
||||
{
|
||||
#if ETL_USING_CPP11 && ETL_NOT_USING_CPP14 && ETL_USING_EXCEPTIONS && ETL_CHECKING_EXTRA
|
||||
return (OFFSET <= size()) && (COUNT != etl::dynamic_extent ? COUNT <= (size() - OFFSET) : true)
|
||||
@ -1305,7 +1301,7 @@ namespace etl
|
||||
/// elements of this span.
|
||||
//*************************************************************************
|
||||
ETL_NODISCARD ETL_CONSTEXPR14 etl::span<element_type, etl::dynamic_extent> subspan(size_t offset, size_t count = etl::dynamic_extent) const
|
||||
ETL_NOEXCEPT_IF(ETL_NOT_USING_EXCEPTIONS || ETL_NOT_CHECKING_EXTRA)
|
||||
ETL_NOEXCEPT_IF(ETL_NOT_CHECKING_EXTRA)
|
||||
{
|
||||
ETL_ASSERT_CHECK_EXTRA(offset <= size(), ETL_ERROR(span_out_of_range));
|
||||
ETL_ASSERT_CHECK_EXTRA(count != etl::dynamic_extent ? count <= (size() - offset) : true, ETL_ERROR(span_out_of_range));
|
||||
|
||||
@ -355,7 +355,7 @@ namespace etl
|
||||
/// If asserts or exceptions are enabled, throws an etl::string_view_bounds
|
||||
/// if the index is out of bounds.
|
||||
//*************************************************************************
|
||||
ETL_CONSTEXPR const_reference operator[](size_type i) const ETL_NOEXCEPT_EXPR(ETL_NOT_USING_EXCEPTIONS || ETL_NOT_CHECKING_INDEX_OPERATOR)
|
||||
ETL_CONSTEXPR const_reference operator[](size_type i) const ETL_NOEXCEPT_EXPR(ETL_NOT_CHECKING_INDEX_OPERATOR)
|
||||
{
|
||||
#if ETL_USING_CPP11 && ETL_NOT_USING_CPP14 && ETL_USING_EXCEPTIONS && ETL_CHECKING_INDEX_OPERATOR
|
||||
return i < size() ? mbegin[i] : throw(ETL_ERROR(string_view_bounds));
|
||||
|
||||
@ -4310,13 +4310,53 @@ namespace etl
|
||||
#endif
|
||||
|
||||
#if ETL_USING_CPP11
|
||||
//*********************************************
|
||||
// Builtin-free noexcept-constructibility/assignability detection. Used when
|
||||
// the __is_nothrow_constructible/__is_nothrow_assignable builtins are
|
||||
// unavailable. Mirrors the void_t-based is_constructible/is_assignable
|
||||
// fallbacks above so that (e.g.) a class with implicit noexcept move
|
||||
// operations is correctly reported nothrow-(move-)constructible/assignable,
|
||||
// and a const-qualified type is correctly reported NOT nothrow-assignable
|
||||
// (the old is_arithmetic||is_pointer fallback ignored const-qualification).
|
||||
#if !ETL_USING_BUILTIN_IS_NOTHROW_CONSTRUCTIBLE
|
||||
namespace private_type_traits
|
||||
{
|
||||
template <typename, typename T, typename... TArgs>
|
||||
struct is_nothrow_constructible_ : etl::false_type
|
||||
{
|
||||
};
|
||||
|
||||
template <typename T, typename... TArgs>
|
||||
struct is_nothrow_constructible_<etl::void_t<decltype(T(etl::declval<TArgs>()...))>, T, TArgs...>
|
||||
: etl::bool_constant<noexcept(T(etl::declval<TArgs>()...))>
|
||||
{
|
||||
};
|
||||
} // namespace private_type_traits
|
||||
#endif
|
||||
|
||||
#if !ETL_USING_BUILTIN_IS_NOTHROW_ASSIGNABLE
|
||||
namespace private_type_traits
|
||||
{
|
||||
template <typename, typename T, typename U>
|
||||
struct is_nothrow_assignable_ : etl::false_type
|
||||
{
|
||||
};
|
||||
|
||||
template <typename T, typename U>
|
||||
struct is_nothrow_assignable_<etl::void_t<decltype(etl::declval<T>() = etl::declval<U>())>, T, U>
|
||||
: etl::bool_constant<noexcept(etl::declval<T>() = etl::declval<U>())>
|
||||
{
|
||||
};
|
||||
} // namespace private_type_traits
|
||||
#endif
|
||||
|
||||
//*********************************************
|
||||
// is_nothrow_constructible
|
||||
template <typename T, typename... TArgs>
|
||||
#if ETL_USING_BUILTIN_IS_NOTHROW_CONSTRUCTIBLE
|
||||
struct is_nothrow_constructible : public etl::bool_constant<__is_nothrow_constructible(T, TArgs...)>
|
||||
#else
|
||||
struct is_nothrow_constructible : public etl::bool_constant<etl::is_arithmetic<T>::value || etl::is_pointer<T>::value>
|
||||
struct is_nothrow_constructible : public private_type_traits::is_nothrow_constructible_<etl::void_t<>, T, TArgs...>
|
||||
#endif
|
||||
{
|
||||
};
|
||||
@ -4324,33 +4364,21 @@ namespace etl
|
||||
//*********************************************
|
||||
// is_nothrow_default_constructible
|
||||
template <typename T>
|
||||
#if ETL_USING_BUILTIN_IS_NOTHROW_CONSTRUCTIBLE
|
||||
struct is_nothrow_default_constructible : public etl::is_nothrow_constructible<T>
|
||||
#else
|
||||
struct is_nothrow_default_constructible : public etl::bool_constant<etl::is_arithmetic<T>::value || etl::is_pointer<T>::value>
|
||||
#endif
|
||||
{
|
||||
};
|
||||
|
||||
//*********************************************
|
||||
// is_nothrow_copy_constructible
|
||||
template <typename T>
|
||||
#if ETL_USING_BUILTIN_IS_NOTHROW_CONSTRUCTIBLE
|
||||
struct is_nothrow_copy_constructible : public etl::is_nothrow_constructible<T, typename etl::add_lvalue_reference<const T>::type>
|
||||
#else
|
||||
struct is_nothrow_copy_constructible : public etl::bool_constant<etl::is_arithmetic<T>::value || etl::is_pointer<T>::value>
|
||||
#endif
|
||||
{
|
||||
};
|
||||
|
||||
//*********************************************
|
||||
// is_nothrow_move_constructible
|
||||
template <typename T>
|
||||
#if ETL_USING_BUILTIN_IS_NOTHROW_CONSTRUCTIBLE
|
||||
struct is_nothrow_move_constructible : public etl::is_nothrow_constructible<T, typename etl::add_rvalue_reference<T>::type>
|
||||
#else
|
||||
struct is_nothrow_move_constructible : public etl::bool_constant<etl::is_arithmetic<T>::value || etl::is_pointer<T>::value>
|
||||
#endif
|
||||
{
|
||||
};
|
||||
|
||||
@ -4360,7 +4388,7 @@ namespace etl
|
||||
#if ETL_USING_BUILTIN_IS_NOTHROW_ASSIGNABLE
|
||||
struct is_nothrow_assignable : public etl::bool_constant<__is_nothrow_assignable(T, U)>
|
||||
#else
|
||||
struct is_nothrow_assignable : public etl::bool_constant<etl::is_arithmetic<T>::value || etl::is_pointer<T>::value>
|
||||
struct is_nothrow_assignable : public private_type_traits::is_nothrow_assignable_<etl::void_t<>, T, U>
|
||||
#endif
|
||||
{
|
||||
};
|
||||
@ -4368,24 +4396,16 @@ namespace etl
|
||||
//*********************************************
|
||||
// is_nothrow_copy_assignable
|
||||
template <typename T>
|
||||
#if ETL_USING_BUILTIN_IS_NOTHROW_ASSIGNABLE
|
||||
struct is_nothrow_copy_assignable
|
||||
: public etl::is_nothrow_assignable<typename etl::add_lvalue_reference<T>::type, typename etl::add_lvalue_reference<const T>::type>
|
||||
#else
|
||||
struct is_nothrow_copy_assignable : public etl::bool_constant<etl::is_arithmetic<T>::value || etl::is_pointer<T>::value>
|
||||
#endif
|
||||
{
|
||||
};
|
||||
|
||||
//*********************************************
|
||||
// is_nothrow_move_assignable
|
||||
template <typename T>
|
||||
#if ETL_USING_BUILTIN_IS_NOTHROW_ASSIGNABLE
|
||||
struct is_nothrow_move_assignable
|
||||
: public etl::is_nothrow_assignable<typename etl::add_lvalue_reference<T>::type, typename etl::add_rvalue_reference<T>::type>
|
||||
#else
|
||||
struct is_nothrow_move_assignable : public etl::bool_constant<etl::is_arithmetic<T>::value || etl::is_pointer<T>::value>
|
||||
#endif
|
||||
{
|
||||
};
|
||||
#endif
|
||||
@ -5175,20 +5195,26 @@ namespace etl
|
||||
#elif ETL_USING_CPP11
|
||||
namespace private_type_traits
|
||||
{
|
||||
template <typename T>
|
||||
struct is_swappable_helper;
|
||||
|
||||
namespace swap_detect
|
||||
{
|
||||
// Bring std::swap into scope, if it is available, so that the unqualified
|
||||
// call below finds it in those configurations that do not define etl::swap.
|
||||
// Otherwise etl::swap is found by ordinary lookup in the enclosing
|
||||
// namespace. Either way, a swap found by argument dependent lookup takes
|
||||
// part in overload resolution.
|
||||
#if ETL_USING_STL || defined(ETL_IN_UNIT_TEST)
|
||||
using std::swap;
|
||||
#endif
|
||||
// A dummy declaration that can never be selected, as it takes no
|
||||
// arguments, but which makes the name visible to ordinary lookup so that
|
||||
// the unqualified calls below are well formed.
|
||||
// Ordinary lookup stops here, so only a swap found by argument dependent
|
||||
// lookup takes part in overload resolution.
|
||||
// The generic swap that the standard library (or etl) provides is not
|
||||
// detected this way, because not every implementation constrains it
|
||||
// (Microsoft's C++14 std::swap does not), which would make every type
|
||||
// appear to be swappable. Its viability is determined by the
|
||||
// is_default_swappable traits below instead.
|
||||
void swap();
|
||||
|
||||
// Selected only if 'swap(declval<T&>(), declval<T&>())' is a valid expression.
|
||||
template <typename T>
|
||||
struct is_swappable_test
|
||||
struct is_adl_swappable_test
|
||||
{
|
||||
private:
|
||||
|
||||
@ -5206,7 +5232,7 @@ namespace etl
|
||||
// Selected only if both 'swap(declval<T>(), declval<U>())' and
|
||||
// 'swap(declval<U>(), declval<T>())' are valid expressions.
|
||||
template <typename T, typename U>
|
||||
struct is_swappable_with_test
|
||||
struct is_adl_swappable_with_test
|
||||
{
|
||||
private:
|
||||
|
||||
@ -5221,15 +5247,61 @@ namespace etl
|
||||
|
||||
static ETL_CONSTANT bool value = decltype(test<T, U>(0))::value;
|
||||
};
|
||||
|
||||
// Determines whether an expression of type T can be bound to a non-const
|
||||
// lvalue reference to its referenced type.
|
||||
// This is not simply 'is_lvalue_reference', because some compilers offer
|
||||
// an extension that binds a class rvalue to a non-const lvalue reference.
|
||||
template <typename T>
|
||||
void binds_to_lvalue_reference_test(T&);
|
||||
|
||||
template <typename T, typename = void>
|
||||
struct binds_to_lvalue_reference : etl::false_type
|
||||
{
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct binds_to_lvalue_reference<
|
||||
T, etl::void_t<decltype(binds_to_lvalue_reference_test<typename etl::remove_reference<T>::type>(etl::declval<T>()))>> : etl::true_type
|
||||
{
|
||||
};
|
||||
} // namespace swap_detect
|
||||
|
||||
// Determines whether the generic swap(T&, T&) is viable for T.
|
||||
template <typename T>
|
||||
struct is_swappable_helper : etl::bool_constant<swap_detect::is_swappable_test<T>::value>
|
||||
struct is_default_swappable : etl::bool_constant<etl::is_move_constructible<T>::value && etl::is_move_assignable<T>::value>
|
||||
{
|
||||
};
|
||||
|
||||
// The generic swap for arrays requires the element type to be swappable.
|
||||
template <typename T, size_t Size>
|
||||
struct is_default_swappable<T[Size]> : is_swappable_helper<T>
|
||||
{
|
||||
};
|
||||
|
||||
// An array of unknown bound cannot be swapped.
|
||||
template <typename T>
|
||||
struct is_default_swappable<T[]> : etl::false_type
|
||||
{
|
||||
};
|
||||
|
||||
// The generic swap only participates when both arguments are lvalues of
|
||||
// the same type.
|
||||
template <typename T, typename U>
|
||||
struct is_default_swappable_with
|
||||
: etl::bool_constant<swap_detect::binds_to_lvalue_reference<T>::value && etl::is_same<T, U>::value
|
||||
&& is_default_swappable<typename etl::remove_reference<T>::type>::value>
|
||||
{
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct is_swappable_helper : etl::bool_constant<swap_detect::is_adl_swappable_test<T>::value || is_default_swappable<T>::value>
|
||||
{
|
||||
};
|
||||
|
||||
template <typename T, typename U>
|
||||
struct is_swappable_with_helper : etl::bool_constant<swap_detect::is_swappable_with_test<T, U>::value>
|
||||
struct is_swappable_with_helper
|
||||
: etl::bool_constant<swap_detect::is_adl_swappable_with_test<T, U>::value || is_default_swappable_with<T, U>::value>
|
||||
{
|
||||
};
|
||||
} // namespace private_type_traits
|
||||
@ -5271,43 +5343,75 @@ namespace etl
|
||||
#elif ETL_USING_CPP11
|
||||
namespace private_type_traits
|
||||
{
|
||||
template <typename T>
|
||||
struct is_nothrow_swappable_helper;
|
||||
|
||||
namespace swap_detect
|
||||
{
|
||||
// Only 'true' if the type is swappable and the swap expression is noexcept.
|
||||
template <typename T, bool Is_Swappable = is_swappable_test<T>::value>
|
||||
struct is_nothrow_swappable_test
|
||||
// Only 'true' if the type has an ADL found swap and the swap expression is noexcept.
|
||||
template <typename T, bool Is_Adl_Swappable = is_adl_swappable_test<T>::value>
|
||||
struct is_nothrow_adl_swappable_test
|
||||
{
|
||||
static ETL_CONSTANT bool value = false;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct is_nothrow_swappable_test<T, true>
|
||||
struct is_nothrow_adl_swappable_test<T, true>
|
||||
{
|
||||
static ETL_CONSTANT bool value = noexcept(swap(etl::declval<T&>(), etl::declval<T&>()));
|
||||
};
|
||||
|
||||
// Only 'true' if the types are swappable with each other and both of the
|
||||
// swap expressions are noexcept.
|
||||
template <typename T, typename U, bool Is_Swappable_With = is_swappable_with_test<T, U>::value>
|
||||
struct is_nothrow_swappable_with_test
|
||||
// Only 'true' if the types have ADL found swaps for each other and both of
|
||||
// the swap expressions are noexcept.
|
||||
template <typename T, typename U, bool Is_Adl_Swappable_With = is_adl_swappable_with_test<T, U>::value>
|
||||
struct is_nothrow_adl_swappable_with_test
|
||||
{
|
||||
static ETL_CONSTANT bool value = false;
|
||||
};
|
||||
|
||||
template <typename T, typename U>
|
||||
struct is_nothrow_swappable_with_test<T, U, true>
|
||||
struct is_nothrow_adl_swappable_with_test<T, U, true>
|
||||
{
|
||||
static ETL_CONSTANT bool value = noexcept(swap(etl::declval<T>(), etl::declval<U>())) && noexcept(swap(etl::declval<U>(), etl::declval<T>()));
|
||||
};
|
||||
} // namespace swap_detect
|
||||
|
||||
// Determines whether the generic swap(T&, T&) is viable for T and cannot throw.
|
||||
template <typename T>
|
||||
struct is_nothrow_swappable_helper : etl::bool_constant<swap_detect::is_nothrow_swappable_test<T>::value>
|
||||
struct is_nothrow_default_swappable
|
||||
: etl::bool_constant<etl::is_nothrow_move_constructible<T>::value && etl::is_nothrow_move_assignable<T>::value>
|
||||
{
|
||||
};
|
||||
|
||||
template <typename T, size_t Size>
|
||||
struct is_nothrow_default_swappable<T[Size]> : is_nothrow_swappable_helper<T>
|
||||
{
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct is_nothrow_default_swappable<T[]> : etl::false_type
|
||||
{
|
||||
};
|
||||
|
||||
template <typename T, typename U>
|
||||
struct is_nothrow_swappable_with_helper : etl::bool_constant<swap_detect::is_nothrow_swappable_with_test<T, U>::value>
|
||||
struct is_nothrow_default_swappable_with
|
||||
: etl::bool_constant<swap_detect::binds_to_lvalue_reference<T>::value && etl::is_same<T, U>::value
|
||||
&& is_nothrow_default_swappable<typename etl::remove_reference<T>::type>::value>
|
||||
{
|
||||
};
|
||||
|
||||
// An ADL found swap hides the generic one, so it decides the result.
|
||||
template <typename T>
|
||||
struct is_nothrow_swappable_helper
|
||||
: etl::bool_constant<swap_detect::is_adl_swappable_test<T>::value ? swap_detect::is_nothrow_adl_swappable_test<T>::value
|
||||
: is_nothrow_default_swappable<T>::value>
|
||||
{
|
||||
};
|
||||
|
||||
template <typename T, typename U>
|
||||
struct is_nothrow_swappable_with_helper
|
||||
: etl::bool_constant<swap_detect::is_adl_swappable_with_test<T, U>::value ? swap_detect::is_nothrow_adl_swappable_with_test<T, U>::value
|
||||
: is_nothrow_default_swappable_with<T, U>::value>
|
||||
{
|
||||
};
|
||||
} // namespace private_type_traits
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -393,7 +393,7 @@ namespace etl
|
||||
typename etl::enable_if<!etl::is_integral<TIterator>::value
|
||||
&& etl::is_convertible<typename etl::iterator_traits<TIterator>::value_type, T>::value,
|
||||
typename etl::enable_if<!etl::is_integral<TIterator>::value, void>::type>::type
|
||||
assign(TIterator first, TIterator last) ETL_NOEXCEPT_IF((etl::is_nothrow_copy_constructible<T>::value && ETL_NOT_USING_EXCEPTIONS))
|
||||
assign(TIterator first, TIterator last)
|
||||
{
|
||||
#if ETL_USING_CPP11
|
||||
ETL_STATIC_ASSERT((etl::is_same<typename etl::remove_cv<T>::type,
|
||||
@ -2082,7 +2082,6 @@ namespace etl
|
||||
/// Copy constructor.
|
||||
//*************************************************************************
|
||||
vector_ext(const vector_ext& other, void* buffer, size_t max_size)
|
||||
ETL_NOEXCEPT_IF((etl::is_nothrow_copy_constructible<T>::value && ETL_NOT_USING_EXCEPTIONS))
|
||||
: etl::ivector<T>(reinterpret_cast<T*>(buffer), max_size)
|
||||
{
|
||||
if (&other != this)
|
||||
@ -2094,7 +2093,7 @@ namespace etl
|
||||
//*************************************************************************
|
||||
/// Assignment operator.
|
||||
//*************************************************************************
|
||||
vector_ext& operator=(const vector_ext& rhs) ETL_NOEXCEPT_IF((etl::is_nothrow_copy_constructible<T>::value && ETL_NOT_USING_EXCEPTIONS))
|
||||
vector_ext& operator=(const vector_ext& rhs)
|
||||
{
|
||||
if (&rhs != this)
|
||||
{
|
||||
@ -2109,7 +2108,6 @@ namespace etl
|
||||
/// Move constructor.
|
||||
//*************************************************************************
|
||||
vector_ext(vector_ext&& other, void* buffer, size_t max_size)
|
||||
ETL_NOEXCEPT_IF((etl::is_nothrow_move_constructible<T>::value && ETL_NOT_USING_EXCEPTIONS))
|
||||
: etl::ivector<T>(reinterpret_cast<T*>(buffer), max_size)
|
||||
{
|
||||
if (&other != this)
|
||||
@ -2123,7 +2121,7 @@ namespace etl
|
||||
//*************************************************************************
|
||||
/// Move assignment operator.
|
||||
//*************************************************************************
|
||||
vector_ext& operator=(vector_ext&& rhs) ETL_NOEXCEPT_IF((etl::is_nothrow_move_constructible<T>::value && ETL_NOT_USING_EXCEPTIONS))
|
||||
vector_ext& operator=(vector_ext&& rhs)
|
||||
{
|
||||
if (&rhs != this)
|
||||
{
|
||||
|
||||
@ -37,3 +37,13 @@ etl::clamped_value<double> clamped_double_run_time;
|
||||
#if ETL_HAS_COMPILE_TIME_FLOATING_POINT_CLAMPED_VALUE
|
||||
etl::clamped_value<float, -1.0f, 1.0f> clamped_float_compile_time;
|
||||
#endif
|
||||
|
||||
#if ETL_USING_CPP11
|
||||
const etl::clamped_value_range<int> referenced_clamped_range(0, 10);
|
||||
etl::referenced_clamped_value<int> referenced_clamped_int(referenced_clamped_range);
|
||||
|
||||
#if ETL_HAS_FLOATING_POINT_CLAMPED_VALUE
|
||||
const etl::clamped_value_range<float> referenced_clamped_float_range(-1.0f, 1.0f);
|
||||
etl::referenced_clamped_value<float> referenced_clamped_float(referenced_clamped_float_range);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@ -149,6 +149,35 @@ namespace
|
||||
|
||||
callback_type free_function_callback2 = callback_type::create<free_callback2>();
|
||||
|
||||
//***************************************************************************
|
||||
// Fixture for a repeating timer that restarts itself from within its own
|
||||
// callback (regression test for issue #967).
|
||||
//***************************************************************************
|
||||
struct RestartInCallback
|
||||
{
|
||||
etl::callback_timer_atomic<1, std::atomic_uint32_t>* p_controller = nullptr;
|
||||
etl::timer::id::type id = etl::timer::id::NO_TIMER;
|
||||
uint32_t new_period = 0U;
|
||||
std::vector<uint64_t> call_ticks;
|
||||
|
||||
void callback()
|
||||
{
|
||||
call_ticks.push_back(ticks);
|
||||
|
||||
// Change the period (this stops/deactivates the timer) and restart it
|
||||
// from inside the callback. Before the fix, tick() would then insert the
|
||||
// timer a second time, creating a circular reference in the active list.
|
||||
p_controller->set_period(id, new_period);
|
||||
p_controller->start(id);
|
||||
}
|
||||
};
|
||||
|
||||
RestartInCallback restart_in_callback;
|
||||
|
||||
using timer_callback_type = etl::icallback_timer_atomic<std::atomic_uint32_t>::callback_type;
|
||||
timer_callback_type restart_in_callback_member =
|
||||
timer_callback_type::create<RestartInCallback, restart_in_callback, &RestartInCallback::callback>();
|
||||
|
||||
SUITE(test_callback_timer_atomic)
|
||||
{
|
||||
//*************************************************************************
|
||||
@ -855,6 +884,54 @@ namespace
|
||||
CHECK_EQUAL(3, timerInsertRemoveTest.removed);
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
// Regression test for issue #967:
|
||||
// Calling start() from within a repeating timer's callback (after the
|
||||
// period has been changed, which stops the timer) must not insert the timer
|
||||
// twice and create a circular reference in the active list.
|
||||
//*************************************************************************
|
||||
TEST(callback_timer_atomic_restart_in_callback_no_circular_reference)
|
||||
{
|
||||
etl::callback_timer_atomic<1, std::atomic_uint32_t> timer_controller;
|
||||
|
||||
restart_in_callback.p_controller = &timer_controller;
|
||||
restart_in_callback.new_period = 20U;
|
||||
restart_in_callback.call_ticks.clear();
|
||||
|
||||
etl::timer::id::type id = timer_controller.register_timer(restart_in_callback_member, 10, etl::timer::mode::Repeating);
|
||||
restart_in_callback.id = id;
|
||||
|
||||
timer_controller.start(id);
|
||||
timer_controller.enable(true);
|
||||
|
||||
ticks = 0;
|
||||
|
||||
const uint32_t step = 1U;
|
||||
|
||||
// Run long enough for several firings at the original (10) then new (20) period.
|
||||
while (ticks < 100U)
|
||||
{
|
||||
ticks += step;
|
||||
timer_controller.tick(step);
|
||||
}
|
||||
|
||||
// Detect a duplicated entry in the active list directly, rather than
|
||||
// relying on the resulting circular reference hanging the test.
|
||||
CHECK_FALSE(timer_controller.duplicate_insert_detected());
|
||||
|
||||
// The timer must still be active (alive, not corrupted) and scheduled at
|
||||
// the new period. A circular reference would corrupt the active list.
|
||||
CHECK_TRUE(timer_controller.is_active(id));
|
||||
|
||||
// First firing at 10, then every 20 thereafter: 10, 30, 50, 70, 90.
|
||||
std::vector<uint64_t> compare = {10, 30, 50, 70, 90};
|
||||
|
||||
CHECK_EQUAL(compare.size(), restart_in_callback.call_ticks.size());
|
||||
CHECK_ARRAY_EQUAL(compare.data(), restart_in_callback.call_ticks.data(), compare.size());
|
||||
|
||||
timer_controller.clear();
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
#if REALTIME_TEST
|
||||
|
||||
|
||||
@ -162,6 +162,33 @@ namespace
|
||||
|
||||
callback_type free_function_callback2 = callback_type::create<free_callback2>();
|
||||
|
||||
//***************************************************************************
|
||||
// Fixture for a repeating timer that restarts itself from within its own
|
||||
// callback (regression test for issue #967).
|
||||
//***************************************************************************
|
||||
struct RestartInCallback
|
||||
{
|
||||
etl::callback_timer_interrupt<1, ScopedGuard>* p_controller = nullptr;
|
||||
etl::timer::id::type id = etl::timer::id::NO_TIMER;
|
||||
uint32_t new_period = 0U;
|
||||
std::vector<uint64_t> call_ticks;
|
||||
|
||||
void callback()
|
||||
{
|
||||
call_ticks.push_back(ticks);
|
||||
|
||||
// Change the period (this stops/deactivates the timer) and restart it
|
||||
// from inside the callback. Before the fix, tick() would then insert the
|
||||
// timer a second time, creating a circular reference in the active list.
|
||||
p_controller->set_period(id, new_period);
|
||||
p_controller->start(id);
|
||||
}
|
||||
};
|
||||
|
||||
RestartInCallback restart_in_callback;
|
||||
|
||||
callback_type restart_in_callback_member = callback_type::create<RestartInCallback, restart_in_callback, &RestartInCallback::callback>();
|
||||
|
||||
SUITE(test_callback_timer_interrupt)
|
||||
{
|
||||
//*************************************************************************
|
||||
@ -1001,5 +1028,55 @@ namespace
|
||||
// Check the
|
||||
CHECK_EQUAL(0U, ScopedGuard::guard_count);
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
// Regression test for issue #967:
|
||||
// Calling start() from within a repeating timer's callback (after the
|
||||
// period has been changed, which stops the timer) must not insert the timer
|
||||
// twice and create a circular reference in the active list.
|
||||
//*************************************************************************
|
||||
TEST(callback_timer_interrupt_restart_in_callback_no_circular_reference)
|
||||
{
|
||||
etl::callback_timer_interrupt<1, ScopedGuard> timer_controller;
|
||||
|
||||
restart_in_callback.p_controller = &timer_controller;
|
||||
restart_in_callback.new_period = 20U;
|
||||
restart_in_callback.call_ticks.clear();
|
||||
|
||||
etl::timer::id::type id = timer_controller.register_timer(restart_in_callback_member, 10, etl::timer::mode::Repeating);
|
||||
restart_in_callback.id = id;
|
||||
|
||||
timer_controller.start(id);
|
||||
timer_controller.enable(true);
|
||||
|
||||
ticks = 0;
|
||||
|
||||
const uint32_t step = 1U;
|
||||
|
||||
// Run long enough for several firings at the original (10) then new (20) period.
|
||||
while (ticks < 100U)
|
||||
{
|
||||
ticks += step;
|
||||
timer_controller.tick(step);
|
||||
}
|
||||
|
||||
// Detect a duplicated entry in the active list directly, rather than
|
||||
// relying on the resulting circular reference hanging the test.
|
||||
CHECK_FALSE(timer_controller.duplicate_insert_detected());
|
||||
|
||||
// The timer must still be active (alive, not corrupted) and scheduled at
|
||||
// the new period. A circular reference would corrupt the active list.
|
||||
CHECK_TRUE(timer_controller.is_active(id));
|
||||
|
||||
// First firing at 10, then every 20 thereafter: 10, 30, 50, 70, 90.
|
||||
std::vector<uint64_t> compare = {10, 30, 50, 70, 90};
|
||||
|
||||
CHECK_EQUAL(compare.size(), restart_in_callback.call_ticks.size());
|
||||
CHECK_ARRAY_EQUAL(compare.data(), restart_in_callback.call_ticks.data(), compare.size());
|
||||
|
||||
timer_controller.clear();
|
||||
|
||||
CHECK_EQUAL(0U, ScopedGuard::guard_count);
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
@ -184,6 +184,33 @@ namespace
|
||||
|
||||
callback_type free_function_callback2 = callback_type::create<free_callback2>();
|
||||
|
||||
//***************************************************************************
|
||||
// Fixture for a repeating timer that restarts itself from within its own
|
||||
// callback (regression test for issue #967).
|
||||
//***************************************************************************
|
||||
struct RestartInCallback
|
||||
{
|
||||
etl::callback_timer_locked<1>* p_controller = nullptr;
|
||||
etl::timer::id::type id = etl::timer::id::NO_TIMER;
|
||||
uint32_t new_period = 0U;
|
||||
std::vector<uint64_t> call_ticks;
|
||||
|
||||
void callback()
|
||||
{
|
||||
call_ticks.push_back(ticks);
|
||||
|
||||
// Change the period (this stops/deactivates the timer) and restart it
|
||||
// from inside the callback. Before the fix, tick() would then insert the
|
||||
// timer a second time, creating a circular reference in the active list.
|
||||
p_controller->set_period(id, new_period);
|
||||
p_controller->start(id);
|
||||
}
|
||||
};
|
||||
|
||||
RestartInCallback restart_in_callback;
|
||||
|
||||
callback_type restart_in_callback_member = callback_type::create<RestartInCallback, restart_in_callback, &RestartInCallback::callback>();
|
||||
|
||||
SUITE(test_callback_timer_locked)
|
||||
{
|
||||
//*************************************************************************
|
||||
@ -1004,6 +1031,59 @@ namespace
|
||||
CHECK_FALSE(timer_controller.has_active_timer());
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
// Regression test for issue #967:
|
||||
// Calling start() from within a repeating timer's callback (after the
|
||||
// period has been changed, which stops the timer) must not insert the timer
|
||||
// twice and create a circular reference in the active list.
|
||||
//*************************************************************************
|
||||
TEST(callback_timer_locked_restart_in_callback_no_circular_reference)
|
||||
{
|
||||
locks.clear();
|
||||
try_lock_type try_lock = try_lock_type::create<Locks, locks, &Locks::try_lock>();
|
||||
lock_type lock = lock_type::create<Locks, locks, &Locks::lock>();
|
||||
unlock_type unlock = unlock_type::create<Locks, locks, &Locks::unlock>();
|
||||
|
||||
etl::callback_timer_locked<1> timer_controller(try_lock, lock, unlock);
|
||||
|
||||
restart_in_callback.p_controller = &timer_controller;
|
||||
restart_in_callback.new_period = 20U;
|
||||
restart_in_callback.call_ticks.clear();
|
||||
|
||||
etl::timer::id::type id = timer_controller.register_timer(restart_in_callback_member, 10, etl::timer::mode::Repeating);
|
||||
restart_in_callback.id = id;
|
||||
|
||||
timer_controller.start(id);
|
||||
timer_controller.enable(true);
|
||||
|
||||
ticks = 0;
|
||||
|
||||
const uint32_t step = 1U;
|
||||
|
||||
// Run long enough for several firings at the original (10) then new (20) period.
|
||||
while (ticks < 100U)
|
||||
{
|
||||
ticks += step;
|
||||
timer_controller.tick(step);
|
||||
}
|
||||
|
||||
// Detect a duplicated entry in the active list directly, rather than
|
||||
// relying on the resulting circular reference hanging the test.
|
||||
CHECK_FALSE(timer_controller.duplicate_insert_detected());
|
||||
|
||||
// The timer must still be active (alive, not corrupted) and scheduled at
|
||||
// the new period. A circular reference would corrupt the active list.
|
||||
CHECK_TRUE(timer_controller.is_active(id));
|
||||
|
||||
// First firing at 10, then every 20 thereafter: 10, 30, 50, 70, 90.
|
||||
std::vector<uint64_t> compare = {10, 30, 50, 70, 90};
|
||||
|
||||
CHECK_EQUAL(compare.size(), restart_in_callback.call_ticks.size());
|
||||
CHECK_ARRAY_EQUAL(compare.data(), restart_in_callback.call_ticks.data(), compare.size());
|
||||
|
||||
timer_controller.clear();
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
#if REALTIME_TEST
|
||||
|
||||
|
||||
@ -1052,9 +1052,34 @@ namespace
|
||||
Chrono::seconds s1(2); // 2 seconds
|
||||
Chrono::milliseconds ms1(1500); // 1500 milliseconds
|
||||
|
||||
CHECK_TRUE(s1 <=> s1 == std::strong_ordering::equal);
|
||||
CHECK_TRUE(ms1 <=> s1 == std::strong_ordering::less);
|
||||
CHECK_TRUE(s1 <=> ms1 == std::strong_ordering::greater);
|
||||
CHECK_TRUE(s1 <=> s1 == etl::strong_ordering::equal);
|
||||
CHECK_TRUE(ms1 <=> s1 == etl::strong_ordering::less);
|
||||
CHECK_TRUE(s1 <=> ms1 == etl::strong_ordering::greater);
|
||||
|
||||
static_assert(etl::is_same<decltype(s1 <=> ms1), etl::strong_ordering>::value, "Must return etl::strong_ordering");
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
TEST(test_duration_spaceship_operator_floating_point_representation)
|
||||
{
|
||||
typedef Chrono::duration<double> fp_seconds;
|
||||
|
||||
fp_seconds fs1(1.5);
|
||||
fp_seconds fs2(2.5);
|
||||
|
||||
// A floating point representation is only partially ordered.
|
||||
static_assert(etl::is_same<decltype(fs1 <=> fs2), etl::partial_ordering>::value, "Must return etl::partial_ordering");
|
||||
|
||||
CHECK_TRUE((fs1 <=> fs1) == etl::partial_ordering::equivalent);
|
||||
CHECK_TRUE((fs1 <=> fs2) == etl::partial_ordering::less);
|
||||
CHECK_TRUE((fs2 <=> fs1) == etl::partial_ordering::greater);
|
||||
|
||||
fp_seconds nan_seconds(etl::numeric_limits<double>::quiet_NaN());
|
||||
|
||||
CHECK_TRUE((nan_seconds <=> fs1) == etl::partial_ordering::unordered);
|
||||
CHECK_FALSE((nan_seconds <=> fs1) < 0);
|
||||
CHECK_FALSE((nan_seconds <=> fs1) == 0);
|
||||
CHECK_FALSE((nan_seconds <=> fs1) > 0);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
@ -97,9 +97,9 @@ namespace
|
||||
Chrono::month_day md2{Chrono::February, Chrono::day{10}};
|
||||
Chrono::month_day md3{Chrono::January, Chrono::day{15}};
|
||||
|
||||
CHECK_TRUE((md1 <=> md3) == std::strong_ordering::equal); // Same month and day
|
||||
CHECK_TRUE((md1 <=> md2) == std::strong_ordering::less); // Different month and day
|
||||
CHECK_TRUE((md2 <=> md1) == std::strong_ordering::greater); // Same month and day
|
||||
CHECK_TRUE((md1 <=> md3) == etl::strong_ordering::equal); // Same month and day
|
||||
CHECK_TRUE((md1 <=> md2) == etl::strong_ordering::less); // Different month and day
|
||||
CHECK_TRUE((md2 <=> md1) == etl::strong_ordering::greater); // Same month and day
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
@ -100,6 +100,20 @@ namespace
|
||||
CHECK_TRUE(month_day_last1 != month_day_last3);
|
||||
}
|
||||
|
||||
#if ETL_USING_CPP20
|
||||
//*************************************************************************
|
||||
TEST(test_month_day_last_spaceship_operator)
|
||||
{
|
||||
Chrono::month_day_last month_day_last1(Chrono::January);
|
||||
Chrono::month_day_last month_day_last2(Chrono::February);
|
||||
Chrono::month_day_last month_day_last3(Chrono::January);
|
||||
|
||||
CHECK_TRUE((month_day_last1 <=> month_day_last3) == etl::strong_ordering::equal);
|
||||
CHECK_TRUE((month_day_last1 <=> month_day_last2) == etl::strong_ordering::less);
|
||||
CHECK_TRUE((month_day_last2 <=> month_day_last1) == etl::strong_ordering::greater);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if ETL_USING_ETL_CHRONO
|
||||
//*************************************************************************
|
||||
TEST(test_month_day_last_hashes_are_unique)
|
||||
|
||||
@ -377,6 +377,8 @@ namespace
|
||||
CHECK_TRUE((tp10d <=> tp10h) == 0);
|
||||
CHECK_TRUE((tp10h <=> tp20d) < 0);
|
||||
CHECK_TRUE((tp20d <=> tp10h) > 0);
|
||||
|
||||
static_assert(etl::is_same<decltype(tp10h <=> tp20d), etl::strong_ordering>::value, "Must return etl::strong_ordering");
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@ -97,11 +97,11 @@ namespace
|
||||
Chrono::year_month ym2{Chrono::year(2001), Chrono::January};
|
||||
Chrono::year_month ym3{Chrono::year(2000), Chrono::February};
|
||||
|
||||
CHECK_TRUE((ym1 <=> ym1) == std::strong_ordering::equal);
|
||||
CHECK_TRUE((ym1 <=> ym2) == std::strong_ordering::less);
|
||||
CHECK_TRUE((ym2 <=> ym1) == std::strong_ordering::greater);
|
||||
CHECK_TRUE((ym1 <=> ym3) == std::strong_ordering::less);
|
||||
CHECK_TRUE((ym3 <=> ym1) == std::strong_ordering::greater);
|
||||
CHECK_TRUE((ym1 <=> ym1) == etl::strong_ordering::equal);
|
||||
CHECK_TRUE((ym1 <=> ym2) == etl::strong_ordering::less);
|
||||
CHECK_TRUE((ym2 <=> ym1) == etl::strong_ordering::greater);
|
||||
CHECK_TRUE((ym1 <=> ym3) == etl::strong_ordering::less);
|
||||
CHECK_TRUE((ym3 <=> ym1) == etl::strong_ordering::greater);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
@ -107,13 +107,13 @@ namespace
|
||||
Chrono::year_month_day ym3{Chrono::year(2000), Chrono::February, Chrono::day(1)};
|
||||
Chrono::year_month_day ym4{Chrono::year(2000), Chrono::January, Chrono::day(2)};
|
||||
|
||||
CHECK_TRUE((ym1 <=> ym1) == std::strong_ordering::equal);
|
||||
CHECK_TRUE((ym1 <=> ym2) == std::strong_ordering::less);
|
||||
CHECK_TRUE((ym2 <=> ym1) == std::strong_ordering::greater);
|
||||
CHECK_TRUE((ym1 <=> ym3) == std::strong_ordering::less);
|
||||
CHECK_TRUE((ym3 <=> ym1) == std::strong_ordering::greater);
|
||||
CHECK_TRUE((ym1 <=> ym4) == std::strong_ordering::less);
|
||||
CHECK_TRUE((ym4 <=> ym1) == std::strong_ordering::greater);
|
||||
CHECK_TRUE((ym1 <=> ym1) == etl::strong_ordering::equal);
|
||||
CHECK_TRUE((ym1 <=> ym2) == etl::strong_ordering::less);
|
||||
CHECK_TRUE((ym2 <=> ym1) == etl::strong_ordering::greater);
|
||||
CHECK_TRUE((ym1 <=> ym3) == etl::strong_ordering::less);
|
||||
CHECK_TRUE((ym3 <=> ym1) == etl::strong_ordering::greater);
|
||||
CHECK_TRUE((ym1 <=> ym4) == etl::strong_ordering::less);
|
||||
CHECK_TRUE((ym4 <=> ym1) == etl::strong_ordering::greater);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
@ -160,6 +160,23 @@ namespace
|
||||
CHECK_TRUE(year_month_day_last1 != year_month_day_last3);
|
||||
}
|
||||
|
||||
#if ETL_USING_CPP20
|
||||
//*************************************************************************
|
||||
TEST(test_year_month_day_last_spaceship_operator)
|
||||
{
|
||||
Chrono::year_month_day_last year_month_day_last1(Chrono::year(2000), Chrono::month_day_last(Chrono::January));
|
||||
Chrono::year_month_day_last year_month_day_last2(Chrono::year(2001), Chrono::month_day_last(Chrono::January));
|
||||
Chrono::year_month_day_last year_month_day_last3(Chrono::year(2000), Chrono::month_day_last(Chrono::February));
|
||||
Chrono::year_month_day_last year_month_day_last4(Chrono::year(2000), Chrono::month_day_last(Chrono::January));
|
||||
|
||||
CHECK_TRUE((year_month_day_last1 <=> year_month_day_last4) == etl::strong_ordering::equal);
|
||||
CHECK_TRUE((year_month_day_last1 <=> year_month_day_last2) == etl::strong_ordering::less);
|
||||
CHECK_TRUE((year_month_day_last2 <=> year_month_day_last1) == etl::strong_ordering::greater);
|
||||
CHECK_TRUE((year_month_day_last1 <=> year_month_day_last3) == etl::strong_ordering::less);
|
||||
CHECK_TRUE((year_month_day_last3 <=> year_month_day_last1) == etl::strong_ordering::greater);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if ETL_USING_ETL_CHRONO
|
||||
//*************************************************************************
|
||||
TEST(test_year_month_day_last_hashes_are_unique)
|
||||
|
||||
@ -531,5 +531,157 @@ namespace
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if ETL_USING_CPP11
|
||||
//*************************************************************************
|
||||
TEST(test_referenced_clamped_value_uses_shared_range)
|
||||
{
|
||||
const etl::clamped_value_range<int> range(2, 7);
|
||||
etl::referenced_clamped_value<int> value1(range, 1);
|
||||
etl::referenced_clamped_value<int> value2(range, 8);
|
||||
|
||||
CHECK_EQUAL(2, value1.get());
|
||||
CHECK_EQUAL(7, value2.get());
|
||||
CHECK_EQUAL(2, value1.min());
|
||||
CHECK_EQUAL(7, value1.max());
|
||||
CHECK_EQUAL(&range, &value1.range());
|
||||
CHECK_EQUAL(&range, &value2.range());
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
TEST(test_referenced_clamped_value_arithmetic_saturates)
|
||||
{
|
||||
const etl::clamped_value_range<int> range(-2, 3);
|
||||
etl::referenced_clamped_value<int> value(range, 0);
|
||||
|
||||
value += 2;
|
||||
CHECK_EQUAL(2, value.get());
|
||||
++value;
|
||||
++value;
|
||||
CHECK_EQUAL(3, value.get());
|
||||
value -= 4;
|
||||
CHECK_EQUAL(-1, value.get());
|
||||
--value;
|
||||
--value;
|
||||
CHECK_EQUAL(-2, value.get());
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
TEST(test_referenced_clamped_value_modifiers_and_postfix_operators)
|
||||
{
|
||||
const etl::clamped_value_range<int> range(2, 7);
|
||||
etl::referenced_clamped_value<int> value(range);
|
||||
|
||||
CHECK_EQUAL(2, value.get());
|
||||
value.set(4);
|
||||
CHECK_EQUAL(4, value.get());
|
||||
|
||||
etl::referenced_clamped_value<int> previous = value++;
|
||||
CHECK_EQUAL(4, previous.get());
|
||||
CHECK_EQUAL(5, value.get());
|
||||
|
||||
previous = value--;
|
||||
CHECK_EQUAL(5, previous.get());
|
||||
CHECK_EQUAL(4, value.get());
|
||||
|
||||
value.to_max();
|
||||
CHECK_EQUAL(7, value.get());
|
||||
value.to_min();
|
||||
CHECK_EQUAL(2, value.get());
|
||||
|
||||
value = 100;
|
||||
CHECK_EQUAL(7, value.get());
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
TEST(test_referenced_clamped_value_default_range_and_comparisons)
|
||||
{
|
||||
const etl::clamped_value_range<int> default_range;
|
||||
const etl::clamped_value_range<int> range(0, 10);
|
||||
etl::referenced_clamped_value<int> value1(range, 3);
|
||||
etl::referenced_clamped_value<int> value2(range, 4);
|
||||
const int converted = value1;
|
||||
|
||||
CHECK_EQUAL(etl::numeric_limits<int>::lowest(), default_range.min());
|
||||
CHECK_EQUAL(etl::numeric_limits<int>::max(), default_range.max());
|
||||
CHECK_EQUAL(3, converted);
|
||||
CHECK(value1 == value1);
|
||||
CHECK(value1 != value2);
|
||||
CHECK(value1 < value2);
|
||||
CHECK(value1 <= value2);
|
||||
CHECK(value2 > value1);
|
||||
CHECK(value2 >= value1);
|
||||
CHECK(value1 < 4);
|
||||
CHECK(3 == value1);
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
TEST(test_referenced_clamped_value_copy_assignment_and_swap_ranges)
|
||||
{
|
||||
const etl::clamped_value_range<int> range1(0, 10);
|
||||
const etl::clamped_value_range<int> range2(20, 30);
|
||||
etl::referenced_clamped_value<int> value1(range1, 4);
|
||||
etl::referenced_clamped_value<int> value2(range2, 24);
|
||||
|
||||
etl::referenced_clamped_value<int> copy(value1);
|
||||
CHECK_EQUAL(&range1, ©.range());
|
||||
CHECK_EQUAL(4, copy.get());
|
||||
|
||||
copy = value2;
|
||||
CHECK_EQUAL(&range2, ©.range());
|
||||
CHECK_EQUAL(24, copy.get());
|
||||
|
||||
swap(value1, value2);
|
||||
CHECK_EQUAL(&range2, &value1.range());
|
||||
CHECK_EQUAL(24, value1.get());
|
||||
CHECK_EQUAL(&range1, &value2.range());
|
||||
CHECK_EQUAL(4, value2.get());
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
TEST(test_referenced_clamped_value_rejects_invalid_range)
|
||||
{
|
||||
CHECK_THROW((etl::clamped_value_range<int>(7, 2)), etl::exception);
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
TEST(test_referenced_clamped_value_rejects_temporary_range)
|
||||
{
|
||||
typedef etl::clamped_value_range<int> range_type;
|
||||
typedef etl::referenced_clamped_value<int> value_type;
|
||||
|
||||
CHECK_FALSE((etl::is_constructible<value_type, range_type&&>::value));
|
||||
CHECK_FALSE((etl::is_constructible<value_type, range_type&&, int>::value));
|
||||
}
|
||||
|
||||
#if ETL_HAS_FLOATING_POINT_CLAMPED_VALUE
|
||||
//*************************************************************************
|
||||
TEST(test_referenced_floating_clamped_value)
|
||||
{
|
||||
const etl::clamped_value_range<float> range(-1.5F, 2.5F);
|
||||
etl::referenced_clamped_value<float> value(range, 0.5F);
|
||||
|
||||
value += 1.25F;
|
||||
CHECK_CLOSE(1.75F, value.get(), 0.0001F);
|
||||
value.advance(10.0F);
|
||||
CHECK_CLOSE(2.5F, value.get(), 0.0001F);
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
TEST(test_referenced_floating_clamped_value_rejects_nan)
|
||||
{
|
||||
const float nan = etl::numeric_limits<float>::quiet_NaN();
|
||||
|
||||
CHECK_THROW((etl::clamped_value_range<float>(nan, 1.0F)), etl::exception);
|
||||
|
||||
const etl::clamped_value_range<float> range(-1.0F, 1.0F);
|
||||
CHECK_THROW((etl::referenced_clamped_value<float>(range, nan)), etl::exception);
|
||||
|
||||
etl::referenced_clamped_value<float> value(range, 0.0F);
|
||||
CHECK_THROW(value = nan, etl::exception);
|
||||
CHECK_THROW(value.advance(nan), etl::exception);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
} // namespace
|
||||
|
||||
@ -91,6 +91,20 @@ namespace
|
||||
}
|
||||
};
|
||||
|
||||
struct ComparatorOnlyObject
|
||||
{
|
||||
int a;
|
||||
int b;
|
||||
};
|
||||
|
||||
struct LessBySecond
|
||||
{
|
||||
bool constexpr operator()(const ComparatorOnlyObject& lhs, const ComparatorOnlyObject& rhs) const
|
||||
{
|
||||
return lhs.b < rhs.b;
|
||||
}
|
||||
};
|
||||
|
||||
typedef etl::compare<Object, LessTest> CompareTest;
|
||||
|
||||
SUITE(test_compare)
|
||||
@ -191,5 +205,139 @@ namespace
|
||||
CHECK_EQUAL(CompareInt::Greater, cmp_test_greater);
|
||||
CHECK_EQUAL(CompareInt::Equal, cmp_test_equal);
|
||||
}
|
||||
|
||||
#if ETL_USING_CPP20
|
||||
//*************************************************************************
|
||||
TEST(test_strong_ordering)
|
||||
{
|
||||
constexpr etl::strong_ordering ordering_less(etl::strong_ordering::less);
|
||||
constexpr etl::strong_ordering ordering_equal(etl::strong_ordering::equal);
|
||||
constexpr etl::strong_ordering ordering_greater(etl::strong_ordering::greater);
|
||||
|
||||
CHECK(ordering_less < 0);
|
||||
CHECK(ordering_less <= 0);
|
||||
CHECK(!(ordering_less == 0));
|
||||
CHECK(!(ordering_less > 0));
|
||||
CHECK(ordering_equal == 0);
|
||||
CHECK(ordering_equal <= 0);
|
||||
CHECK(ordering_equal >= 0);
|
||||
CHECK(ordering_greater > 0);
|
||||
CHECK(ordering_greater >= 0);
|
||||
CHECK(!(ordering_greater == 0));
|
||||
|
||||
// Reversed operands.
|
||||
CHECK(0 > ordering_less);
|
||||
CHECK(0 < ordering_greater);
|
||||
|
||||
static_assert(etl::strong_ordering::equivalent == etl::strong_ordering::equal, "Equivalent must alias equal");
|
||||
static_assert(etl::is_same<etl::weak_ordering, decltype(etl::weak_ordering(etl::strong_ordering::less))>::value,
|
||||
"Must convert to weak_ordering");
|
||||
CHECK(etl::weak_ordering(etl::strong_ordering::less) < 0);
|
||||
CHECK(etl::partial_ordering(etl::strong_ordering::greater) > 0);
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
TEST(test_make_strong_ordering)
|
||||
{
|
||||
constexpr etl::strong_ordering ordering_less = etl::make_strong_ordering(-1);
|
||||
constexpr etl::strong_ordering ordering_equal = etl::make_strong_ordering(0);
|
||||
constexpr etl::strong_ordering ordering_greater = etl::make_strong_ordering(1);
|
||||
|
||||
CHECK(ordering_less < 0);
|
||||
CHECK(ordering_equal == 0);
|
||||
CHECK(ordering_greater > 0);
|
||||
|
||||
static_assert(etl::is_same<decltype(etl::make_strong_ordering(0)), etl::strong_ordering>::value, "Must return etl::strong_ordering");
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
TEST(test_weak_ordering)
|
||||
{
|
||||
constexpr etl::weak_ordering ordering_less(etl::weak_ordering::less);
|
||||
constexpr etl::weak_ordering ordering_equivalent(etl::weak_ordering::equivalent);
|
||||
constexpr etl::weak_ordering ordering_greater(etl::weak_ordering::greater);
|
||||
|
||||
CHECK(ordering_less < 0);
|
||||
CHECK(ordering_equivalent == 0);
|
||||
CHECK(ordering_greater > 0);
|
||||
|
||||
CHECK(0 > ordering_less);
|
||||
CHECK(0 < ordering_greater);
|
||||
|
||||
static_assert(etl::weak_ordering::equivalent == 0, "Equivalent must be zero");
|
||||
CHECK(etl::partial_ordering(etl::weak_ordering::less) < 0);
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
TEST(test_make_weak_ordering)
|
||||
{
|
||||
constexpr etl::weak_ordering ordering_less = etl::make_weak_ordering(-1);
|
||||
constexpr etl::weak_ordering ordering_equivalent = etl::make_weak_ordering(0);
|
||||
constexpr etl::weak_ordering ordering_greater = etl::make_weak_ordering(1);
|
||||
|
||||
CHECK(ordering_less < 0);
|
||||
CHECK(ordering_equivalent == 0);
|
||||
CHECK(ordering_greater > 0);
|
||||
|
||||
static_assert(etl::is_same<decltype(etl::make_weak_ordering(0)), etl::weak_ordering>::value, "Must return etl::weak_ordering");
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
TEST(test_partial_ordering)
|
||||
{
|
||||
constexpr etl::partial_ordering ordering_less(etl::partial_ordering::less);
|
||||
constexpr etl::partial_ordering ordering_equivalent(etl::partial_ordering::equivalent);
|
||||
constexpr etl::partial_ordering ordering_greater(etl::partial_ordering::greater);
|
||||
constexpr etl::partial_ordering ordering_unordered(etl::partial_ordering::unordered);
|
||||
|
||||
CHECK(ordering_less < 0);
|
||||
CHECK(ordering_equivalent == 0);
|
||||
CHECK(ordering_greater > 0);
|
||||
|
||||
// An unordered result compares false against zero in every direction.
|
||||
CHECK(!(ordering_unordered < 0));
|
||||
CHECK(!(ordering_unordered <= 0));
|
||||
CHECK(!(ordering_unordered == 0));
|
||||
CHECK(!(ordering_unordered >= 0));
|
||||
CHECK(!(ordering_unordered > 0));
|
||||
CHECK(ordering_unordered != 0);
|
||||
|
||||
static_assert(etl::partial_ordering::equivalent == 0, "Equivalent must be zero");
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
TEST(test_make_partial_ordering)
|
||||
{
|
||||
constexpr etl::partial_ordering ordering_less = etl::make_partial_ordering(-1);
|
||||
constexpr etl::partial_ordering ordering_equivalent = etl::make_partial_ordering(0);
|
||||
constexpr etl::partial_ordering ordering_greater = etl::make_partial_ordering(1);
|
||||
|
||||
CHECK(ordering_less < 0);
|
||||
CHECK(ordering_equivalent == 0);
|
||||
CHECK(ordering_greater > 0);
|
||||
|
||||
static_assert(etl::is_same<decltype(etl::make_partial_ordering(0)), etl::partial_ordering>::value, "Must return etl::partial_ordering");
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
TEST(test_three_way_compare_custom_less)
|
||||
{
|
||||
constexpr ComparatorOnlyObject lhs = {5, 1};
|
||||
constexpr ComparatorOnlyObject rhs = {1, 2};
|
||||
constexpr ComparatorOnlyObject eq = {9, 1};
|
||||
|
||||
constexpr int cmp_less = etl::three_way_compare<ComparatorOnlyObject, LessBySecond>(lhs, rhs);
|
||||
constexpr int cmp_greater = etl::three_way_compare<ComparatorOnlyObject, LessBySecond>(rhs, lhs);
|
||||
constexpr int cmp_equal = etl::three_way_compare<ComparatorOnlyObject, LessBySecond>(lhs, eq);
|
||||
|
||||
CHECK_EQUAL(-1, cmp_less);
|
||||
CHECK_EQUAL(1, cmp_greater);
|
||||
CHECK_EQUAL(0, cmp_equal);
|
||||
|
||||
CHECK(etl::make_strong_ordering(cmp_less) < 0);
|
||||
CHECK(etl::make_strong_ordering(cmp_greater) > 0);
|
||||
CHECK(etl::make_strong_ordering(cmp_equal) == 0);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
} // namespace
|
||||
|
||||
@ -282,6 +282,294 @@ namespace
|
||||
CHECK_THROW(etl::be_uint8_t be_byte_empty(buffer.data(), 0U), etl::unaligned_type_buffer_size);
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
// 'value_from' reads a value straight out of a byte buffer, without first
|
||||
// copying the bytes into an unaligned_type's own storage.
|
||||
//*************************************************************************
|
||||
TEST(test_value_from_integral)
|
||||
{
|
||||
const std::array<unsigned char, 8> buffer = {0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0};
|
||||
|
||||
CHECK_EQUAL(uint16_t(0x1234), etl::be_uint16_t::value_from(buffer.data()));
|
||||
CHECK_EQUAL(uint16_t(0x3412), etl::le_uint16_t::value_from(buffer.data()));
|
||||
|
||||
CHECK_EQUAL(uint32_t(0x12345678), etl::be_uint32_t::value_from(buffer.data()));
|
||||
CHECK_EQUAL(uint32_t(0x78563412), etl::le_uint32_t::value_from(buffer.data()));
|
||||
|
||||
CHECK_EQUAL(uint64_t(0x123456789ABCDEF0), etl::be_uint64_t::value_from(buffer.data()));
|
||||
CHECK_EQUAL(uint64_t(0xF0DEBC9A78563412), etl::le_uint64_t::value_from(buffer.data()));
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
/// The address need not be aligned for the value's type.
|
||||
//*************************************************************************
|
||||
TEST(test_value_from_unaligned_address)
|
||||
{
|
||||
const std::array<unsigned char, 6> buffer = {0xFF, 0x12, 0x34, 0x56, 0x78, 0xFF};
|
||||
|
||||
CHECK_EQUAL(uint32_t(0x12345678), etl::be_uint32_t::value_from(buffer.data() + 1));
|
||||
CHECK_EQUAL(uint32_t(0x78563412), etl::le_uint32_t::value_from(buffer.data() + 1));
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
/// 'value_from' must agree with constructing from the same address and
|
||||
/// then reading the value back out.
|
||||
//*************************************************************************
|
||||
TEST(test_value_from_matches_construction)
|
||||
{
|
||||
const std::array<unsigned char, 8> buffer = {0xEE, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF};
|
||||
|
||||
CHECK_EQUAL(etl::be_uint16_t(buffer.data()).value(), etl::be_uint16_t::value_from(buffer.data()));
|
||||
CHECK_EQUAL(etl::le_uint16_t(buffer.data()).value(), etl::le_uint16_t::value_from(buffer.data()));
|
||||
CHECK_EQUAL(etl::be_uint32_t(buffer.data()).value(), etl::be_uint32_t::value_from(buffer.data()));
|
||||
CHECK_EQUAL(etl::le_uint32_t(buffer.data()).value(), etl::le_uint32_t::value_from(buffer.data()));
|
||||
CHECK_EQUAL(etl::be_uint64_t(buffer.data()).value(), etl::be_uint64_t::value_from(buffer.data()));
|
||||
CHECK_EQUAL(etl::le_uint64_t(buffer.data()).value(), etl::le_uint64_t::value_from(buffer.data()));
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
/// Boundary values must survive the round trip through the store.
|
||||
//*************************************************************************
|
||||
TEST(test_value_from_boundary_values)
|
||||
{
|
||||
const std::array<unsigned char, 4> zeroes = {0x00, 0x00, 0x00, 0x00};
|
||||
const std::array<unsigned char, 4> ones = {0xFF, 0xFF, 0xFF, 0xFF};
|
||||
|
||||
CHECK_EQUAL(uint32_t(0), etl::be_uint32_t::value_from(zeroes.data()));
|
||||
CHECK_EQUAL(uint32_t(0), etl::le_uint32_t::value_from(zeroes.data()));
|
||||
|
||||
CHECK_EQUAL(etl::integral_limits<uint32_t>::max, etl::be_uint32_t::value_from(ones.data()));
|
||||
CHECK_EQUAL(etl::integral_limits<uint32_t>::max, etl::le_uint32_t::value_from(ones.data()));
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
/// Signed types are reconstructed through their unsigned representation.
|
||||
//*************************************************************************
|
||||
TEST(test_value_from_negative_values)
|
||||
{
|
||||
const std::array<unsigned char, 4> buffer = {0xFF, 0xFF, 0xFF, 0xFE};
|
||||
|
||||
CHECK_EQUAL(int32_t(-2), etl::be_int32_t::value_from(buffer.data()));
|
||||
CHECK_EQUAL(int16_t(-1), etl::be_int16_t::value_from(buffer.data()));
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
/// The floating point specialisation provides 'value_from' too.
|
||||
//*************************************************************************
|
||||
TEST(test_value_from_float)
|
||||
{
|
||||
const float f = 3.1415927f;
|
||||
const double d = 2.718281828459045;
|
||||
|
||||
etl::be_float_t be_f(f);
|
||||
etl::le_double_t le_d(d);
|
||||
|
||||
CHECK_EQUAL(f, etl::be_float_t::value_from(be_f.data()));
|
||||
CHECK_EQUAL(d, etl::le_double_t::value_from(le_d.data()));
|
||||
}
|
||||
/// Unsigned integers that have no native equivalent, such as 24 bit.
|
||||
//*************************************************************************
|
||||
TEST(test_uint24)
|
||||
{
|
||||
CHECK_EQUAL(3U, sizeof(etl::le_uint24_t));
|
||||
CHECK_EQUAL(3U, sizeof(etl::be_uint24_t));
|
||||
CHECK_EQUAL(3U, size_t(etl::le_uint24_t::Size));
|
||||
|
||||
CHECK((etl::is_same<uint32_t, etl::le_uint24_t::value_type>::value));
|
||||
CHECK_EQUAL(uint32_t(0x00FFFFFF), etl::le_uint24_t::Max_Value);
|
||||
|
||||
etl::le_uint24_t le_v(0x123456U);
|
||||
etl::be_uint24_t be_v(0x123456U);
|
||||
|
||||
CHECK_EQUAL(uint32_t(0x123456), le_v.value());
|
||||
CHECK_EQUAL(uint32_t(0x123456), be_v.value());
|
||||
|
||||
// Check the storage byte order.
|
||||
CHECK_EQUAL(0x56, int(le_v.data()[0]));
|
||||
CHECK_EQUAL(0x34, int(le_v.data()[1]));
|
||||
CHECK_EQUAL(0x12, int(le_v.data()[2]));
|
||||
|
||||
CHECK_EQUAL(0x12, int(be_v.data()[0]));
|
||||
CHECK_EQUAL(0x34, int(be_v.data()[1]));
|
||||
CHECK_EQUAL(0x56, int(be_v.data()[2]));
|
||||
|
||||
// Copy construction, including from the other endianness.
|
||||
etl::le_uint24_t le_v2(le_v);
|
||||
etl::be_uint24_t be_v2(be_v);
|
||||
etl::le_uint24_t le_v3(be_v);
|
||||
etl::be_uint24_t be_v3(le_v);
|
||||
|
||||
CHECK_EQUAL(uint32_t(0x123456), le_v2.value());
|
||||
CHECK_EQUAL(uint32_t(0x123456), be_v2.value());
|
||||
CHECK_EQUAL(uint32_t(0x123456), le_v3.value());
|
||||
CHECK_EQUAL(uint32_t(0x123456), be_v3.value());
|
||||
|
||||
// Assignment.
|
||||
le_v2 = 0xFFFFFFU;
|
||||
be_v2 = 0xFFFFFFU;
|
||||
CHECK_EQUAL(uint32_t(0xFFFFFF), le_v2.value());
|
||||
CHECK_EQUAL(uint32_t(0xFFFFFF), be_v2.value());
|
||||
|
||||
// Boundary values.
|
||||
etl::le_uint24_t le_zero(0U);
|
||||
etl::be_uint24_t be_zero(0U);
|
||||
CHECK_EQUAL(uint32_t(0), le_zero.value());
|
||||
CHECK_EQUAL(uint32_t(0), be_zero.value());
|
||||
|
||||
// Values that do not fit are truncated, like a narrowing cast.
|
||||
etl::le_uint24_t le_truncated(0xAB123456U);
|
||||
etl::be_uint24_t be_truncated(0xAB123456U);
|
||||
CHECK_EQUAL(uint32_t(0x123456), le_truncated.value());
|
||||
CHECK_EQUAL(uint32_t(0x123456), be_truncated.value());
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
TEST(test_uint24_decode_buffer)
|
||||
{
|
||||
const std::array<unsigned char, 4> buffer = {0xAA, 0xBB, 0xCC, 0xDD};
|
||||
|
||||
etl::le_uint24_t le_v(buffer.data(), buffer.size());
|
||||
etl::be_uint24_t be_v(buffer.data(), buffer.size());
|
||||
|
||||
CHECK_EQUAL(uint32_t(0xCCBBAA), le_v.value());
|
||||
CHECK_EQUAL(uint32_t(0xAABBCC), be_v.value());
|
||||
|
||||
// One byte short.
|
||||
CHECK_THROW(etl::le_uint24_t le_short(buffer.data(), 2U), etl::unaligned_type_buffer_size);
|
||||
CHECK_THROW(etl::be_uint24_t be_short(buffer.data(), 2U), etl::unaligned_type_buffer_size);
|
||||
}
|
||||
|
||||
#if ETL_USING_64BIT_TYPES
|
||||
//*************************************************************************
|
||||
TEST(test_uint40_uint48_uint56)
|
||||
{
|
||||
CHECK_EQUAL(5U, sizeof(etl::be_uint40_t));
|
||||
CHECK_EQUAL(6U, sizeof(etl::be_uint48_t));
|
||||
CHECK_EQUAL(7U, sizeof(etl::be_uint56_t));
|
||||
|
||||
etl::be_uint48_t be_v(0x0123456789ABULL);
|
||||
etl::le_uint48_t le_v(0x0123456789ABULL);
|
||||
|
||||
CHECK_EQUAL(uint64_t(0x0123456789AB), be_v.value());
|
||||
CHECK_EQUAL(uint64_t(0x0123456789AB), le_v.value());
|
||||
|
||||
CHECK_EQUAL(0x01, int(be_v.data()[0]));
|
||||
CHECK_EQUAL(0xAB, int(le_v.data()[0]));
|
||||
|
||||
CHECK_EQUAL(uint64_t(0x000000FFFFFFFFFF), etl::be_uint40_t::Max_Value);
|
||||
CHECK_EQUAL(uint64_t(0x00FFFFFFFFFFFFFF), etl::be_uint56_t::Max_Value);
|
||||
}
|
||||
#endif
|
||||
|
||||
//*************************************************************************
|
||||
/// Signed integers that have no native equivalent, such as 24 bit.
|
||||
//*************************************************************************
|
||||
TEST(test_int24)
|
||||
{
|
||||
CHECK_EQUAL(3U, sizeof(etl::le_int24_t));
|
||||
CHECK_EQUAL(3U, sizeof(etl::be_int24_t));
|
||||
CHECK_EQUAL(3U, size_t(etl::le_int24_t::Size));
|
||||
|
||||
CHECK((etl::is_same<int32_t, etl::le_int24_t::value_type>::value));
|
||||
CHECK_EQUAL(int32_t(8388607), etl::le_int24_t::Max_Value);
|
||||
CHECK_EQUAL(int32_t(-8388608), etl::le_int24_t::Min_Value);
|
||||
|
||||
etl::le_int24_t le_v(0x123456);
|
||||
etl::be_int24_t be_v(0x123456);
|
||||
|
||||
CHECK_EQUAL(int32_t(0x123456), le_v.value());
|
||||
CHECK_EQUAL(int32_t(0x123456), be_v.value());
|
||||
|
||||
// Check the storage byte order.
|
||||
CHECK_EQUAL(0x56, int(le_v.data()[0]));
|
||||
CHECK_EQUAL(0x12, int(le_v.data()[2]));
|
||||
CHECK_EQUAL(0x12, int(be_v.data()[0]));
|
||||
CHECK_EQUAL(0x56, int(be_v.data()[2]));
|
||||
|
||||
// Negative values are sign extended on reading.
|
||||
etl::le_int24_t le_n(-2);
|
||||
etl::be_int24_t be_n(-2);
|
||||
|
||||
CHECK_EQUAL(int32_t(-2), le_n.value());
|
||||
CHECK_EQUAL(int32_t(-2), be_n.value());
|
||||
|
||||
CHECK_EQUAL(0xFE, int(le_n.data()[0]));
|
||||
CHECK_EQUAL(0xFF, int(le_n.data()[1]));
|
||||
CHECK_EQUAL(0xFF, int(le_n.data()[2]));
|
||||
|
||||
CHECK_EQUAL(0xFF, int(be_n.data()[0]));
|
||||
CHECK_EQUAL(0xFF, int(be_n.data()[1]));
|
||||
CHECK_EQUAL(0xFE, int(be_n.data()[2]));
|
||||
|
||||
// Copy construction, including from the other endianness.
|
||||
etl::le_int24_t le_n2(be_n);
|
||||
etl::be_int24_t be_n2(le_n);
|
||||
|
||||
CHECK_EQUAL(int32_t(-2), le_n2.value());
|
||||
CHECK_EQUAL(int32_t(-2), be_n2.value());
|
||||
|
||||
// Assignment.
|
||||
le_n2 = -1;
|
||||
be_n2 = -1;
|
||||
CHECK_EQUAL(int32_t(-1), le_n2.value());
|
||||
CHECK_EQUAL(int32_t(-1), be_n2.value());
|
||||
|
||||
// Boundary values.
|
||||
etl::le_int24_t le_min(etl::le_int24_t::Min_Value);
|
||||
etl::be_int24_t be_min(etl::be_int24_t::Min_Value);
|
||||
etl::le_int24_t le_max(etl::le_int24_t::Max_Value);
|
||||
etl::be_int24_t be_max(etl::be_int24_t::Max_Value);
|
||||
|
||||
CHECK_EQUAL(int32_t(-8388608), le_min.value());
|
||||
CHECK_EQUAL(int32_t(-8388608), be_min.value());
|
||||
CHECK_EQUAL(int32_t(8388607), le_max.value());
|
||||
CHECK_EQUAL(int32_t(8388607), be_max.value());
|
||||
|
||||
etl::le_int24_t le_zero(0);
|
||||
CHECK_EQUAL(int32_t(0), le_zero.value());
|
||||
|
||||
// Values that do not fit are truncated, like a narrowing cast.
|
||||
etl::be_int24_t be_truncated(0x7F123456);
|
||||
CHECK_EQUAL(int32_t(0x123456), be_truncated.value());
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
TEST(test_int24_decode_buffer)
|
||||
{
|
||||
const std::array<unsigned char, 4> buffer = {0x80, 0x00, 0x00, 0xDD};
|
||||
|
||||
etl::le_int24_t le_v(buffer.data(), buffer.size());
|
||||
etl::be_int24_t be_v(buffer.data(), buffer.size());
|
||||
|
||||
CHECK_EQUAL(int32_t(0x000080), le_v.value());
|
||||
CHECK_EQUAL(int32_t(-8388608), be_v.value());
|
||||
|
||||
// One byte short.
|
||||
CHECK_THROW(etl::le_int24_t le_short(buffer.data(), 2U), etl::unaligned_type_buffer_size);
|
||||
CHECK_THROW(etl::be_int24_t be_short(buffer.data(), 2U), etl::unaligned_type_buffer_size);
|
||||
}
|
||||
|
||||
#if ETL_USING_64BIT_TYPES
|
||||
//*************************************************************************
|
||||
TEST(test_int40_int48_int56)
|
||||
{
|
||||
CHECK_EQUAL(5U, sizeof(etl::be_int40_t));
|
||||
CHECK_EQUAL(6U, sizeof(etl::be_int48_t));
|
||||
CHECK_EQUAL(7U, sizeof(etl::be_int56_t));
|
||||
|
||||
etl::be_int48_t be_v(-1099511627775LL);
|
||||
etl::le_int48_t le_v(-1099511627775LL);
|
||||
|
||||
CHECK_EQUAL(int64_t(-1099511627775LL), be_v.value());
|
||||
CHECK_EQUAL(int64_t(-1099511627775LL), le_v.value());
|
||||
|
||||
CHECK_EQUAL(int64_t(549755813887LL), etl::be_int40_t::Max_Value);
|
||||
CHECK_EQUAL(int64_t(-549755813888LL), etl::be_int40_t::Min_Value);
|
||||
CHECK_EQUAL(int64_t(36028797018963967LL), etl::be_int56_t::Max_Value);
|
||||
CHECK_EQUAL(int64_t(-36028797018963968LL), etl::be_int56_t::Min_Value);
|
||||
}
|
||||
#endif
|
||||
|
||||
//*************************************************************************
|
||||
// The following tests demonstrate the 'decode' direction: given a raw byte
|
||||
// buffer (e.g. as received from a file, network socket or memory-mapped
|
||||
@ -625,6 +913,25 @@ namespace
|
||||
CHECK_EQUAL(0x12345678U, be_v.value());
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
/// 'value_from' is usable at compile time for the same reason: it reads
|
||||
/// the buffer through the 'const unsigned char*' path.
|
||||
//*************************************************************************
|
||||
TEST(test_constexpr_value_from_integral)
|
||||
{
|
||||
static ETL_CONSTANT unsigned char le_buffer[4] = {0x78, 0x56, 0x34, 0x12};
|
||||
static ETL_CONSTANT unsigned char be_buffer[4] = {0x12, 0x34, 0x56, 0x78};
|
||||
|
||||
constexpr uint32_t le_v = etl::le_uint32_t::value_from(le_buffer);
|
||||
constexpr uint32_t be_v = etl::be_uint32_t::value_from(be_buffer);
|
||||
|
||||
static_assert(le_v == 0x12345678U, "le_uint32_t constexpr value_from");
|
||||
static_assert(be_v == 0x12345678U, "be_uint32_t constexpr value_from");
|
||||
|
||||
CHECK_EQUAL(0x12345678U, le_v);
|
||||
CHECK_EQUAL(0x12345678U, be_v);
|
||||
}
|
||||
|
||||
#if ETL_USING_BUILTIN_BIT_CAST
|
||||
//*************************************************************************
|
||||
TEST(test_constexpr_decode_buffer_float)
|
||||
|
||||
@ -49,6 +49,8 @@ namespace
|
||||
using storage_i16_t = std::array<unsigned char, sizeof(int16_t)>;
|
||||
using storage_i32_t = std::array<unsigned char, sizeof(int32_t)>;
|
||||
using storage_i64_t = std::array<unsigned char, sizeof(int64_t)>;
|
||||
using storage_24_t = std::array<unsigned char, 3U>;
|
||||
using storage_48_t = std::array<unsigned char, 6U>;
|
||||
|
||||
SUITE(test_unaligned_type_ext)
|
||||
{
|
||||
@ -506,6 +508,148 @@ namespace
|
||||
CHECK_EQUAL(sizeof(uint64_t), etl::net_uint64_ext_t::Size);
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
/// Integers that have no native equivalent, such as 24 bit.
|
||||
//*************************************************************************
|
||||
TEST(test_uint24)
|
||||
{
|
||||
storage_24_t store_le1;
|
||||
storage_24_t store_be1;
|
||||
storage_24_t store_le2;
|
||||
storage_24_t store_be2;
|
||||
|
||||
CHECK_EQUAL(3U, size_t(etl::le_uint24_ext_t::Size));
|
||||
CHECK((etl::is_same<uint32_t, etl::le_uint24_ext_t::value_type>::value));
|
||||
CHECK_EQUAL(uint32_t(0x00FFFFFF), etl::le_uint24_ext_t::Max_Value);
|
||||
CHECK_EQUAL(uint32_t(0), etl::le_uint24_ext_t::Min_Value);
|
||||
|
||||
etl::le_uint24_ext_t le_v1(store_le1.data());
|
||||
le_v1 = 0x123456U;
|
||||
etl::be_uint24_ext_t be_v1(0x123456U, store_be1.data());
|
||||
|
||||
CHECK_EQUAL(uint32_t(0x123456), le_v1.value());
|
||||
CHECK_EQUAL(uint32_t(0x123456), be_v1.value());
|
||||
|
||||
// The external buffers hold the requested byte order.
|
||||
CHECK_EQUAL(0x56, int(store_le1[0]));
|
||||
CHECK_EQUAL(0x12, int(store_le1[2]));
|
||||
CHECK_EQUAL(0x12, int(store_be1[0]));
|
||||
CHECK_EQUAL(0x56, int(store_be1[2]));
|
||||
|
||||
// Copy construct from the other endianness.
|
||||
etl::le_uint24_ext_t le_v2(be_v1, store_le2.data());
|
||||
etl::be_uint24_ext_t be_v2(le_v1, store_be2.data());
|
||||
|
||||
CHECK_EQUAL(uint32_t(0x123456), le_v2.value());
|
||||
CHECK_EQUAL(uint32_t(0x123456), be_v2.value());
|
||||
|
||||
// Assignment.
|
||||
le_v2 = le_v1;
|
||||
be_v2 = le_v1;
|
||||
CHECK_EQUAL(uint32_t(0x123456), le_v2.value());
|
||||
CHECK_EQUAL(uint32_t(0x123456), be_v2.value());
|
||||
|
||||
// Values that do not fit are truncated.
|
||||
be_v2 = 0xABCDEF12U;
|
||||
CHECK_EQUAL(uint32_t(0xCDEF12), be_v2.value());
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
TEST(test_move_construction_uint24)
|
||||
{
|
||||
storage_24_t store_le1;
|
||||
storage_24_t store_be1;
|
||||
|
||||
etl::le_uint24_ext_t le_v1(0x123456U, store_le1.data());
|
||||
etl::be_uint24_ext_t be_v1(0x123456U, store_be1.data());
|
||||
|
||||
etl::le_uint24_ext_t le_v2(etl::move(le_v1));
|
||||
etl::be_uint24_ext_t be_v2(etl::move(be_v1));
|
||||
|
||||
CHECK_TRUE(nullptr == le_v1.data());
|
||||
CHECK_TRUE(nullptr == be_v1.data());
|
||||
CHECK_EQUAL(uint32_t(0x123456), le_v2.value());
|
||||
CHECK_EQUAL(uint32_t(0x123456), be_v2.value());
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
TEST(test_int24)
|
||||
{
|
||||
storage_24_t store_le1;
|
||||
storage_24_t store_be1;
|
||||
storage_24_t store_le2;
|
||||
storage_24_t store_be2;
|
||||
|
||||
CHECK_EQUAL(3U, size_t(etl::le_int24_ext_t::Size));
|
||||
CHECK((etl::is_same<int32_t, etl::le_int24_ext_t::value_type>::value));
|
||||
CHECK_EQUAL(int32_t(8388607), etl::le_int24_ext_t::Max_Value);
|
||||
CHECK_EQUAL(int32_t(-8388608), etl::le_int24_ext_t::Min_Value);
|
||||
|
||||
etl::le_int24_ext_t le_v1(store_le1.data());
|
||||
le_v1 = -2;
|
||||
etl::be_int24_ext_t be_v1(-2, store_be1.data());
|
||||
|
||||
// Negative values are sign extended on reading.
|
||||
CHECK_EQUAL(int32_t(-2), le_v1.value());
|
||||
CHECK_EQUAL(int32_t(-2), be_v1.value());
|
||||
|
||||
CHECK_EQUAL(0xFE, int(store_le1[0]));
|
||||
CHECK_EQUAL(0xFF, int(store_le1[2]));
|
||||
CHECK_EQUAL(0xFF, int(store_be1[0]));
|
||||
CHECK_EQUAL(0xFE, int(store_be1[2]));
|
||||
|
||||
// Copy construct from the other endianness.
|
||||
etl::le_int24_ext_t le_v2(be_v1, store_le2.data());
|
||||
etl::be_int24_ext_t be_v2(le_v1, store_be2.data());
|
||||
|
||||
CHECK_EQUAL(int32_t(-2), le_v2.value());
|
||||
CHECK_EQUAL(int32_t(-2), be_v2.value());
|
||||
|
||||
// Boundary values.
|
||||
le_v2 = etl::le_int24_ext_t::Min_Value;
|
||||
be_v2 = etl::be_int24_ext_t::Max_Value;
|
||||
|
||||
CHECK_EQUAL(int32_t(-8388608), le_v2.value());
|
||||
CHECK_EQUAL(int32_t(8388607), be_v2.value());
|
||||
|
||||
le_v2 = 0;
|
||||
CHECK_EQUAL(int32_t(0), le_v2.value());
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
TEST(test_move_construction_int24)
|
||||
{
|
||||
storage_24_t store_le1;
|
||||
storage_24_t store_be1;
|
||||
|
||||
etl::le_int24_ext_t le_v1(-1234567, store_le1.data());
|
||||
etl::be_int24_ext_t be_v1(-1234567, store_be1.data());
|
||||
|
||||
etl::le_int24_ext_t le_v2(etl::move(le_v1));
|
||||
etl::be_int24_ext_t be_v2(etl::move(be_v1));
|
||||
|
||||
CHECK_TRUE(nullptr == le_v1.data());
|
||||
CHECK_TRUE(nullptr == be_v1.data());
|
||||
CHECK_EQUAL(int32_t(-1234567), le_v2.value());
|
||||
CHECK_EQUAL(int32_t(-1234567), be_v2.value());
|
||||
}
|
||||
|
||||
#if ETL_USING_64BIT_TYPES
|
||||
//*************************************************************************
|
||||
TEST(test_uint48_int48)
|
||||
{
|
||||
storage_48_t store_u;
|
||||
storage_48_t store_i;
|
||||
|
||||
etl::be_uint48_ext_t be_u(0x0123456789ABULL, store_u.data());
|
||||
etl::be_int48_ext_t be_i(-1099511627775LL, store_i.data());
|
||||
|
||||
CHECK_EQUAL(6U, size_t(etl::be_uint48_ext_t::Size));
|
||||
CHECK_EQUAL(uint64_t(0x0123456789AB), be_u.value());
|
||||
CHECK_EQUAL(int64_t(-1099511627775LL), be_i.value());
|
||||
}
|
||||
#endif
|
||||
|
||||
#if ETL_HAS_CONSTEXPR_ENDIANNESS
|
||||
//*************************************************************************
|
||||
TEST(test_host_sizes)
|
||||
@ -1528,6 +1672,106 @@ namespace
|
||||
CHECK_EQUAL(0x12, bev0);
|
||||
CHECK_EQUAL(0x34, bev1);
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
TEST(test_shared_storage_copy_construction_uint24)
|
||||
{
|
||||
storage_24_t store;
|
||||
|
||||
etl::be_uint24_ext_t be_v(0x123456, store.data());
|
||||
|
||||
// Reinterpret the same buffer with the opposite endianness.
|
||||
etl::le_uint24_ext_t le_v(be_v, store.data());
|
||||
|
||||
CHECK_EQUAL(0x56, int(store[0]));
|
||||
CHECK_EQUAL(0x34, int(store[1]));
|
||||
CHECK_EQUAL(0x12, int(store[2]));
|
||||
CHECK_EQUAL(0x123456, le_v.value());
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
TEST(test_shared_storage_copy_construction_int24)
|
||||
{
|
||||
storage_24_t store;
|
||||
|
||||
etl::be_int24_ext_t be_v(-0x123456, store.data());
|
||||
|
||||
etl::le_int24_ext_t le_v(be_v, store.data());
|
||||
|
||||
CHECK_EQUAL(-0x123456, le_v.value());
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
TEST(test_shared_storage_copy_assignment_uint24)
|
||||
{
|
||||
storage_24_t store;
|
||||
|
||||
etl::be_uint24_ext_t be_v(0x123456, store.data());
|
||||
etl::le_uint24_ext_t le_v(store.data());
|
||||
|
||||
le_v = be_v;
|
||||
|
||||
CHECK_EQUAL(0x56, int(store[0]));
|
||||
CHECK_EQUAL(0x34, int(store[1]));
|
||||
CHECK_EQUAL(0x12, int(store[2]));
|
||||
CHECK_EQUAL(0x123456, le_v.value());
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
TEST(test_shared_storage_copy_assignment_int24)
|
||||
{
|
||||
storage_24_t store;
|
||||
|
||||
etl::be_int24_ext_t be_v(-0x123456, store.data());
|
||||
etl::le_int24_ext_t le_v(store.data());
|
||||
|
||||
le_v = be_v;
|
||||
|
||||
CHECK_EQUAL(-0x123456, le_v.value());
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
TEST(test_shared_storage_copy_assignment_uint32)
|
||||
{
|
||||
storage_i32_t store;
|
||||
|
||||
etl::be_uint32_ext_t be_v(0x12345678, store.data());
|
||||
etl::le_uint32_ext_t le_v(store.data());
|
||||
|
||||
le_v = be_v;
|
||||
|
||||
CHECK_EQUAL(0x78, int(store[0]));
|
||||
CHECK_EQUAL(0x56, int(store[1]));
|
||||
CHECK_EQUAL(0x34, int(store[2]));
|
||||
CHECK_EQUAL(0x12, int(store[3]));
|
||||
CHECK_EQUAL(0x12345678, le_v.value());
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
TEST(test_shared_storage_copy_assignment_same_endianness)
|
||||
{
|
||||
storage_i32_t store;
|
||||
|
||||
etl::le_uint32_ext_t le_v1(0x12345678, store.data());
|
||||
etl::le_uint32_ext_t le_v2(store.data());
|
||||
|
||||
le_v2 = le_v1;
|
||||
|
||||
CHECK_EQUAL(0x12345678, le_v2.value());
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
TEST(test_shared_storage_copy_assignment_float)
|
||||
{
|
||||
storage_f_t store;
|
||||
|
||||
etl::be_float_ext_t be_v(3.1415927f, store.data());
|
||||
etl::le_float_ext_t le_v(store.data());
|
||||
|
||||
le_v = be_v;
|
||||
|
||||
CHECK_EQUAL(3.1415927f, le_v.value());
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user