Merge branch 'arrstr-from-vector'

Improve interoperability between wxArrayString and std::vector.

See #23434, #23036.
This commit is contained in:
Vadim Zeitlin 2023-04-10 16:20:16 +02:00
commit 58a7b7b31b
6 changed files with 153 additions and 70 deletions

View file

@ -15,9 +15,6 @@
#include "wx/string.h"
#include "wx/dynarray.h"
#include <vector>
#include <initializer_list>
// these functions are only used in STL build now but we define them in any
// case for compatibility with the existing code outside of the library which
// could be using them
@ -68,10 +65,8 @@ inline int wxCMPFUNC_CONV wxNaturalStringSortDescending(const wxString& s1, cons
#if wxUSE_STD_CONTAINERS
typedef int (wxCMPFUNC_CONV *CMPFUNCwxString)(wxString*, wxString*);
WX_DEFINE_USER_EXPORTED_TYPEARRAY(wxString, wxArrayStringBase,
wxARRAY_DUMMY_BASE, WXDLLIMPEXP_BASE);
class WXDLLIMPEXP_BASE wxArrayString : public wxArrayStringBase
class WXDLLIMPEXP_BASE wxArrayString : public wxBaseArray<wxString>
{
public:
// type of function used by wxArrayString::Sort()
@ -83,19 +78,25 @@ public:
wxArrayString(size_t sz, const wchar_t** a);
wxArrayString(size_t sz, const wxString* a);
template<typename U>
wxArrayString(std::initializer_list<U> list) : wxArrayStringBase(list) { }
wxArrayString(std::initializer_list<U> list) : wxBaseArray<wxString>(list) { }
wxArrayString(const std::vector<wxString>& vec) : wxBaseArray<wxString>(vec) { }
wxArrayString(std::vector<wxString>&& vec) : wxBaseArray<wxString>(std::move(vec)) { }
template<typename U>
wxArrayString(const std::vector<U>& vec) : wxBaseArray<wxString>(vec.begin(), vec.end()) { }
int Index(const wxString& str, bool bCase = true, bool bFromEnd = false) const;
void Sort(bool reverseOrder = false);
void Sort(CompareFunction function);
void Sort(CMPFUNCwxString function) { wxArrayStringBase::Sort(function); }
void Sort(CMPFUNCwxString function) { wxBaseArray<wxString>::Sort(function); }
size_t Add(const wxString& string, size_t copies = 1)
{
wxArrayStringBase::Add(string, copies);
wxBaseArray<wxString>::Add(string, copies);
return size() - copies;
}
const std::vector<wxString>& AsVector() const { return *this; }
};
// Unlike all the other sorted arrays, this one uses a comparison function
@ -168,7 +169,7 @@ public:
// constructors and destructor
// default ctor
wxArrayString() { Init(false); }
wxArrayString() = default;
// if autoSort is true, the array is always sorted (in alphabetical order)
//
// NB: the reason for using int and not bool is that like this we can avoid
@ -177,7 +178,11 @@ public:
// wouldn't be needed if the 'explicit' keyword was supported by all
// compilers, or if this was protected ctor for wxSortedArrayString,
// but we're stuck with it now.
explicit wxArrayString(int autoSort) { Init(autoSort != 0); }
explicit wxArrayString(int autoSort)
{
if ( autoSort )
m_autoSort = true;
}
// C string array ctor
wxArrayString(size_t sz, const char** a);
wxArrayString(size_t sz, const wchar_t** a);
@ -187,7 +192,10 @@ public:
wxArrayString(const wxArrayString& array);
// list constructor
template<typename U>
wxArrayString(std::initializer_list<U> list) { Init(false); assign(list.begin(), list.end()); }
wxArrayString(std::initializer_list<U> list) { assign(list.begin(), list.end()); }
// ctor from a std::vector
template<typename U>
wxArrayString(const std::vector<U>& vec) { assign(vec.begin(), vec.end()); }
// assignment operator
wxArrayString& operator=(const wxArrayString& src);
// not virtual, this class should not be derived from
@ -234,6 +242,8 @@ public:
}
const wxString& Last() const { return const_cast<wxArrayString*>(this)->Last(); }
// get all items as a vector
std::vector<wxString> AsVector() const;
// item management
// Search the element in the array, starting from the beginning if
@ -333,8 +343,8 @@ public:
};
wxArrayString(const_iterator first, const_iterator last)
{ Init(false); assign(first, last); }
wxArrayString(size_type n, const_reference v) { Init(false); assign(n, v); }
{ assign(first, last); }
wxArrayString(size_type n, const_reference v) { assign(n, v); }
template <class Iterator>
void assign(Iterator first, Iterator last)
@ -391,10 +401,9 @@ public:
}
protected:
void Init(bool autoSort); // common part of all ctors
void Copy(const wxArrayString& src); // copies the contents of another array
CompareFunction m_compareFunction; // set only from wxSortedArrayString
CompareFunction m_compareFunction = nullptr; // set only from wxSortedArrayString
private:
// Allocate the new buffer big enough to hold m_nCount + nIncrement items and
@ -407,12 +416,12 @@ private:
// the string should be inserted and if it's false return wxNOT_FOUND.
size_t BinarySearch(const wxString& str, bool lowerBound) const;
size_t m_nSize, // current size of the array
m_nCount; // current number of elements
size_t m_nSize = 0, // current size of the array
m_nCount = 0; // current number of elements
wxString *m_pItems; // pointer to data
wxString *m_pItems = nullptr; // pointer to data
bool m_autoSort; // if true, keep the array always sorted
bool m_autoSort = false; // if true, keep the array always sorted
};
class WXDLLIMPEXP_BASE wxSortedArrayString : public wxArrayString

View file

@ -110,6 +110,9 @@ public:
template<typename U>
wxBaseArray(std::initializer_list<U> list) : base_vec(list.begin(), list.end()) {}
wxBaseArray(const std::vector<T>& vec) : base_vec(vec) { }
wxBaseArray(std::vector<T>&& vec) : base_vec(std::move(vec)) { }
void Empty() { this->clear(); }
void Clear() { this->clear(); }
void Alloc(size_t uiSize) { this->reserve(uiSize); }

View file

@ -8,42 +8,54 @@
/**
@class wxArrayString
wxArrayString is an efficient container for storing wxString objects.
wxArrayString is a legacy class similar to std::vector<wxString>.
It has the same features as all wxArray classes, i.e. it dynamically expands
when new items are added to it (so it is as easy to use as a linked list),
but the access time to the elements is constant, instead of being linear in
number of elements as in the case of linked lists. It is also very size
efficient and doesn't take more space than a C array @e wxString[] type
(wxArrayString uses its knowledge of internals of wxString class to achieve this).
This class is used in the same way as other dynamic arrays(), except that no
::WX_DEFINE_ARRAY declaration is needed for it.
When a string is added or inserted in the array, a copy of the string is created,
so the original string may be safely deleted (e.g. if it was a @e wxChar *
pointer the memory it was using can be freed immediately after this).
In general, there is no need to worry about string memory deallocation when using
this class - it will always free the memory it uses itself.
The references returned by wxArrayString::Item, wxArrayString::Last or
wxArrayString::operator[] are not constant, so the array elements may
be modified in place like this:
This class shouldn't normally be used in the new code, but is still needed
when passing multiple items to various functions in wxWidgets API, notably
the constructors of various GUI control classes. Usually, even in this case
it doesn't need to be used explicitly, as wxArrayString will be implicitly
created if you use either an initializer list or a vector of strings, e.g.
you can just pass either of those instead of wxArrayString, for example
@code
array.Last().MakeUpper();
// wxListBox ctor is documented as taking wxArrayString but you can
// pass an initializer_list to it directly:
auto listbox = new wxListBox(parent, wxID_ANY,
wxDefaultPosition, wxDefaultSize,
{ "some", "items", "for", "the", "listbox" });
// Similarly, if you already have a vector filled with strings
// elsewhere in your program, you can just pass it instead:
std::vector<std::string> countries = GetListOfCountries();
auto choices = new wxChoice(parent, wxID_ANY,
wxDefaultPosition, wxDefaultSize,
countries);
@endcode
@note None of the methods of wxArrayString is virtual including its
destructor, so this class should not be used as a base class.
When using a wxWidgets function returning an object of this class, you can
either use it as if it were a `std::vector<wxString>`, as this class has
all vector methods, or actually convert it to such vector using its
AsVector(), e.g.
Although this is not true strictly speaking, this class may be considered as
a specialization of wxArray class for the wxString member data: it is not
implemented like this, but it does have all of the wxArray functions.
@code
wxArrayString files;
wxDir::GetAllFiles("/some/path", &files);
It also has the full set of <tt>std::vector<wxString></tt> compatible
methods, including nested @c iterator and @c const_iterator classes which
should be used in the new code for forward compatibility with the future
wxWidgets versions.
// Can use the usual accessors:
if ( !files.empty() ) {
auto first = files[0];
auto total = files.size();
...
}
// Can iterate over it like over a vector, too.
for ( const wxString& file: files ) {
...
}
// Or can just convert it to the "real" vector:
const std::vector<wxString>& vec = files.AsVector();
@endcode
@library{wxbase}
@category{containers}
@ -97,6 +109,30 @@ public:
template<typename T>
wxArrayString(std::initializer_list<T> list);
/**
Constructs the container with the contents of the vector @a vec.
Template parameter `T` must be convertible to wxString, i.e. it can be
wxString itself or `std::string`, `std::wstring` etc.
@since 3.3.0
*/
template<typename T>
wxArrayString(const std::vector<T>& vec);
/**
Constructs the container with the contents of the vector @a vec.
When using @ref overview_container_std, this constructor is more
efficient than the overload taking const reference to the vector.
Otherwise it is identical to the other overload, see its documentation
for more details.
@since 3.3.0
*/
template<typename T>
wxArrayString(std::vector<T>&& vec);
/**
Destructor frees memory occupied by the array strings. For performance
reasons it is not virtual, so this class should not be derived from.
@ -119,6 +155,25 @@ public:
*/
void Alloc(size_t nCount);
/**
Constructs a std::vector containing the same strings as this array.
In @ref overview_container_std, this function actually returns a const
reference to this object itself, without making a copy, but in the
default/compatible build, it has to copy all the strings, making it
expensive to call for big arrays.
Note that using it like this:
@code
const std::vector<wxString>& vec = array.AsVector();
@endcode
works in all builds as long as you don't need to modify the returned
vector and doesn't impose any extra overhead in the STL build.
@since 3.3.0
*/
std::vector<wxString> AsVector() const;
/**
Clears the array contents and frees memory.

View file

@ -31,25 +31,16 @@
wxArrayString::wxArrayString(size_t sz, const char** a)
{
#if !wxUSE_STD_CONTAINERS
Init(false);
#endif
assign(a, a + sz);
}
wxArrayString::wxArrayString(size_t sz, const wchar_t** a)
{
#if !wxUSE_STD_CONTAINERS
Init(false);
#endif
assign(a, a + sz);
}
wxArrayString::wxArrayString(size_t sz, const wxString* a)
{
#if !wxUSE_STD_CONTAINERS
Init(false);
#endif
assign(a, a + sz);
}
@ -130,20 +121,11 @@ int wxSortedArrayString::Index(const wxString& str,
#define ARRAY_DEFAULT_INITIAL_SIZE (16)
#endif
// ctor
void wxArrayString::Init(bool autoSort)
{
m_nSize =
m_nCount = 0;
m_pItems = nullptr;
m_compareFunction = nullptr;
m_autoSort = autoSort;
}
// copy ctor
wxArrayString::wxArrayString(const wxArrayString& src)
{
Init(src.m_autoSort);
if ( src.m_autoSort )
m_autoSort = true;
*this = src;
}
@ -168,6 +150,14 @@ wxArrayString& wxArrayString::operator=(const wxArrayString& src)
return *this;
}
std::vector<wxString> wxArrayString::AsVector() const
{
if ( !m_pItems )
return {};
return std::vector<wxString>{m_pItems, m_pItems + m_nCount};
}
void wxArrayString::Copy(const wxArrayString& src)
{
if ( src.m_nCount > ARRAY_DEFAULT_INITIAL_SIZE )

View file

@ -355,6 +355,33 @@ TEST_CASE("wxArrayString", "[dynarray]")
CHECK( a9.size() == 5 );
}
TEST_CASE("wxArrayString::Vector", "[dynarray][vector]")
{
SECTION("wxString")
{
std::vector<wxString> vec{"first", "second"};
wxArrayString a(vec);
REQUIRE( a.size() == 2 );
CHECK( a[1] == "second" );
}
SECTION("string")
{
std::vector<std::string> vec{"third", "fourth"};
wxArrayString a(vec);
REQUIRE( a.size() == 2 );
CHECK( a[1] == "fourth" );
}
SECTION("AsVector")
{
wxArrayString a{"five", "six", "seven"};
const std::vector<wxString>& vec = a.AsVector();
REQUIRE( vec.size() == 3 );
CHECK( vec.at(2) == "seven" );
}
}
TEST_CASE("wxSortedArrayString", "[dynarray]")
{
wxSortedArrayString a;

View file

@ -79,7 +79,6 @@ void wxType::SetTypeFromString(const wxString& t)
m_strType.Replace(" ,", ",");
// ADHOC-FIX
m_strType.Replace("_wxArraywxArrayStringBase", "wxString");
m_strType.Replace("ExitCode", "void*"); // used in wxThread stuff
m_strType = m_strType.Strip(wxString::both);