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 <jwellbelove@users.noreply.github.com>
This commit is contained in:
Roland Reichwein 2026-06-13 11:00:39 +02:00 committed by GitHub
parent 6d9ed143b2
commit 20cab32256
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 150 additions and 47 deletions

View File

@ -880,14 +880,14 @@ namespace etl
TDistance parent = (value_index - 1) / 2; TDistance parent = (value_index - 1) / 2;
#include "etl/private/diagnostic_array_bounds_push.h" #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]); *(first + value_index) = ETL_MOVE(*(first + parent));
value_index = parent; value_index = parent;
parent = (value_index - 1) / 2; parent = (value_index - 1) / 2;
} }
first[value_index] = ETL_MOVE(value); *(first + value_index) = ETL_MOVE(value);
#include "etl/private/diagnostic_pop.h" #include "etl/private/diagnostic_pop.h"
} }
@ -900,19 +900,19 @@ namespace etl
while (child2nd < length) while (child2nd < length)
{ {
if (compare(first[child2nd], first[child2nd - 1])) if (compare(*(first + child2nd), *(first + (child2nd - 1))))
{ {
--child2nd; --child2nd;
} }
first[value_index] = ETL_MOVE(first[child2nd]); *(first + value_index) = ETL_MOVE(*(first + child2nd));
value_index = child2nd; value_index = child2nd;
child2nd = 2 * (child2nd + 1); child2nd = 2 * (child2nd + 1);
} }
if (child2nd == length) if (child2nd == length)
{ {
first[value_index] = ETL_MOVE(first[child2nd - 1]); *(first + value_index) = ETL_MOVE(*(first + (child2nd - 1)));
value_index = child2nd - 1; value_index = child2nd - 1;
} }
@ -927,7 +927,7 @@ namespace etl
for (TDistance child = 1; child < n; ++child) for (TDistance child = 1; child < n; ++child)
{ {
if (compare(first[parent], first[child])) if (compare(*(first + parent), *(first + child)))
{ {
return false; return false;
} }
@ -949,8 +949,8 @@ namespace etl
typedef typename etl::iterator_traits<TIterator>::value_type value_t; typedef typename etl::iterator_traits<TIterator>::value_type value_t;
typedef typename etl::iterator_traits<TIterator>::difference_type distance_t; typedef typename etl::iterator_traits<TIterator>::difference_type distance_t;
value_t value = ETL_MOVE(last[-1]); value_t value = ETL_MOVE(*(last - 1));
last[-1] = ETL_MOVE(first[0]); *(last - 1) = ETL_MOVE(*first);
private_heap::adjust_heap(first, distance_t(0), distance_t(last - first - 1), ETL_MOVE(value), compare); private_heap::adjust_heap(first, distance_t(0), distance_t(last - first - 1), ETL_MOVE(value), compare);
} }

View File

@ -33,11 +33,13 @@ SOFTWARE.
#include "platform.h" #include "platform.h"
#include "algorithm.h" #include "algorithm.h"
#include "deque.h"
#include "error_handler.h" #include "error_handler.h"
#include "exception.h" #include "exception.h"
#include "functional.h" #include "functional.h"
#include "iterator.h" #include "iterator.h"
#include "parameter_type.h" #include "parameter_type.h"
#include "static_assert.h"
#include "type_traits.h" #include "type_traits.h"
#include "utility.h" #include "utility.h"
#include "vector.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 <typename TContainer>
struct priority_queue_container_interface
{
ETL_STATIC_ASSERT(sizeof(TContainer) == 0, "Unsupported container type for etl::ipriority_queue");
};
template <typename T, const size_t N>
struct priority_queue_container_interface<etl::vector<T, N> >
{
typedef etl::ivector<T> type;
};
template <typename T, const size_t N>
struct priority_queue_container_interface<etl::deque<T, N> >
{
typedef etl::ideque<T> type;
};
//*************************************************************************** //***************************************************************************
///\ingroup queue ///\ingroup queue
///\brief This is the base for all priority queues that contain a particular ///\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 /// type. \details Normally a reference to this type will be taken from a
/// derived queue. /// derived queue.
/// The TContainer specified must provide the front, push_back, pop_back, and
/// assign methods to work correctly with priority_queue.
///\code ///\code
/// etl::priority_queue<int, 10> myPriorityQueue; /// etl::priority_queue<int, 10> myPriorityQueue;
/// etl::ipriority_queue<int>& iQueue = myPriorityQueue; /// etl::ipriority_queue<int>& iQueue = myPriorityQueue;
@ -123,24 +145,24 @@ namespace etl
/// \warning This priority queue cannot be used for concurrent access from /// \warning This priority queue cannot be used for concurrent access from
/// multiple threads. /// multiple threads.
/// \tparam T The type of value that the queue holds. /// \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<T>)
/// \tparam TCompare to use in comparing T values /// \tparam TCompare to use in comparing T values
//*************************************************************************** //***************************************************************************
template <typename T, typename TContainer, typename TCompare = etl::less<T> > template <typename T, typename TContainerBase = etl::ivector<T>, typename TCompare = etl::less<T> >
class ipriority_queue class ipriority_queue
{ {
public: public:
typedef T value_type; ///< The type stored in the queue. 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 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 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 const T& const_reference; ///< A const reference to the type used in the queue.
#if ETL_USING_CPP11 #if ETL_USING_CPP11
typedef T&& rvalue_reference; ///< An rvalue reference to the type used in the queue. typedef T&& rvalue_reference; ///< An rvalue reference to the type used in the queue.
#endif #endif
typedef typename TContainer::size_type size_type; ///< The type used for determining the size of the queue. typedef typename TContainerBase::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::difference_type difference_type;
//************************************************************************* //*************************************************************************
/// Gets a reference to the highest priority value in the priority queue. /// Gets a reference to the highest priority value in the priority queue.
@ -432,6 +454,21 @@ namespace etl
} }
#endif #endif
//*************************************************************************
/// Destructor.
//*************************************************************************
#if defined(ETL_POLYMORPHIC_PRIORITY_QUEUE) || defined(ETL_POLYMORPHIC_CONTAINERS)
public:
virtual ~ipriority_queue() {}
#else
protected:
~ipriority_queue() {}
#endif
protected: protected:
//************************************************************************* //*************************************************************************
@ -461,7 +498,10 @@ namespace etl
//************************************************************************* //*************************************************************************
/// The constructor that is called from derived classes. /// The constructor that is called from derived classes.
//************************************************************************* //*************************************************************************
ipriority_queue() {} ipriority_queue(TContainerBase& container_)
: container(container_)
{
}
private: private:
@ -469,7 +509,7 @@ namespace etl
ipriority_queue(const ipriority_queue&); ipriority_queue(const ipriority_queue&);
/// The container specified at instantiation of the priority_queue /// The container specified at instantiation of the priority_queue
TContainer container; TContainerBase& container;
TCompare compare; TCompare compare;
}; };
@ -483,8 +523,12 @@ namespace etl
//*************************************************************************** //***************************************************************************
template <typename T, const size_t SIZE, typename TContainer = etl::vector<T, SIZE>, template <typename T, const size_t SIZE, typename TContainer = etl::vector<T, SIZE>,
typename TCompare = etl::less<typename TContainer::value_type> > typename TCompare = etl::less<typename TContainer::value_type> >
class priority_queue : public etl::ipriority_queue<T, TContainer, TCompare> class priority_queue : public etl::ipriority_queue<T, typename etl::priority_queue_container_interface<TContainer>::type, TCompare>
{ {
private:
typedef etl::ipriority_queue<T, typename etl::priority_queue_container_interface<TContainer>::type, TCompare> base_t;
public: public:
typedef typename TContainer::size_type size_type; typedef typename TContainer::size_type size_type;
@ -496,7 +540,7 @@ namespace etl
/// Default constructor. /// Default constructor.
//************************************************************************* //*************************************************************************
priority_queue() priority_queue()
: etl::ipriority_queue<T, TContainer, TCompare>() : base_t(container)
{ {
} }
@ -504,9 +548,9 @@ namespace etl
/// Copy constructor /// Copy constructor
//************************************************************************* //*************************************************************************
priority_queue(const priority_queue& rhs) priority_queue(const priority_queue& rhs)
: etl::ipriority_queue<T, TContainer, TCompare>() : base_t(container)
{ {
etl::ipriority_queue<T, TContainer, TCompare>::clone(rhs); base_t::clone(rhs);
} }
#if ETL_USING_CPP11 #if ETL_USING_CPP11
@ -514,9 +558,9 @@ namespace etl
/// Move constructor /// Move constructor
//************************************************************************* //*************************************************************************
priority_queue(priority_queue&& rhs) priority_queue(priority_queue&& rhs)
: etl::ipriority_queue<T, TContainer, TCompare>() : base_t(container)
{ {
etl::ipriority_queue<T, TContainer, TCompare>::move(etl::move(rhs)); base_t::move(etl::move(rhs));
} }
#endif #endif
@ -528,9 +572,9 @@ namespace etl
//************************************************************************* //*************************************************************************
template <typename TIterator> template <typename TIterator>
priority_queue(TIterator first, TIterator last) priority_queue(TIterator first, TIterator last)
: etl::ipriority_queue<T, TContainer, TCompare>() : base_t(container)
{ {
etl::ipriority_queue<T, TContainer, TCompare>::assign(first, last); base_t::assign(first, last);
} }
//************************************************************************* //*************************************************************************
@ -538,7 +582,7 @@ namespace etl
//************************************************************************* //*************************************************************************
~priority_queue() ~priority_queue()
{ {
etl::ipriority_queue<T, TContainer, TCompare>::clear(); base_t::clear();
} }
//************************************************************************* //*************************************************************************
@ -548,7 +592,7 @@ namespace etl
{ {
if (&rhs != this) if (&rhs != this)
{ {
etl::ipriority_queue<T, TContainer, TCompare>::clone(rhs); base_t::clone(rhs);
} }
return *this; return *this;
@ -562,13 +606,17 @@ namespace etl
{ {
if (&rhs != this) if (&rhs != this)
{ {
etl::ipriority_queue<T, TContainer, TCompare>::clear(); base_t::clear();
etl::ipriority_queue<T, TContainer, TCompare>::move(etl::move(rhs)); base_t::move(etl::move(rhs));
} }
return *this; return *this;
} }
#endif #endif
private:
TContainer container;
}; };
template <typename T, const size_t SIZE, typename TContainer, typename TCompare> template <typename T, const size_t SIZE, typename TContainer, typename TCompare>

View File

@ -67,6 +67,7 @@ SOFTWARE.
#define ETL_POLYMORPHIC_SET #define ETL_POLYMORPHIC_SET
#define ETL_POLYMORPHIC_MULTISET #define ETL_POLYMORPHIC_MULTISET
#define ETL_POLYMORPHIC_QUEUE #define ETL_POLYMORPHIC_QUEUE
#define ETL_POLYMORPHIC_PRIORITY_QUEUE
#define ETL_POLYMORPHIC_STACK #define ETL_POLYMORPHIC_STACK
#define ETL_POLYMORPHIC_REFERENCE_FLAT_MAP #define ETL_POLYMORPHIC_REFERENCE_FLAT_MAP
#define ETL_POLYMORPHIC_REFERENCE_FLAT_MULTIMAP #define ETL_POLYMORPHIC_REFERENCE_FLAT_MULTIMAP

View File

@ -101,7 +101,7 @@ namespace
priority_queue_t* ppriority_queue = new etl::priority_queue<int, 4>; priority_queue_t* ppriority_queue = new etl::priority_queue<int, 4>;
etl::ipriority_queue<int, priority_queue_t::container_type, priority_queue_t::compare_type>* pipriority_queue = ppriority_queue; etl::ipriority_queue<int>* pipriority_queue = ppriority_queue;
pipriority_queue->push(1); pipriority_queue->push(1);
pipriority_queue->push(2); pipriority_queue->push(2);
@ -577,8 +577,8 @@ namespace
etl::priority_queue<int, SIZE> priority_queue2; etl::priority_queue<int, SIZE> priority_queue2;
etl::ipriority_queue<int, etl::vector<int, SIZE>>& ipriority_queue1 = priority_queue1; etl::ipriority_queue<int>& ipriority_queue1 = priority_queue1;
etl::ipriority_queue<int, etl::vector<int, SIZE>>& ipriority_queue2 = priority_queue2; etl::ipriority_queue<int>& ipriority_queue2 = priority_queue2;
ipriority_queue2 = ipriority_queue1; ipriority_queue2 = ipriority_queue1;
@ -623,7 +623,7 @@ namespace
{ {
typedef etl::priority_queue<int, SIZE> priority_queue_t; typedef etl::priority_queue<int, SIZE> priority_queue_t;
priority_queue_t priority_queue; priority_queue_t priority_queue;
etl::ipriority_queue<int, priority_queue_t::container_type, priority_queue_t::compare_type>& ipriority_queue = priority_queue; etl::ipriority_queue<int>& ipriority_queue = priority_queue;
std::priority_queue<int> compare_priority_queue; std::priority_queue<int> compare_priority_queue;
@ -654,8 +654,8 @@ namespace
etl::priority_queue<int, SIZE, etl::vector<int, SIZE>, std::less<int> > priority_queue2; etl::priority_queue<int, SIZE, etl::vector<int, SIZE>, std::less<int> > priority_queue2;
etl::ipriority_queue<int, etl::vector<int, SIZE>, std::less<int>>& ipriority_queue1 = priority_queue1; etl::ipriority_queue<int, etl::ivector<int>, std::less<int> >& ipriority_queue1 = priority_queue1;
etl::ipriority_queue<int, etl::vector<int, SIZE>, std::less<int>>& ipriority_queue2 = priority_queue2; etl::ipriority_queue<int, etl::ivector<int>, std::less<int> >& ipriority_queue2 = priority_queue2;
ipriority_queue2 = ipriority_queue1; ipriority_queue2 = ipriority_queue1;
@ -668,5 +668,59 @@ namespace
priority_queue2.pop(); priority_queue2.pop();
} }
} }
//*************************************************************************
TEST(test_deque_container)
{
etl::priority_queue<int, SIZE, etl::deque<int, SIZE> > 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<int, SIZE, etl::deque<int, SIZE> > pq1;
pq1.push(10);
pq1.push(30);
pq1.push(20);
etl::ipriority_queue<int, etl::ideque<int> >& ipq = pq1;
CHECK_EQUAL(3UL, ipq.size());
CHECK_EQUAL(30, ipq.top());
etl::priority_queue<int, SIZE, etl::deque<int, SIZE> > pq2;
etl::ipriority_queue<int, etl::ideque<int> >& ipq2 = pq2;
ipq2 = ipq;
CHECK_EQUAL(pq1.size(), pq2.size());
while (!pq1.empty())
{
CHECK_EQUAL(pq1.top(), pq2.top());
pq1.pop();
pq2.pop();
}
}
} }
} // namespace } // namespace