mirror of
https://github.com/esp8266/Arduino.git
synced 2025-06-09 03:41:41 +03:00
WString: direct operator overloads instead of StringSumHelper (#7781)
* wip * huh, turns out String = 'c' did some weird stuff * style * allow "blah" + String, 'c' + String and F("...") + String also, simplify const char* vs. __FlashStringHelper, and just always use _P functions * shuffle things into .cpp * trying to fix arduinojson based on the implementation, we only need to specify that this symbol is a class * fix accidental realloc, add test for operator+ basic chaining should work just like with master comparing std::move() buffers won't work though, because we never allow anything but `const StringSumHelper&` references * fixup! fix accidental realloc, add test for operator+ * don't need another branch * template +=(String / char* / numbers) and +(String, numbers / char*) * nul after moving (isnt mem always zeroed tho?) * check if lhs cant keep before switching to rhs * fix String used to store struct data `cannot bind bit-field '...' to 'signed char&' `cannot bind bit-field '...' to 'unssigned char&' noticed in both tasmota and espeasy, where this generates a webpage content from some status struct containing bitfields * style once more * typo * recover 444002180bca8e36b3014eaf5ecf5e690837b440
This commit is contained in:
parent
1b922edad1
commit
0894b514cf
@ -236,7 +236,6 @@ public:
|
|||||||
StreamString(const String& string): String(string), S2Stream(this) { }
|
StreamString(const String& string): String(string), S2Stream(this) { }
|
||||||
StreamString(const __FlashStringHelper *str): String(str), S2Stream(this) { }
|
StreamString(const __FlashStringHelper *str): String(str), S2Stream(this) { }
|
||||||
StreamString(String&& string): String(string), S2Stream(this) { }
|
StreamString(String&& string): String(string), S2Stream(this) { }
|
||||||
StreamString(StringSumHelper&& sum): String(sum), S2Stream(this) { }
|
|
||||||
|
|
||||||
explicit StreamString(char c): String(c), S2Stream(this) { }
|
explicit StreamString(char c): String(c), S2Stream(this) { }
|
||||||
explicit StreamString(unsigned char c, unsigned char base = 10): String(c, base), S2Stream(this) { }
|
explicit StreamString(unsigned char c, unsigned char base = 10): String(c, base), S2Stream(this) { }
|
||||||
@ -281,13 +280,6 @@ public:
|
|||||||
resetpp();
|
resetpp();
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
StreamString& operator= (StringSumHelper&& rval)
|
|
||||||
{
|
|
||||||
String::operator=(rval);
|
|
||||||
resetpp();
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // __STREAMSTRING_H
|
#endif // __STREAMSTRING_H
|
||||||
|
@ -21,7 +21,7 @@
|
|||||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <Arduino.h>
|
#include "Arduino.h"
|
||||||
#include "WString.h"
|
#include "WString.h"
|
||||||
#include "stdlib_noniso.h"
|
#include "stdlib_noniso.h"
|
||||||
|
|
||||||
@ -56,11 +56,6 @@ String::String(String &&rval) noexcept {
|
|||||||
move(rval);
|
move(rval);
|
||||||
}
|
}
|
||||||
|
|
||||||
String::String(StringSumHelper &&rval) noexcept {
|
|
||||||
init();
|
|
||||||
move(rval);
|
|
||||||
}
|
|
||||||
|
|
||||||
String::String(unsigned char value, unsigned char base) {
|
String::String(unsigned char value, unsigned char base) {
|
||||||
init();
|
init();
|
||||||
char buf[1 + 8 * sizeof(unsigned char)];
|
char buf[1 + 8 * sizeof(unsigned char)];
|
||||||
@ -390,98 +385,92 @@ unsigned char String::concat(const __FlashStringHelper *str) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*********************************************/
|
/*********************************************/
|
||||||
/* Concatenate */
|
/* Insert */
|
||||||
/*********************************************/
|
/*********************************************/
|
||||||
|
|
||||||
StringSumHelper &operator +(const StringSumHelper &lhs, const String &rhs) {
|
String &String::insert(size_t position, const char *other, size_t other_length) {
|
||||||
StringSumHelper &a = const_cast<StringSumHelper &>(lhs);
|
if (position > length())
|
||||||
if (!a.concat(rhs.buffer(), rhs.len()))
|
return *this;
|
||||||
a.invalidate();
|
|
||||||
return a;
|
auto len = length();
|
||||||
|
auto total = len + other_length;
|
||||||
|
if (!reserve(total))
|
||||||
|
return *this;
|
||||||
|
|
||||||
|
auto left = len - position;
|
||||||
|
setLen(total);
|
||||||
|
|
||||||
|
auto *start = wbuffer() + position;
|
||||||
|
memmove(start + other_length, start, left);
|
||||||
|
memmove_P(start, other, other_length);
|
||||||
|
wbuffer()[total] = '\0';
|
||||||
|
|
||||||
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
StringSumHelper &operator +(const StringSumHelper &lhs, const char *cstr) {
|
String &String::insert(size_t position, const __FlashStringHelper *other) {
|
||||||
StringSumHelper &a = const_cast<StringSumHelper &>(lhs);
|
auto *p = reinterpret_cast<const char*>(other);
|
||||||
if (!cstr || !a.concat(cstr, strlen(cstr)))
|
return insert(position, p, strlen_P(p));
|
||||||
a.invalidate();
|
|
||||||
return a;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
StringSumHelper &operator +(const StringSumHelper &lhs, char c) {
|
String &String::insert(size_t position, char other) {
|
||||||
StringSumHelper &a = const_cast<StringSumHelper &>(lhs);
|
char tmp[2] { other, '\0' };
|
||||||
if (!a.concat(c))
|
return insert(position, tmp, 1);
|
||||||
a.invalidate();
|
|
||||||
return a;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
StringSumHelper &operator +(const StringSumHelper &lhs, unsigned char num) {
|
String &String::insert(size_t position, const char *other) {
|
||||||
StringSumHelper &a = const_cast<StringSumHelper &>(lhs);
|
return insert(position, other, strlen(other));
|
||||||
if (!a.concat(num))
|
|
||||||
a.invalidate();
|
|
||||||
return a;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
StringSumHelper &operator +(const StringSumHelper &lhs, int num) {
|
String &String::insert(size_t position, const String &other) {
|
||||||
StringSumHelper &a = const_cast<StringSumHelper &>(lhs);
|
return insert(position, other.c_str(), other.length());
|
||||||
if (!a.concat(num))
|
|
||||||
a.invalidate();
|
|
||||||
return a;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
StringSumHelper &operator +(const StringSumHelper &lhs, unsigned int num) {
|
String operator +(const String &lhs, String &&rhs) {
|
||||||
StringSumHelper &a = const_cast<StringSumHelper &>(lhs);
|
String res;
|
||||||
if (!a.concat(num))
|
auto total = lhs.length() + rhs.length();
|
||||||
a.invalidate();
|
if (rhs.capacity() > total) {
|
||||||
return a;
|
rhs.insert(0, lhs);
|
||||||
|
res = std::move(rhs);
|
||||||
|
} else {
|
||||||
|
res.reserve(total);
|
||||||
|
res += lhs;
|
||||||
|
res += rhs;
|
||||||
|
rhs.invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
StringSumHelper &operator +(const StringSumHelper &lhs, long num) {
|
String operator +(String &&lhs, String &&rhs) {
|
||||||
StringSumHelper &a = const_cast<StringSumHelper &>(lhs);
|
String res;
|
||||||
if (!a.concat(num))
|
auto total = lhs.length() + rhs.length();
|
||||||
a.invalidate();
|
if ((total > lhs.capacity()) && (total < rhs.capacity())) {
|
||||||
return a;
|
rhs.insert(0, lhs);
|
||||||
|
res = std::move(rhs);
|
||||||
|
} else {
|
||||||
|
lhs += rhs;
|
||||||
|
rhs.invalidate();
|
||||||
|
res = std::move(lhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
StringSumHelper &operator +(const StringSumHelper &lhs, unsigned long num) {
|
String operator +(char lhs, const String &rhs) {
|
||||||
StringSumHelper &a = const_cast<StringSumHelper &>(lhs);
|
String res;
|
||||||
if (!a.concat(num))
|
res.reserve(rhs.length() + 1);
|
||||||
a.invalidate();
|
res += lhs;
|
||||||
return a;
|
res += rhs;
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
StringSumHelper &operator +(const StringSumHelper &lhs, long long num) {
|
String operator +(const char *lhs, const String &rhs) {
|
||||||
StringSumHelper &a = const_cast<StringSumHelper &>(lhs);
|
String res;
|
||||||
if (!a.concat(num))
|
res.reserve(strlen_P(lhs) + rhs.length());
|
||||||
a.invalidate();
|
res += lhs;
|
||||||
return a;
|
res += rhs;
|
||||||
}
|
return res;
|
||||||
|
|
||||||
StringSumHelper &operator +(const StringSumHelper &lhs, unsigned long long num) {
|
|
||||||
StringSumHelper &a = const_cast<StringSumHelper &>(lhs);
|
|
||||||
if (!a.concat(num))
|
|
||||||
a.invalidate();
|
|
||||||
return a;
|
|
||||||
}
|
|
||||||
|
|
||||||
StringSumHelper &operator +(const StringSumHelper &lhs, float num) {
|
|
||||||
StringSumHelper &a = const_cast<StringSumHelper &>(lhs);
|
|
||||||
if (!a.concat(num))
|
|
||||||
a.invalidate();
|
|
||||||
return a;
|
|
||||||
}
|
|
||||||
|
|
||||||
StringSumHelper &operator +(const StringSumHelper &lhs, double num) {
|
|
||||||
StringSumHelper &a = const_cast<StringSumHelper &>(lhs);
|
|
||||||
if (!a.concat(num))
|
|
||||||
a.invalidate();
|
|
||||||
return a;
|
|
||||||
}
|
|
||||||
|
|
||||||
StringSumHelper &operator +(const StringSumHelper &lhs, const __FlashStringHelper *rhs) {
|
|
||||||
StringSumHelper &a = const_cast<StringSumHelper &>(lhs);
|
|
||||||
if (!a.concat(rhs))
|
|
||||||
a.invalidate();
|
|
||||||
return a;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*********************************************/
|
/*********************************************/
|
||||||
|
@ -23,14 +23,15 @@
|
|||||||
#define String_class_h
|
#define String_class_h
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <ctype.h>
|
|
||||||
#include <pgmspace.h>
|
#include <pgmspace.h>
|
||||||
|
|
||||||
// An inherited class for holding the result of a concatenation. These
|
#include <cstdlib>
|
||||||
// result objects are assumed to be writable by subsequent concatenations.
|
#include <cstdint>
|
||||||
class StringSumHelper;
|
#include <cstring>
|
||||||
|
#include <cctype>
|
||||||
|
|
||||||
|
#include <utility>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
// an abstract class used as a means to proide a unique pointer type
|
// an abstract class used as a means to proide a unique pointer type
|
||||||
// but really has no body
|
// but really has no body
|
||||||
@ -38,6 +39,10 @@ class __FlashStringHelper;
|
|||||||
#define FPSTR(pstr_pointer) (reinterpret_cast<const __FlashStringHelper *>(pstr_pointer))
|
#define FPSTR(pstr_pointer) (reinterpret_cast<const __FlashStringHelper *>(pstr_pointer))
|
||||||
#define F(string_literal) (FPSTR(PSTR(string_literal)))
|
#define F(string_literal) (FPSTR(PSTR(string_literal)))
|
||||||
|
|
||||||
|
// support libraries that expect this name to be available
|
||||||
|
// replace with `using StringSumHelper = String;` in case something wants this constructible
|
||||||
|
class StringSumHelper;
|
||||||
|
|
||||||
// The string class
|
// The string class
|
||||||
class String {
|
class String {
|
||||||
// use a function pointer to allow for "if (s)" without the
|
// use a function pointer to allow for "if (s)" without the
|
||||||
@ -60,7 +65,6 @@ class String {
|
|||||||
String(const String &str);
|
String(const String &str);
|
||||||
String(const __FlashStringHelper *str);
|
String(const __FlashStringHelper *str);
|
||||||
String(String &&rval) noexcept;
|
String(String &&rval) noexcept;
|
||||||
String(StringSumHelper &&rval) noexcept;
|
|
||||||
explicit String(char c) {
|
explicit String(char c) {
|
||||||
sso.buff[0] = c;
|
sso.buff[0] = c;
|
||||||
sso.buff[1] = 0;
|
sso.buff[1] = 0;
|
||||||
@ -104,8 +108,10 @@ class String {
|
|||||||
String &operator =(const char *cstr);
|
String &operator =(const char *cstr);
|
||||||
String &operator =(const __FlashStringHelper *str);
|
String &operator =(const __FlashStringHelper *str);
|
||||||
String &operator =(String &&rval) noexcept;
|
String &operator =(String &&rval) noexcept;
|
||||||
String &operator =(StringSumHelper &&rval) noexcept {
|
String &operator =(char c) {
|
||||||
return operator =((String &&)rval);
|
char buffer[2] { c, '\0' };
|
||||||
|
*this = buffer;
|
||||||
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
// concatenate (works w/ built-in types)
|
// concatenate (works w/ built-in types)
|
||||||
@ -130,72 +136,11 @@ class String {
|
|||||||
|
|
||||||
// if there's not enough memory for the concatenated value, the string
|
// if there's not enough memory for the concatenated value, the string
|
||||||
// will be left unchanged (but this isn't signalled in any way)
|
// will be left unchanged (but this isn't signalled in any way)
|
||||||
String &operator +=(const String &rhs) {
|
template <typename T>
|
||||||
|
String &operator +=(const T &rhs) {
|
||||||
concat(rhs);
|
concat(rhs);
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
String &operator +=(const char *cstr) {
|
|
||||||
concat(cstr);
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
String &operator +=(char c) {
|
|
||||||
concat(c);
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
String &operator +=(unsigned char num) {
|
|
||||||
concat(num);
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
String &operator +=(int num) {
|
|
||||||
concat(num);
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
String &operator +=(unsigned int num) {
|
|
||||||
concat(num);
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
String &operator +=(long num) {
|
|
||||||
concat(num);
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
String &operator +=(unsigned long num) {
|
|
||||||
concat(num);
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
String &operator +=(long long num) {
|
|
||||||
concat(num);
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
String &operator +=(unsigned long long num) {
|
|
||||||
concat(num);
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
String &operator +=(float num) {
|
|
||||||
concat(num);
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
String &operator +=(double num) {
|
|
||||||
concat(num);
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
String &operator +=(const __FlashStringHelper *str) {
|
|
||||||
concat(str);
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
friend StringSumHelper &operator +(const StringSumHelper &lhs, const String &rhs);
|
|
||||||
friend StringSumHelper &operator +(const StringSumHelper &lhs, const char *cstr);
|
|
||||||
friend StringSumHelper &operator +(const StringSumHelper &lhs, char c);
|
|
||||||
friend StringSumHelper &operator +(const StringSumHelper &lhs, unsigned char num);
|
|
||||||
friend StringSumHelper &operator +(const StringSumHelper &lhs, int num);
|
|
||||||
friend StringSumHelper &operator +(const StringSumHelper &lhs, unsigned int num);
|
|
||||||
friend StringSumHelper &operator +(const StringSumHelper &lhs, long num);
|
|
||||||
friend StringSumHelper &operator +(const StringSumHelper &lhs, unsigned long num);
|
|
||||||
friend StringSumHelper &operator +(const StringSumHelper &lhs, long long num);
|
|
||||||
friend StringSumHelper &operator +(const StringSumHelper &lhs, unsigned long long num);
|
|
||||||
friend StringSumHelper &operator +(const StringSumHelper &lhs, float num);
|
|
||||||
friend StringSumHelper &operator +(const StringSumHelper &lhs, double num);
|
|
||||||
friend StringSumHelper &operator +(const StringSumHelper &lhs, const __FlashStringHelper *rhs);
|
|
||||||
|
|
||||||
// comparison (only works w/ Strings and "strings")
|
// comparison (only works w/ Strings and "strings")
|
||||||
operator StringIfHelperType() const {
|
operator StringIfHelperType() const {
|
||||||
@ -338,6 +283,14 @@ class String {
|
|||||||
const char *buffer() const { return wbuffer(); }
|
const char *buffer() const { return wbuffer(); }
|
||||||
char *wbuffer() const { return isSSO() ? const_cast<char *>(sso.buff) : ptr.buff; } // Writable version of buffer
|
char *wbuffer() const { return isSSO() ? const_cast<char *>(sso.buff) : ptr.buff; } // Writable version of buffer
|
||||||
|
|
||||||
|
// concatenation is done via non-member functions
|
||||||
|
// make sure we still have access to internal methods, since we optimize based on capacity of both sides and want to manipulate internal buffers directly
|
||||||
|
friend String operator +(const String &lhs, String &&rhs);
|
||||||
|
friend String operator +(String &&lhs, String &&rhs);
|
||||||
|
friend String operator +(char lhs, String &&rhs);
|
||||||
|
friend String operator +(const char *lhs, String &&rhs);
|
||||||
|
friend String operator +(const __FlashStringHelper *lhs, String &&rhs);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void init(void) __attribute__((always_inline)) {
|
void init(void) __attribute__((always_inline)) {
|
||||||
sso.buff[0] = 0;
|
sso.buff[0] = 0;
|
||||||
@ -359,54 +312,81 @@ class String {
|
|||||||
void invalidate(void);
|
void invalidate(void);
|
||||||
unsigned char changeBuffer(unsigned int maxStrLen);
|
unsigned char changeBuffer(unsigned int maxStrLen);
|
||||||
|
|
||||||
// copy and move
|
// copy or insert at a specific position
|
||||||
String ©(const char *cstr, unsigned int length);
|
String ©(const char *cstr, unsigned int length);
|
||||||
String ©(const __FlashStringHelper *pstr, unsigned int length);
|
String ©(const __FlashStringHelper *pstr, unsigned int length);
|
||||||
|
|
||||||
|
String &insert(size_t position, char);
|
||||||
|
String &insert(size_t position, const char *);
|
||||||
|
String &insert(size_t position, const __FlashStringHelper *);
|
||||||
|
String &insert(size_t position, const char *, size_t length);
|
||||||
|
String &insert(size_t position, const String &);
|
||||||
|
|
||||||
|
// rvalue helper
|
||||||
void move(String &rhs) noexcept;
|
void move(String &rhs) noexcept;
|
||||||
};
|
};
|
||||||
|
|
||||||
class StringSumHelper: public String {
|
// concatenation (note that it's done using non-method operators to handle both possible type refs)
|
||||||
public:
|
|
||||||
StringSumHelper(const String &s) :
|
inline String operator +(const String &lhs, const String &rhs) {
|
||||||
String(s) {
|
String res;
|
||||||
}
|
res.reserve(lhs.length() + rhs.length());
|
||||||
StringSumHelper(const char *p) :
|
res += lhs;
|
||||||
String(p) {
|
res += rhs;
|
||||||
}
|
return res;
|
||||||
StringSumHelper(char c) :
|
}
|
||||||
String(c) {
|
|
||||||
}
|
inline String operator +(String &&lhs, const String &rhs) {
|
||||||
StringSumHelper(unsigned char num) :
|
lhs += rhs;
|
||||||
String(num) {
|
return std::move(lhs);
|
||||||
}
|
}
|
||||||
StringSumHelper(int num) :
|
|
||||||
String(num) {
|
String operator +(const String &lhs, String &&rhs);
|
||||||
}
|
String operator +(String &&lhs, String &&rhs);
|
||||||
StringSumHelper(unsigned int num) :
|
|
||||||
String(num) {
|
template <typename T,
|
||||||
}
|
typename = std::enable_if_t<!std::is_same_v<String, std::decay_t<T>>>>
|
||||||
StringSumHelper(long num) :
|
inline String operator +(const String &lhs, const T &value) {
|
||||||
String(num) {
|
String res(lhs);
|
||||||
}
|
res += value;
|
||||||
StringSumHelper(unsigned long num) :
|
return res;
|
||||||
String(num) {
|
}
|
||||||
}
|
|
||||||
StringSumHelper(long long num) :
|
template <typename T,
|
||||||
String(num) {
|
typename = std::enable_if_t<!std::is_same_v<String, std::decay_t<T>>>>
|
||||||
}
|
inline String operator +(String &&lhs, const T &value) {
|
||||||
StringSumHelper(unsigned long long num) :
|
lhs += value;
|
||||||
String(num) {
|
return std::move(lhs);
|
||||||
}
|
}
|
||||||
StringSumHelper(float num) :
|
|
||||||
String(num) {
|
// `String(char)` is explicit, but we used to have StringSumHelper silently allowing the following:
|
||||||
}
|
// `String x; x = 'a' + String('b') + 'c';`
|
||||||
StringSumHelper(double num) :
|
// For comparison, `std::string(char)` does not exist. However, we are allowed to chain `char` as both lhs and rhs
|
||||||
String(num) {
|
|
||||||
}
|
String operator +(char lhs, const String &rhs);
|
||||||
StringSumHelper(const __FlashStringHelper *s) :
|
|
||||||
String(s) {
|
inline String operator +(char lhs, String &&rhs) {
|
||||||
}
|
return std::move(rhs.insert(0, lhs));
|
||||||
};
|
}
|
||||||
|
|
||||||
|
// both `char*` and `__FlashStringHelper*` are implicitly converted into `String()`, calling the `operator+(const String& ...);`
|
||||||
|
// however, here we:
|
||||||
|
// - do an automatic `reserve(total length)` for the resulting string
|
||||||
|
// - possibly do rhs.insert(0, ...), when &&rhs capacity could fit both
|
||||||
|
|
||||||
|
String operator +(const char *lhs, const String &rhs);
|
||||||
|
|
||||||
|
inline String operator +(const char *lhs, String &&rhs) {
|
||||||
|
return std::move(rhs.insert(0, lhs));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline String operator +(const __FlashStringHelper *lhs, const String &rhs) {
|
||||||
|
return reinterpret_cast<const char*>(lhs) + rhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline String operator +(const __FlashStringHelper *lhs, String &&rhs) {
|
||||||
|
return std::move(rhs.insert(0, lhs));
|
||||||
|
}
|
||||||
|
|
||||||
extern const String emptyString;
|
extern const String emptyString;
|
||||||
|
|
||||||
|
@ -555,3 +555,42 @@ TEST_CASE("Replace and string expansion", "[core][String]")
|
|||||||
REQUIRE(l.length() == strlen(buff));
|
REQUIRE(l.length() == strlen(buff));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE("String chaining", "[core][String]")
|
||||||
|
{
|
||||||
|
const char* chunks[] {
|
||||||
|
"~12345",
|
||||||
|
"67890",
|
||||||
|
"qwertyuiopasdfghjkl",
|
||||||
|
"zxcvbnm"
|
||||||
|
};
|
||||||
|
|
||||||
|
String all;
|
||||||
|
for (auto* chunk : chunks) {
|
||||||
|
all += chunk;
|
||||||
|
}
|
||||||
|
|
||||||
|
// make sure we can chain a combination of things to form a String
|
||||||
|
REQUIRE((String(chunks[0]) + String(chunks[1]) + String(chunks[2]) + String(chunks[3])) == all);
|
||||||
|
REQUIRE((chunks[0] + String(chunks[1]) + F(chunks[2]) + chunks[3]) == all);
|
||||||
|
REQUIRE((String(chunks[0]) + F(chunks[1]) + F(chunks[2]) + String(chunks[3])) == all);
|
||||||
|
REQUIRE(('~' + String(&chunks[0][0] + 1) + chunks[1] + String(chunks[2]) + F(chunks[3])) == all);
|
||||||
|
REQUIRE((String(chunks[0]) + '6' + (&chunks[1][0] + 1) + String(chunks[2]) + F(chunks[3])) == all);
|
||||||
|
|
||||||
|
// these are still invalid (and also cannot compile at all):
|
||||||
|
// - `F(...)` + `F(...)`
|
||||||
|
// - `F(...)` + `const char*`
|
||||||
|
// - `const char*` + `F(...)`
|
||||||
|
// we need `String()` as either rhs or lhs
|
||||||
|
|
||||||
|
// ensure chaining reuses the buffer
|
||||||
|
// (internal details...)
|
||||||
|
{
|
||||||
|
String tmp(chunks[3]);
|
||||||
|
tmp.reserve(2 * all.length());
|
||||||
|
auto* ptr = tmp.c_str();
|
||||||
|
String result("~1" + String(&chunks[0][0] + 2) + F(chunks[1]) + chunks[2] + std::move(tmp));
|
||||||
|
REQUIRE(result == all);
|
||||||
|
REQUIRE(static_cast<const void*>(result.c_str()) == static_cast<const void*>(ptr));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user