From 6d9ed143b28a72170e94b5e6a3cee278ef3d62dc Mon Sep 17 00:00:00 2001 From: Roland Reichwein Date: Fri, 12 Jun 2026 09:04:24 +0200 Subject: [PATCH 1/2] Add etl::infinite_loop (#1458) --- docs/utilities/infinite-loop.md | 39 +++++++++++++++ include/etl/infinite_loop.h | 65 +++++++++++++++++++++++++ test/syntax_check/CMakeLists.txt | 1 + test/syntax_check/infinite_loop.h.t.cpp | 29 +++++++++++ 4 files changed, 134 insertions(+) create mode 100644 docs/utilities/infinite-loop.md create mode 100644 include/etl/infinite_loop.h create mode 100644 test/syntax_check/infinite_loop.h.t.cpp diff --git a/docs/utilities/infinite-loop.md b/docs/utilities/infinite-loop.md new file mode 100644 index 00000000..7b8672a4 --- /dev/null +++ b/docs/utilities/infinite-loop.md @@ -0,0 +1,39 @@ +--- +title: "infinite_loop" +--- + +{{< callout type="info">}} + Header: `infinite_loop.h` +{{< /callout >}} + +A portable infinite loop that will not be optimised away by the compiler. + +Before C++26, an empty infinite loop without side effects is considered undefined behaviour and may +be optimised away. This utility uses a compiler memory barrier to prevent that optimisation. + +See [P2809R1](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2023/p2809r1.html) for background. + +## Function + +```cpp +[[noreturn]] inline void etl::infinite_loop() +``` + +**Description** +Enters an infinite loop that is guaranteed not to be removed by the compiler. + +On GCC and Clang this is achieved with an inline assembly memory clobber. +On MSVC this uses `_ReadWriteBarrier()`. + +## Example + +```cpp +#include + +int main() +{ + // Initialise hardware... + + etl::infinite_loop(); +} +``` diff --git a/include/etl/infinite_loop.h b/include/etl/infinite_loop.h new file mode 100644 index 00000000..530e9b4c --- /dev/null +++ b/include/etl/infinite_loop.h @@ -0,0 +1,65 @@ +///\file + +/****************************************************************************** +The MIT License(MIT) + +Embedded Template Library. +https://github.com/ETLCPP/etl +https://www.etlcpp.com + +Copyright(c) 2026 BMW AG + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files(the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions : + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +******************************************************************************/ + +#ifndef ETL_INFINITE_LOOP_INCLUDED +#define ETL_INFINITE_LOOP_INCLUDED + +#include "platform.h" + +namespace etl +{ + //***************************************************************************** + /// An infinite loop that will not be optimised out + /// + /// Before C++26, an empty infinite loop without side effects is considered + /// undefined behavior, and may be optimised away by the compiler. + /// + /// See also + /// https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2023/p2809r1.html + //***************************************************************************** + ETL_NORETURN + inline void infinite_loop() + { + while (true) + { +#if ETL_NOT_USING_CPP26 + #if defined(ETL_COMPILER_GCC) || defined(ETL_COMPILER_CLANG) + __asm__ __volatile__("" : : : "memory"); + #elif defined(ETL_COMPILER_MICROSOFT) + _ReadWriteBarrier(); + #else + #error "Infinite loop not supported for this compiler and platform" + #endif +#endif + } + } +} // namespace etl + +#endif diff --git a/test/syntax_check/CMakeLists.txt b/test/syntax_check/CMakeLists.txt index 62fa017e..3546cfa3 100644 --- a/test/syntax_check/CMakeLists.txt +++ b/test/syntax_check/CMakeLists.txt @@ -243,6 +243,7 @@ target_sources(tests PRIVATE imemory_block_allocator.h.t.cpp index_of_type.h.t.cpp indirect_vector.h.t.cpp + infinite_loop.h.t.cpp initializer_list.h.t.cpp inplace_function.h.t.cpp instance_count.h.t.cpp diff --git a/test/syntax_check/infinite_loop.h.t.cpp b/test/syntax_check/infinite_loop.h.t.cpp new file mode 100644 index 00000000..aee39075 --- /dev/null +++ b/test/syntax_check/infinite_loop.h.t.cpp @@ -0,0 +1,29 @@ +/****************************************************************************** +The MIT License(MIT) + +Embedded Template Library. +https://github.com/ETLCPP/etl +https://www.etlcpp.com + +Copyright(c) 2026 BMW AG + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files(the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions : + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +******************************************************************************/ + +#include From 20cab3225691a9782f422ad1ad132f3f8ce5cf8a Mon Sep 17 00:00:00 2001 From: Roland Reichwein Date: Sat, 13 Jun 2026 11:00:39 +0200 Subject: [PATCH 2/2] Make ipriority_queue non-sized base class (#1459) * Fix iterator access algorithm.h Move from operator[] access to operator+ and operator- to prevent limited compatibility. This worked before for vector because of iterators being ptrdiff_t (signed), but not for unsigned access like size_t as in etl::queue. * Make ipriority_queue non-sized base class Fixes issue #1457 --------- Co-authored-by: John Wellbelove --- include/etl/algorithm.h | 28 +++++------ include/etl/priority_queue.h | 98 +++++++++++++++++++++++++++--------- test/etl_profile.h | 1 + test/test_priority_queue.cpp | 70 +++++++++++++++++++++++--- 4 files changed, 150 insertions(+), 47 deletions(-) diff --git a/include/etl/algorithm.h b/include/etl/algorithm.h index 91252319..57c58da2 100644 --- a/include/etl/algorithm.h +++ b/include/etl/algorithm.h @@ -880,14 +880,14 @@ namespace etl TDistance parent = (value_index - 1) / 2; #include "etl/private/diagnostic_array_bounds_push.h" - while ((value_index > top_index) && compare(first[parent], value)) + while ((value_index > top_index) && compare(*(first + parent), value)) { - first[value_index] = ETL_MOVE(first[parent]); - value_index = parent; - parent = (value_index - 1) / 2; + *(first + value_index) = ETL_MOVE(*(first + parent)); + value_index = parent; + parent = (value_index - 1) / 2; } - first[value_index] = ETL_MOVE(value); + *(first + value_index) = ETL_MOVE(value); #include "etl/private/diagnostic_pop.h" } @@ -900,20 +900,20 @@ namespace etl while (child2nd < length) { - if (compare(first[child2nd], first[child2nd - 1])) + if (compare(*(first + child2nd), *(first + (child2nd - 1)))) { --child2nd; } - first[value_index] = ETL_MOVE(first[child2nd]); - value_index = child2nd; - child2nd = 2 * (child2nd + 1); + *(first + value_index) = ETL_MOVE(*(first + child2nd)); + value_index = child2nd; + child2nd = 2 * (child2nd + 1); } if (child2nd == length) { - first[value_index] = ETL_MOVE(first[child2nd - 1]); - value_index = child2nd - 1; + *(first + value_index) = ETL_MOVE(*(first + (child2nd - 1))); + value_index = child2nd - 1; } push_heap(first, value_index, top_index, ETL_MOVE(value), compare); @@ -927,7 +927,7 @@ namespace etl for (TDistance child = 1; child < n; ++child) { - if (compare(first[parent], first[child])) + if (compare(*(first + parent), *(first + child))) { return false; } @@ -949,8 +949,8 @@ namespace etl typedef typename etl::iterator_traits::value_type value_t; typedef typename etl::iterator_traits::difference_type distance_t; - value_t value = ETL_MOVE(last[-1]); - last[-1] = ETL_MOVE(first[0]); + value_t value = ETL_MOVE(*(last - 1)); + *(last - 1) = ETL_MOVE(*first); private_heap::adjust_heap(first, distance_t(0), distance_t(last - first - 1), ETL_MOVE(value), compare); } diff --git a/include/etl/priority_queue.h b/include/etl/priority_queue.h index 2623fc07..ea9df835 100644 --- a/include/etl/priority_queue.h +++ b/include/etl/priority_queue.h @@ -33,11 +33,13 @@ SOFTWARE. #include "platform.h" #include "algorithm.h" +#include "deque.h" #include "error_handler.h" #include "exception.h" #include "functional.h" #include "iterator.h" #include "parameter_type.h" +#include "static_assert.h" #include "type_traits.h" #include "utility.h" #include "vector.h" @@ -109,13 +111,33 @@ namespace etl } }; + //*************************************************************************** + /// Trait to deduce the non-sized interface type of a container. + /// Specialize for containers that can be used with ipriority_queue. + //*************************************************************************** + template + struct priority_queue_container_interface + { + ETL_STATIC_ASSERT(sizeof(TContainer) == 0, "Unsupported container type for etl::ipriority_queue"); + }; + + template + struct priority_queue_container_interface > + { + typedef etl::ivector type; + }; + + template + struct priority_queue_container_interface > + { + typedef etl::ideque type; + }; + //*************************************************************************** ///\ingroup queue ///\brief This is the base for all priority queues that contain a particular /// type. \details Normally a reference to this type will be taken from a /// derived queue. - /// The TContainer specified must provide the front, push_back, pop_back, and - /// assign methods to work correctly with priority_queue. ///\code /// etl::priority_queue myPriorityQueue; /// etl::ipriority_queue& iQueue = myPriorityQueue; @@ -123,24 +145,24 @@ namespace etl /// \warning This priority queue cannot be used for concurrent access from /// multiple threads. /// \tparam T The type of value that the queue holds. - /// \tparam TContainer to hold the T queue values + /// \tparam TContainerBase The non-sized base class of the underlying container (e.g. etl::ivector) /// \tparam TCompare to use in comparing T values //*************************************************************************** - template > + template , typename TCompare = etl::less > class ipriority_queue { public: - typedef T value_type; ///< The type stored in the queue. - typedef TContainer container_type; ///< The container type used for priority queue. - typedef TCompare compare_type; ///< The comparison type. - typedef T& reference; ///< A reference to the type used in the queue. - typedef const T& const_reference; ///< A const reference to the type used in the queue. + typedef T value_type; ///< The type stored in the queue. + typedef TCompare compare_type; ///< The comparison type. + typedef TContainerBase container_base_type; ///< The non-sized container base type. + typedef T& reference; ///< A reference to the type used in the queue. + typedef const T& const_reference; ///< A const reference to the type used in the queue. #if ETL_USING_CPP11 typedef T&& rvalue_reference; ///< An rvalue reference to the type used in the queue. #endif - typedef typename TContainer::size_type size_type; ///< The type used for determining the size of the queue. - typedef typename etl::iterator_traits< typename TContainer::iterator>::difference_type difference_type; + typedef typename TContainerBase::size_type size_type; ///< The type used for determining the size of the queue. + typedef typename TContainerBase::difference_type difference_type; //************************************************************************* /// Gets a reference to the highest priority value in the priority queue. @@ -432,6 +454,21 @@ namespace etl } #endif + //************************************************************************* + /// Destructor. + //************************************************************************* +#if defined(ETL_POLYMORPHIC_PRIORITY_QUEUE) || defined(ETL_POLYMORPHIC_CONTAINERS) + + public: + + virtual ~ipriority_queue() {} +#else + + protected: + + ~ipriority_queue() {} +#endif + protected: //************************************************************************* @@ -461,7 +498,10 @@ namespace etl //************************************************************************* /// The constructor that is called from derived classes. //************************************************************************* - ipriority_queue() {} + ipriority_queue(TContainerBase& container_) + : container(container_) + { + } private: @@ -469,7 +509,7 @@ namespace etl ipriority_queue(const ipriority_queue&); /// The container specified at instantiation of the priority_queue - TContainer container; + TContainerBase& container; TCompare compare; }; @@ -483,8 +523,12 @@ namespace etl //*************************************************************************** template , typename TCompare = etl::less > - class priority_queue : public etl::ipriority_queue + class priority_queue : public etl::ipriority_queue::type, TCompare> { + private: + + typedef etl::ipriority_queue::type, TCompare> base_t; + public: typedef typename TContainer::size_type size_type; @@ -496,7 +540,7 @@ namespace etl /// Default constructor. //************************************************************************* priority_queue() - : etl::ipriority_queue() + : base_t(container) { } @@ -504,9 +548,9 @@ namespace etl /// Copy constructor //************************************************************************* priority_queue(const priority_queue& rhs) - : etl::ipriority_queue() + : base_t(container) { - etl::ipriority_queue::clone(rhs); + base_t::clone(rhs); } #if ETL_USING_CPP11 @@ -514,9 +558,9 @@ namespace etl /// Move constructor //************************************************************************* priority_queue(priority_queue&& rhs) - : etl::ipriority_queue() + : base_t(container) { - etl::ipriority_queue::move(etl::move(rhs)); + base_t::move(etl::move(rhs)); } #endif @@ -528,9 +572,9 @@ namespace etl //************************************************************************* template priority_queue(TIterator first, TIterator last) - : etl::ipriority_queue() + : base_t(container) { - etl::ipriority_queue::assign(first, last); + base_t::assign(first, last); } //************************************************************************* @@ -538,7 +582,7 @@ namespace etl //************************************************************************* ~priority_queue() { - etl::ipriority_queue::clear(); + base_t::clear(); } //************************************************************************* @@ -548,7 +592,7 @@ namespace etl { if (&rhs != this) { - etl::ipriority_queue::clone(rhs); + base_t::clone(rhs); } return *this; @@ -562,13 +606,17 @@ namespace etl { if (&rhs != this) { - etl::ipriority_queue::clear(); - etl::ipriority_queue::move(etl::move(rhs)); + base_t::clear(); + base_t::move(etl::move(rhs)); } return *this; } #endif + + private: + + TContainer container; }; template diff --git a/test/etl_profile.h b/test/etl_profile.h index 6c908cf4..83ddcd7c 100644 --- a/test/etl_profile.h +++ b/test/etl_profile.h @@ -67,6 +67,7 @@ SOFTWARE. #define ETL_POLYMORPHIC_SET #define ETL_POLYMORPHIC_MULTISET #define ETL_POLYMORPHIC_QUEUE +#define ETL_POLYMORPHIC_PRIORITY_QUEUE #define ETL_POLYMORPHIC_STACK #define ETL_POLYMORPHIC_REFERENCE_FLAT_MAP #define ETL_POLYMORPHIC_REFERENCE_FLAT_MULTIMAP diff --git a/test/test_priority_queue.cpp b/test/test_priority_queue.cpp index bf5988bc..7de04c5e 100644 --- a/test/test_priority_queue.cpp +++ b/test/test_priority_queue.cpp @@ -101,7 +101,7 @@ namespace priority_queue_t* ppriority_queue = new etl::priority_queue; - etl::ipriority_queue* pipriority_queue = ppriority_queue; + etl::ipriority_queue* pipriority_queue = ppriority_queue; pipriority_queue->push(1); pipriority_queue->push(2); @@ -577,8 +577,8 @@ namespace etl::priority_queue priority_queue2; - etl::ipriority_queue>& ipriority_queue1 = priority_queue1; - etl::ipriority_queue>& ipriority_queue2 = priority_queue2; + etl::ipriority_queue& ipriority_queue1 = priority_queue1; + etl::ipriority_queue& ipriority_queue2 = priority_queue2; ipriority_queue2 = ipriority_queue1; @@ -621,9 +621,9 @@ namespace //************************************************************************* TEST(test_interface) { - typedef etl::priority_queue priority_queue_t; - priority_queue_t priority_queue; - etl::ipriority_queue& ipriority_queue = priority_queue; + typedef etl::priority_queue priority_queue_t; + priority_queue_t priority_queue; + etl::ipriority_queue& ipriority_queue = priority_queue; std::priority_queue compare_priority_queue; @@ -654,8 +654,8 @@ namespace etl::priority_queue, std::less > priority_queue2; - etl::ipriority_queue, std::less>& ipriority_queue1 = priority_queue1; - etl::ipriority_queue, std::less>& ipriority_queue2 = priority_queue2; + etl::ipriority_queue, std::less >& ipriority_queue1 = priority_queue1; + etl::ipriority_queue, std::less >& ipriority_queue2 = priority_queue2; ipriority_queue2 = ipriority_queue1; @@ -668,5 +668,59 @@ namespace priority_queue2.pop(); } } + + //************************************************************************* + TEST(test_deque_container) + { + etl::priority_queue > priority_queue; + + CHECK_EQUAL(priority_queue.size(), 0UL); + CHECK_EQUAL(priority_queue.available(), SIZE); + CHECK_EQUAL(priority_queue.max_size(), SIZE); + + priority_queue.push(3); + priority_queue.push(1); + priority_queue.push(4); + priority_queue.push(2); + + CHECK_EQUAL(4UL, priority_queue.size()); + CHECK(priority_queue.full()); + CHECK_EQUAL(4, priority_queue.top()); + + priority_queue.pop(); + CHECK_EQUAL(3, priority_queue.top()); + + priority_queue.pop(); + CHECK_EQUAL(2, priority_queue.top()); + + priority_queue.pop(); + CHECK_EQUAL(1, priority_queue.top()); + + priority_queue.pop(); + CHECK(priority_queue.empty()); + + // Test via ipriority_queue interface + etl::priority_queue > pq1; + pq1.push(10); + pq1.push(30); + pq1.push(20); + + etl::ipriority_queue >& ipq = pq1; + CHECK_EQUAL(3UL, ipq.size()); + CHECK_EQUAL(30, ipq.top()); + + etl::priority_queue > pq2; + etl::ipriority_queue >& ipq2 = pq2; + ipq2 = ipq; + + CHECK_EQUAL(pq1.size(), pq2.size()); + + while (!pq1.empty()) + { + CHECK_EQUAL(pq1.top(), pq2.top()); + pq1.pop(); + pq2.pop(); + } + } } } // namespace