Merge branch 'vector-enhancements'

Make wxVector<> more compatible with std::vector<>.
This commit is contained in:
Vadim Zeitlin
2017-11-21 13:50:26 +01:00
3 changed files with 208 additions and 19 deletions

View File

@@ -113,6 +113,34 @@ struct wxVectorMemOpsGeneric
}
};
// We need to distinguish integers from iterators in assign() overloads and the
// simplest way to do it would be by using std::iterator_traits<>, however this
// might break existing code using custom iterator classes but not specializing
// iterator_traits<> for them, so we approach the problem from the other end
// and use our own traits that we specialize for all integer types.
struct IsIntType {};
struct IsNotIntType {};
template <typename T> struct IsInt : IsNotIntType {};
#define WX_DECLARE_TYPE_IS_INT(type) \
template <> struct IsInt<type> : IsIntType {}
WX_DECLARE_TYPE_IS_INT(unsigned char);
WX_DECLARE_TYPE_IS_INT(signed char);
WX_DECLARE_TYPE_IS_INT(unsigned short int);
WX_DECLARE_TYPE_IS_INT(signed short int);
WX_DECLARE_TYPE_IS_INT(unsigned int);
WX_DECLARE_TYPE_IS_INT(signed int);
WX_DECLARE_TYPE_IS_INT(unsigned long int);
WX_DECLARE_TYPE_IS_INT(signed long int);
#ifdef wxLongLong_t
WX_DECLARE_TYPE_IS_INT(wxLongLong_t);
WX_DECLARE_TYPE_IS_INT(wxULongLong_t);
#endif
#undef WX_DECLARE_TYPE_IS_INT
} // namespace wxPrivate
@@ -170,6 +198,8 @@ public:
{ return reverse_iterator(m_ptr + n); }
reverse_iterator& operator-=(difference_type n)
{ m_ptr += n; return *this; }
difference_type operator-(const reverse_iterator& it) const
{ return it.m_ptr - m_ptr; }
reference operator[](difference_type n) const
{ return *(*this + n); }
@@ -215,6 +245,8 @@ public:
{ return const_reverse_iterator(m_ptr + n); }
const_reverse_iterator& operator-=(difference_type n)
{ m_ptr += n; return *this; }
difference_type operator-(const const_reverse_iterator& it) const
{ return it.m_ptr - m_ptr; }
const_reference operator[](difference_type n) const
{ return *(*this + n); }
@@ -265,23 +297,13 @@ public:
void assign(size_type p_size, const value_type& v)
{
clear();
reserve(p_size);
for ( size_t n = 0; n < p_size; n++ )
push_back(v);
AssignFromValue(p_size, v);
}
template <class InputIterator>
template <typename InputIterator>
void assign(InputIterator first, InputIterator last)
{
clear();
// Notice that it would be nice to call reserve() here but we can't do
// it for arbitrary input iterators, we should have a dispatch on
// iterator type and call it if possible.
for ( InputIterator it = first; it != last; ++it )
push_back(*it);
AssignDispatch(first, last, typename wxPrivate::IsInt<InputIterator>());
}
void swap(wxVector& v)
@@ -351,6 +373,12 @@ public:
return m_capacity;
}
void shrink_to_fit()
{
m_values = Ops::Realloc(m_values, m_size, m_size);
m_capacity = m_size;
}
bool empty() const
{
return size() == 0;
@@ -366,6 +394,25 @@ public:
return *this;
}
bool operator==(const wxVector& vb) const
{
if ( vb.m_size != m_size )
return false;
for ( size_type i = 0; i < m_size; i++ )
{
if ( vb.m_values[i] != m_values[i] )
return false;
}
return true;
}
bool operator!=(const wxVector& vb) const
{
return !(*this == vb);
}
void push_back(const value_type& v)
{
reserve(size() + 1);
@@ -417,14 +464,14 @@ public:
const_reverse_iterator rbegin() const { return const_reverse_iterator(end() - 1); }
const_reverse_iterator rend() const { return const_reverse_iterator(begin() - 1); }
iterator insert(iterator it, const value_type& v = value_type())
iterator insert(iterator it, size_type count, const value_type& v)
{
// NB: this must be done before reserve(), because reserve()
// invalidates iterators!
const size_t idx = it - begin();
const size_t after = end() - it;
reserve(size() + 1);
reserve(size() + count);
// the place where the new element is going to be inserted
value_type * const place = m_values + idx;
@@ -432,27 +479,33 @@ public:
// unless we're inserting at the end, move following elements out of
// the way:
if ( after > 0 )
Ops::MemmoveForward(place + 1, place, after);
Ops::MemmoveForward(place + count, place, after);
// if the ctor called below throws an exception, we need to move all
// the elements back to their original positions in m_values
wxScopeGuard moveBack = wxMakeGuard(
Ops::MemmoveBackward, place, place + 1, after);
Ops::MemmoveBackward, place, place + count, after);
if ( !after )
moveBack.Dismiss();
// use placement new to initialize new object in preallocated place in
// m_values and store 'v' in it:
::new(place) value_type(v);
for ( size_type i = 0; i < count; i++ )
::new(place + i) value_type(v);
// now that we did successfully add the new element, increment the size
// and disable moving the items back
moveBack.Dismiss();
m_size++;
m_size += count;
return begin() + idx;
}
iterator insert(iterator it, const value_type& v = value_type())
{
return insert(it, 1, v);
}
iterator erase(iterator it)
{
return erase(it, it + 1);
@@ -513,6 +566,36 @@ private:
push_back(v);
}
void AssignFromValue(size_type p_size, const value_type& v)
{
clear();
reserve(p_size);
for ( size_t n = 0; n < p_size; n++ )
push_back(v);
}
template <typename InputIterator>
void AssignDispatch(InputIterator first, InputIterator last,
wxPrivate::IsIntType)
{
AssignFromValue(static_cast<size_type>(first),
static_cast<const value_type&>(last));
}
template <typename InputIterator>
void AssignDispatch(InputIterator first, InputIterator last,
wxPrivate::IsNotIntType)
{
clear();
// Notice that it would be nice to call reserve() here but we can't do
// it for arbitrary input iterators, we should have a dispatch on
// iterator type and call it if possible.
for ( InputIterator it = first; it != last; ++it )
push_back(*it);
}
size_type m_size,
m_capacity;
value_type *m_values;

View File

@@ -194,11 +194,34 @@ public:
*/
iterator insert(iterator it, const value_type& v = value_type());
/**
Insert the given number of copies of @a v at the given position.
@return Iterator for the first inserted item.
@since 3.1.1
*/
iterator insert(iterator it, size_type count, const value_type& v);
/**
Assignment operator.
*/
wxVector& operator=(const wxVector& vb);
/**
Equality operator.
@since 3.1.1
*/
wxVector& operator==(const wxVector& vb) const;
/**
Inequality operator.
@since 3.1.1
*/
wxVector& operator!=(const wxVector& vb) const;
/**
Returns item at position @a idx.
*/
@@ -239,6 +262,18 @@ public:
void resize(size_type n, const value_type& v);
//@}
/**
Free unused memory allocated by the vector.
Reduces the memory used by the vector to the bare minimum required to
hold its current number of elements, possibly 0.
After calling this method, capacity() returns the same as size().
@since 3.1.1
*/
void shrink_to_fit();
/**
Returns the size of the vector.
*/

View File

@@ -162,6 +162,16 @@ void VectorsTestCase::Insert()
CPPUNIT_ASSERT( v[1] == 'a' );
CPPUNIT_ASSERT( v[2] == 'X' );
CPPUNIT_ASSERT( v[3] == 'b' );
v.insert(v.begin() + 3, 3, 'Z');
REQUIRE( v.size() == 7 );
CHECK( v[0] == '0' );
CHECK( v[1] == 'a' );
CHECK( v[2] == 'X' );
CHECK( v[3] == 'Z' );
CHECK( v[4] == 'Z' );
CHECK( v[5] == 'Z' );
CHECK( v[6] == 'b' );
}
void VectorsTestCase::Erase()
@@ -314,3 +324,64 @@ void VectorsTestCase::Sort()
CPPUNIT_ASSERT( v[idx-1] <= v[idx] );
}
}
TEST_CASE("wxVector::operator==", "[vector][compare]")
{
wxVector<wxString> v1, v2;
CHECK( v1 == v2 );
CHECK( !(v1 != v2) );
v1.push_back("foo");
CHECK( v1 != v2 );
v2.push_back("foo");
CHECK( v1 == v2 );
v1.push_back("bar");
v2.push_back("baz");
CHECK( v1 != v2 );
}
TEST_CASE("wxVector::reverse_iterator", "[vector][reverse_iterator]")
{
wxVector<int> v;
for ( int i = 0; i < 10; ++i )
v.push_back(i + 1);
const wxVector<int>::reverse_iterator rb = v.rbegin();
const wxVector<int>::reverse_iterator re = v.rend();
CHECK( re - rb == 10 );
wxVector<int>::reverse_iterator ri = rb;
++ri;
CHECK( ri - rb == 1 );
CHECK( re - ri == 9 );
ri = rb + 2;
CHECK( ri - rb == 2 );
CHECK( re - ri == 8 );
}
TEST_CASE("wxVector::capacity", "[vector][capacity][shrink_to_fit]")
{
wxVector<int> v;
CHECK( v.capacity() == 0 );
v.push_back(0);
// When using the standard library vector, we don't know what growth
// strategy it uses, so we can't rely on this check passing, but with our
// own one we can, allowing us to check that shrink_to_fit() really shrinks
// the capacity below.
#if !wxUSE_STD_CONTAINERS
CHECK( v.capacity() > 1 );
#endif
v.shrink_to_fit();
CHECK( v.capacity() == 1 );
v.erase(v.begin());
CHECK( v.capacity() == 1 );
v.shrink_to_fit();
CHECK( v.capacity() == 0 );
}