Add move ctor and assignment operator to wxString

This results in noticeable speed up in wxArrayString operations, as
shown by the new benchmark.

See #23224.
This commit is contained in:
Pavel Tyunin 2022-12-01 17:06:24 +02:00 committed by Vadim Zeitlin
parent 2922fa0fa2
commit 74512dbc57
6 changed files with 255 additions and 3 deletions

View File

@ -235,6 +235,10 @@ Changes in behaviour which may result in build errors
3.2.3: (released 2023-??-??)
----------------------------
All:
- Add move ctor and assignment to wxString (Pavel Tyunin, #23224).
3.2.2: (released 2023-02-08)
----------------------------

View File

@ -304,6 +304,10 @@ typedef short int WXTYPE;
#if __cplusplus >= 201103L || wxCHECK_VISUALC_VERSION(14)
#define wxHAS_MEMBER_DEFAULT
// Rvalue references are supported since MSVS 2010, but enabling them
// causes compilation errors on versions before 2015
#define wxHAS_RVALUE_REF
#define wxHAS_NOEXCEPT
#define wxNOEXCEPT noexcept
#else

View File

@ -1140,12 +1140,25 @@ private:
static wxString FromImpl(const wxStringImpl& src)
{ return wxString((CtorFromStringImplTag*)NULL, src); }
#ifdef wxHAS_RVALUE_REF
wxString(CtorFromStringImplTag* WXUNUSED(dummy), wxStringImpl&& src) wxNOEXCEPT
: m_impl(std::move(src)) {}
static wxString FromImpl(wxStringImpl&& src) wxNOEXCEPT
{
return wxString((CtorFromStringImplTag*)NULL, std::move(src));
}
#endif
#else
#if !wxUSE_STL_BASED_WXSTRING
wxString(const wxStringImpl& src) : m_impl(src) { }
// else: already defined as wxString(wxStdString) below
#endif
static wxString FromImpl(const wxStringImpl& src) { return wxString(src); }
#ifdef wxHAS_RVALUE_REF
static wxString FromImpl(wxStringImpl&& src) wxNOEXCEPT { return wxString(std::move(src)); }
#endif
#endif
public:
@ -1156,6 +1169,16 @@ public:
// copy ctor
wxString(const wxString& stringSrc) : m_impl(stringSrc.m_impl) { }
#ifdef wxHAS_RVALUE_REF
// move ctor
wxString(wxString&& stringSrc) wxNOEXCEPT : m_impl(std::move(stringSrc.m_impl))
{
#if wxUSE_STRING_POS_CACHE
stringSrc.InvalidateCache();
#endif // wxUSE_STRING_POS_CACHE
}
#endif
// string containing nRepeat copies of ch
wxString(wxUniChar ch, size_t nRepeat = 1 )
{ assign(nRepeat, ch); }
@ -1243,6 +1266,10 @@ public:
// we also need to provide this one
wxString(const wxString& str, size_t nLength)
{ assign(str, nLength); }
#ifdef wxHAS_RVALUE_REF
wxString(wxString&& str, size_t nLength)
{ assign(std::move(str), nLength); }
#endif
#if wxUSE_STRING_POS_CACHE
@ -1265,6 +1292,9 @@ public:
#if wxUSE_STD_STRING
#if wxUSE_UNICODE_WCHAR
wxString(const wxStdWideString& str) : m_impl(str) {}
#ifdef wxHAS_RVALUE_REF
wxString(wxStdWideString&& str) wxNOEXCEPT : m_impl(std::move(str)) {}
#endif
#else // UTF-8 or ANSI
wxString(const wxStdWideString& str)
{ assign(str.c_str(), str.length()); }
@ -1274,6 +1304,9 @@ public:
#if !wxUSE_UNICODE // ANSI build
// FIXME-UTF8: do this in UTF8 build #if wxUSE_UTF8_LOCALE_ONLY, too
wxString(const std::string& str) : m_impl(str) {}
#ifdef wxHAS_RVALUE_REF
wxString(std::string&& str) wxNOEXCEPT : m_impl(std::move(str)) {}
#endif
#else // Unicode
wxString(const std::string& str)
{ assign(str.c_str(), str.length()); }
@ -1716,6 +1749,20 @@ public:
return FromImpl(utf8);
}
#ifdef wxHAS_RVALUE_REF
static wxString FromUTF8Unchecked(std::string&& utf8) wxNOEXCEPT
{
wxASSERT(wxStringOperations::IsValidUtf8String(utf8.c_str(), utf8.length()));
return FromImpl(std::move(utf8));
}
static wxString FromUTF8(std::string&& utf8)
{
if (utf8.empty() || !wxStringOperations::IsValidUtf8String(utf8.c_str(), utf8.length()))
return wxString();
return FromImpl(std::move(utf8));
}
#endif
std::string utf8_string() const { return m_impl; }
#endif
@ -1895,6 +1942,20 @@ public:
return *this;
}
#ifdef wxHAS_RVALUE_REF
// move from another wxString
wxString& operator=(wxString&& stringSrc) wxNOEXCEPT
{
m_impl = std::move(stringSrc.m_impl);
#if wxUSE_STRING_POS_CACHE
InvalidateCache();
stringSrc.InvalidateCache();
#endif // wxUSE_STRING_POS_CACHE
return *this;
}
#endif
wxString& operator=(const wxCStrData& cstr)
{ return *this = cstr.AsString(); }
// from a character
@ -2596,6 +2657,19 @@ public:
return *this;
}
#ifdef wxHAS_RVALUE_REF
wxString& assign(wxString&& str) wxNOEXCEPT
{
#if wxUSE_STRING_POS_CACHE
wxSTRING_SET_CACHED_LENGTH(str.length());
str.InvalidateCache();
#endif
m_impl = std::move(str.m_impl);
return *this;
}
#endif
// This is a non-standard-compliant overload taking the first "len"
// characters of the source string.
wxString& assign(const wxString& str, size_t len)
@ -2617,6 +2691,14 @@ public:
return *this;
}
#ifdef wxHAS_RVALUE_REF
wxString& assign(wxString&& str, size_t len)
{
str.Truncate(len);
return assign(std::move(str));
}
#endif
// same as ` = str[pos..pos + n]
wxString& assign(const wxString& str, size_t pos, size_t n)
{
@ -3111,7 +3193,7 @@ public:
{ replace(first, last, first1, last1 - first1); return *this; }
// swap two strings
void swap(wxString& str)
void swap(wxString& str) wxNOEXCEPT
{
#if wxUSE_STRING_POS_CACHE
// we modify not only this string but also the other one directly so we
@ -3124,6 +3206,12 @@ public:
m_impl.swap(str.m_impl);
}
// non-member swap for ADL
friend void swap(wxString& s1, wxString& s2) wxNOEXCEPT
{
s1.swap(s2);
}
// find a substring
size_t find(const wxString& str, size_t nStart = 0) const
{ return PosFromImpl(m_impl.find(str.m_impl, PosToImpl(nStart))); }

View File

@ -1637,6 +1637,7 @@ wxString& wxString::Truncate(size_t uiLen)
if ( uiLen < length() )
{
erase(begin() + uiLen, end());
wxSTRING_SET_CACHED_LENGTH(uiLen);
}
//else: nothing to do, string is already short enough

View File

@ -9,6 +9,7 @@
#include "wx/string.h"
#include "wx/ffile.h"
#include "wx/arrstr.h"
#include "bench.h"
#include "htmlparser/htmlpars.h"
@ -292,6 +293,74 @@ BENCHMARK_FUNC(ReplaceShorter)
return str.Replace("xx", "y") != 0;
}
// ----------------------------------------------------------------------------
// string arrays
// ----------------------------------------------------------------------------
BENCHMARK_FUNC(ArrStrPushBack)
{
wxArrayString a;
for (int i = 0; i < 100; ++i)
{
a.push_back(wxString(asciistr));
a.push_back(wxString(utf8str));
}
return !a.empty();
}
BENCHMARK_FUNC(ArrStrInsert)
{
wxArrayString a;
for (int i = 0; i < 100; ++i)
{
a.insert(a.begin(), wxString(asciistr));
a.insert(a.begin(), wxString(utf8str));
}
return !a.empty();
}
BENCHMARK_FUNC(ArrStrSort)
{
wxArrayString a;
a.reserve(100);
for (int i = 0; i < 100; ++i)
a.push_back(wxString(asciistr + i));
a.Sort();
return !a.empty();
}
BENCHMARK_FUNC(VectorStrPushBack)
{
std::vector<wxString> v;
for (int i = 0; i < 100; ++i)
{
v.push_back(wxString(asciistr));
v.push_back(wxString(utf8str));
}
return !v.empty();
}
BENCHMARK_FUNC(VectorStrInsert)
{
std::vector<wxString> v;
for (int i = 0; i < 100; ++i)
{
v.insert(v.begin(), wxString(asciistr));
v.insert(v.begin(), wxString(utf8str));
}
return !v.empty();
}
BENCHMARK_FUNC(VectorStrSort)
{
std::vector<wxString> v;
v.reserve(100);
for (int i = 0; i < 100; ++i)
v.push_back(wxString(asciistr + i));
std::sort(v.begin(), v.end());
return !v.empty();
}
// ----------------------------------------------------------------------------
// string case conversion
// ----------------------------------------------------------------------------

View File

@ -43,6 +43,13 @@ TEST_CASE("StdString::Constructors", "[stdstring]")
const wchar_t *pw = s2.c_str();
CHECK( wxString(pw, pw + 1) == "a" );
#ifdef wxHAS_RVALUE_REF
wxString s9(std::move(s1));
CHECK( s9 == wxT("abcdefgh"));
wxString s10(std::move(s3), 8);
CHECK( s10 == wxT("abcdefgh"));
#endif
}
TEST_CASE("StdString::Iterators", "[stdstring]")
@ -118,9 +125,9 @@ TEST_CASE("StdString::Append", "[stdstring]")
TEST_CASE("StdString::Assign", "[stdstring]")
{
wxString s1, s2, s3, s4, s5, s6, s7, s8;
wxString s1, s2, s3, s4, s5, s6, s7, s8, s9;
s1 = s2 = s3 = s4 = s5 = s6 = s7 = s8 = wxT("abc");
s1 = s2 = s3 = s4 = s5 = s6 = s7 = s8 = s9 = wxT("abc");
s1.assign(wxT("def"));
s2.assign(wxT("defgh"), 3);
s3.assign(wxString(wxT("abcdef")), 3, 6);
@ -145,6 +152,85 @@ TEST_CASE("StdString::Assign", "[stdstring]")
s1.assign(s1, 1, 1);
CHECK( s1 == "e" );
#ifdef wxHAS_RVALUE_REF
s9.assign(std::move(s2));
CHECK(s9 == wxT("def"));
s2 = wxT("qwerty");
CHECK(s2 == wxT("qwerty"));
#endif
// Self-assignment
s9 = wxT("def");
wxString& s9ref = s9;
s9ref.assign(s9);
CHECK(s9 == wxT("def"));
// Self-move may change the value, but shouldn't crash
// and reassignment should work
#ifdef wxHAS_RVALUE_REF
s9ref.assign(std::move(s9));
s9 = "qwerty";
CHECK(s9 == wxT("qwerty"));
#endif
}
TEST_CASE("StdString::AssignOp", "[stdstring]")
{
wxString s1, s2, s3, s4, s5, s6, s7, s8;
s1 = s2 = s3 = s4 = s5 = s6 = s7 = s8 = wxT("abc");
// operator=
s1 = wxT("def");
CHECK(s1 == wxT("def"));
s2 = s1;
CHECK(s2 == wxT("def"));
const char* pc = s1.c_str();
s3 = pc;
CHECK(s3 == wxT("def"));
const wchar_t* pw = s1.c_str();
s4 = pw;
CHECK(s4 == wxT("def"));
#ifdef wxHAS_RVALUE_REF
s5 = std::move(s1);
CHECK(s5 == wxT("def"));
s1 = wxT("qwerty");
CHECK(s1 == wxT("qwerty"));
#endif
// swap
s6 = wxT("def");
std::swap(s6, s7);
CHECK(s6 == wxT("abc"));
CHECK(s7 == wxT("def"));
swap(s6, s7);
CHECK(s6 == wxT("def"));
CHECK(s7 == wxT("abc"));
s6.swap(s7);
CHECK(s6 == wxT("abc"));
CHECK(s7 == wxT("def"));
// Self-assignment
wxString& s8ref = s8;
s8ref = s8;
CHECK(s8 == wxT("abc"));
// Self-move may change the value, but shouldn't crash
// and reassignment should work
#ifdef wxHAS_RVALUE_REF
s8ref = std::move(s8);
#endif
s8 = "qwerty";
CHECK(s8 == wxT("qwerty"));
// Self-swap
std::swap(s8, s8ref);
CHECK(s8 == wxT("qwerty"));
swap(s8, s8ref);
CHECK(s8 == wxT("qwerty"));
s8.swap(s8ref);
CHECK(s8 == wxT("qwerty"));
}
TEST_CASE("StdString::Compare", "[stdstring]")