Hi.
I'm trying to write a string library with SSO.
I'm using union that old short string under 11 char, and pointer, length and capacity for long string.
The library is a rewrite of WString.h.
The trouble is: if I create a short string, I cannot create a long string after. Create any long string work until I create a short.
Digging , I found that it was the allocation that fail. but I can't find why.
Tested on Arduino DUE.
Here the example I use:
#include "WString_OP.h"
void setup() {
Serial.begin(9600);
Serial.println("long to long");
MyString s5("tests3______");
//MyString s6("ab");
Serial.println(s5());
//s5 = s5 + 'c';
//s5.concat( s6 );
Serial.println(s5());
Serial.println("s to s");
MyString s1("test");
Serial.println(s1());
MyString s2 = ("short");
s1 = s2;
Serial.println(s1());
Serial.println("s to l");
MyString s3("test");
Serial.println(s3());
//MyString s4 = ("long string test 1");
MyString s4("long string test 1");
s3 = s4;
Serial.println(s3());
}
WString_OP.h:
/*
WString.h - String library for Wiring & Arduino
...mostly rewritten by Paul Stoffregen...
Copyright (c) 2009-10 Hernando Barragan. All right reserved.
Copyright 2011, Paul Stoffregen, paul@pjrc.com
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "arduino.h"
#ifndef String_class_OP_h
#define String_class_OP_h
#ifdef __cplusplus
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <avr/pgmspace.h>
#include <avr/dtostrf.h>
// When compiling programs with this class, the following gcc parameters
// dramatically increase performance and memory (RAM) efficiency, typically
// with little or no increase in code size.
// -felide-constructors
// -std=c++0x
class __FlashStringHelper;
#define F(string_literal) (reinterpret_cast<const __FlashStringHelper *>(PSTR(string_literal)))
#define LONG_STRING 1
#define SHORT_STRING 0
#define SHORT_STRING_CAPACTY 11
#define LONG_STRING_FLAG 1<<7
#define LONG_STRING_FLAG_TO_LONG (1UL << 31)
/*
bool shortStringValide (const char* str)
{
return ( strlen(str)<= SHORT_STRING_CAPACTY ) ? 1 : 0;
}
bool shortStringValide (int len)
{
return ( len <= SHORT_STRING_CAPACTY ) ? 1 : 0;
}*/
// An inherited class for holding the result of a concatenation. These
// result objects are assumed to be writable by subsequent concatenations.
class MyStringSumHelper;
// The string class
class MyString
{
// use a function pointer to allow for "if (s)" without the
// complications of an operator bool(). for more information, see:
// http://www.artima.com/cppsource/safebool.html
typedef void (MyString::*MyStringIfHelperType)() const;
void MyStringIfHelper() const {}
public:
// constructors ---> should add shortstring description
// creates a copy of the initial value.
// if the initial value is null or invalid, or if memory allocation
// fails, the string will be marked as invalid (i.e. "if (s)" will
// be false).
MyString( const char* cstr = "");
MyString(const MyString &value);
MyString(const __FlashStringHelper *str); // to implement
#if __cplusplus >= 201103L || defined(__GXX_EXPERIMENTAL_CXX0X__)
MyString(MyString &&rval); // to test
//MyString(MyStringSumHelper &&rval); //to impelement
#endif
explicit MyString(char c);
explicit MyString(unsigned char, unsigned char base=10);
explicit MyString(int, unsigned char base=10);
explicit MyString(unsigned int, unsigned char base=10);
explicit MyString(long, unsigned char base=10);
explicit MyString(unsigned long, unsigned char base=10);
explicit MyString(float, unsigned char decimalPlaces=2);
explicit MyString(double, unsigned char decimalPlaces=2);
~MyString();
//!!!!!!!!!! have to add short string memory management description
// memory management
// return true on success, false on failure (in which case, the string
// is left unchanged). reserve(0), if successful, will validate an
// invalid string (i.e., "if (s)" will be true afterwards)
unsigned char reserve(unsigned int size);
inline const unsigned int length() const;
const char * operator()() const;
MyString & operator = (const MyString &rhs);
MyString & operator = (const char *cstr);
MyString & operator = (const __FlashStringHelper *str); //to test
#if __cplusplus >= 201103L || defined(__GXX_EXPERIMENTAL_CXX0X__)
MyString & operator = (MyString &&rval);
//MyString & operator = (MyStringSumHelper &&rval);
#endif
// concatenate (works w/ built-in types)
// returns true on success, false on failure (in which case, the string
// is left unchanged). if the argument is null or invalid, the
// concatenation is considered unsucessful.
unsigned char concat(const MyString &str);
unsigned char concat(const char *cstr);
unsigned char concat(char c);
unsigned char concat(unsigned char c);
unsigned char concat(int num);//not singlely tested but other overload works
unsigned char concat(unsigned int num);//not singlely tested but other overload works
unsigned char concat(long num);//not singlely tested but other overload works
unsigned char concat(unsigned long num);//not singlely tested but other overload works
unsigned char concat(float num);//not singlely tested but other overload works
unsigned char concat(double num);//not singlely tested but other overload works
//unsigned char concat(const __FlashStringHelper * str);
friend MyStringSumHelper & operator + (const MyStringSumHelper &lhs, const MyString &rhs);
friend MyStringSumHelper & operator + (const MyStringSumHelper &lhs, const char *cstr);
friend MyStringSumHelper & operator + (const MyStringSumHelper &lhs, char c);
friend MyStringSumHelper & operator + (const MyStringSumHelper &lhs, unsigned char num);
friend MyStringSumHelper & operator + (const MyStringSumHelper &lhs, int num);
friend MyStringSumHelper & operator + (const MyStringSumHelper &lhs, unsigned int num);
friend MyStringSumHelper & operator + (const MyStringSumHelper &lhs, long num);
friend MyStringSumHelper & operator + (const MyStringSumHelper &lhs, unsigned long num);
friend MyStringSumHelper & operator + (const MyStringSumHelper &lhs, float num);
friend MyStringSumHelper & operator + (const MyStringSumHelper &lhs, double num);
//friend MyStringSumHelper & operator + (const MyStringSumHelper &lhs, const __FlashStringHelper *rhs);
protected:
//DATA structure
struct LongString
{
char* data;
unsigned long capacity;
unsigned long len;
};
struct ShortString
{
char data[SHORT_STRING_CAPACTY];
byte len;
};
struct ShortStringArray { //trick, if size and flag == 0 size return 11 and the last but is use as null caracter
char data[SHORT_STRING_CAPACTY+1];
};
union Buffer
{
ShortString shortString;
LongString longString;
ShortStringArray shortStringArray;
};
Buffer buffer;
protected:
inline void init(void);
void setLen( byte len );
bool bufferIsLongString() const { return buffer.shortString.len & LONG_STRING_FLAG; }
void setLongShortStringFlag(bool flag);
void invalidate(void);
unsigned char changeBuffer(unsigned int maxStrLen);
unsigned char concat(const char *cstr, unsigned int length);
MyString & copy(const char *cstr, unsigned int length);
MyString & copy(const __FlashStringHelper *pstr, unsigned int length); //to implement
#if __cplusplus >= 201103L || defined(__GXX_EXPERIMENTAL_CXX0X__)
void move(MyString &rhs);
#endif
unsigned int shortToLong( MyString & str, int newLen );
};
class MyStringSumHelper : public MyString
{
public:
MyStringSumHelper(const MyString &s) : MyString(s) {}
MyStringSumHelper(const char *p) : MyString(p) {}
MyStringSumHelper(char c) : MyString(c) {}
MyStringSumHelper(unsigned char num) : MyString(num) {}
MyStringSumHelper(int num) : MyString(num) {}
MyStringSumHelper(unsigned int num) : MyString(num) {}
MyStringSumHelper(long num) : MyString(num) {}
MyStringSumHelper(unsigned long num) : MyString(num) {}
MyStringSumHelper(float num) : MyString(num) {}
MyStringSumHelper(double num) : MyString(num) {}
};
#endif // __cplusplus
#endif // String_class_h
WString_OP.cpp
/*
WString.cpp - String library for Wiring & Arduino
...mostly rewritten by Paul Stoffregen...
Copyright (c) 2009-10 Hernando Barragan. All rights reserved.
Copyright 2011, Paul Stoffregen, paul@pjrc.com
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "WString_OP.h"
/*********************************************/
/* Constructors */
/*********************************************/
MyString::MyString( const char* cstr ) {
init();
if ( cstr ) {
int len = strlen( cstr );
copy( cstr, len );
}
/* old version
init();
if (cstr) copy(cstr, strlen(cstr));
*/
}
MyString::MyString(const MyString &value)
{
init();
*this = value;
}
/*
MyString::MyString(const __FlashStringHelper *pstr)
{
init();
*this = pstr;
}
*/
#if __cplusplus >= 201103L || defined(__GXX_EXPERIMENTAL_CXX0X__)
MyString::MyString(MyString &&rval)
{
init();
move(rval);
}
/*MyString::MyString(MyStringSumHelper &&rval)
{
init();
move(rval);
}*/
#endif
MyString::MyString(char c)
{
init();
buffer.shortStringArray.data[0] = c;
buffer.shortStringArray.data[1] = 0;
//setLen(1);
/*old version
init();
char buf[2];
buf[0] = c;
buf[1] = 0;
*this = buf;*/
}
MyString::MyString(unsigned char value, unsigned char base)
{
init();
utoa(value, buffer.shortStringArray.data , base);
/* old version
init();
char buf[1 + 8 * sizeof(unsigned char)];
utoa(value, buf, base);
*this = buf;
*/
}
MyString::MyString(int value, unsigned char base)
{
init();
char buf[2 + 8 * sizeof(int)];
itoa(value, buf, base);
*this = buf;
}
MyString::MyString(unsigned int value, unsigned char base)
{
init();
char buf[1 + 8 * sizeof(unsigned int)];
utoa(value, buf, base);
*this = buf;
}
MyString::MyString(long value, unsigned char base)
{
init();
char buf[2 + 8 * sizeof(long)];
ltoa(value, buf, base);
*this = buf;
}
MyString::MyString(unsigned long value, unsigned char base)
{
init();
char buf[1 + 8 * sizeof(unsigned long)];
ultoa(value, buf, base);
*this = buf;
}
MyString::MyString(float value, unsigned char decimalPlaces)
{
init();
char buf[33];
*this = dtostrf(value, (decimalPlaces + 2), decimalPlaces, buf);
}
MyString::MyString(double value, unsigned char decimalPlaces)
{
init();
char buf[33];
*this = dtostrf(value, (decimalPlaces + 2), decimalPlaces, buf);
}
MyString::~MyString()
{
if ( bufferIsLongString() && buffer.longString.data )
{
free( buffer.longString.data);
}
//free(buffer); //old version
}
/*********************************************/
/* Memory Management */
/*********************************************/
void MyString::setLen( byte len )
{
if ( !bufferIsLongString() ) {
buffer.shortString.len = len | ( buffer.shortString.len & LONG_STRING_FLAG );
}
else {
buffer.longString.len = len | ( buffer.longString.len & LONG_STRING_FLAG_TO_LONG );
}
}
const unsigned int MyString::length() const
{
if ( bufferIsLongString() ) {
return buffer.longString.len & ~LONG_STRING_FLAG_TO_LONG;
} else {
if ( buffer.shortStringArray.data[0] == 0){
return 0;
}
else if ( buffer.shortString.len ) return buffer.shortString.len;
else return 11; //trick to use all 12 bytes, use "len" as null character
}
}
void MyString::init(void)
{
///set to 0 all struct. good for both long and short string
buffer.longString.data = nullptr;
buffer.longString.len = 0;
buffer.longString.capacity = 0;
}
void MyString::setLongShortStringFlag(bool flag)
{
buffer.shortString.len = buffer.shortString.len & ~LONG_STRING_FLAG; //reset flag
buffer.shortString.len = buffer.shortString.len | flag << 7; //set flag
}
unsigned char MyString::reserve(unsigned int size)
{
if (length() && buffer.longString.capacity >= size) return 1;
if (changeBuffer(size)) {
if (length() == 0) buffer.longString.data[0] = 0;
return 1;
}
return 0;
/* old version:
if (buffer && capacity >= size) return 1;
if (changeBuffer(size)) {
if (len == 0) buffer[0] = 0;
return 1;
}
return 0;
*/
}
const char * MyString::operator()() const {
//Serial.println(buffer.longString.data);
if ( bufferIsLongString() ) {
return buffer.longString.data;
}
else return buffer.shortStringArray.data;
}
void MyString::invalidate(void)
{
if (buffer.longString.data) free(buffer.longString.data);
init();
//buffer.longString.data = nullptr;
//buffer.longString.capacity = buffer.longString.len = 0;
/*
if (buffer) free(buffer);
buffer = NULL;
capacity = len = 0;
*/
}
unsigned char MyString::changeBuffer(unsigned int maxStrLen)
{
Serial.print("change buffer sixe: "); Serial.println(maxStrLen);
char *newbuffer = (char *)realloc(buffer.longString.data, maxStrLen + 1);
if (newbuffer) {
Serial.println("alocation succed");
buffer.longString.data = newbuffer;
buffer.longString.capacity = maxStrLen;
return 1;
}
return 0;
/* old version:
char *newbuffer = (char *)realloc(buffer, maxStrLen + 1);
if (newbuffer) {
buffer = newbuffer;
capacity = maxStrLen;
return 1;
}
return 0;
*/
}
/*********************************************/
/* Copy and Move */
/*********************************************/
MyString & MyString::copy(const char *cstr, unsigned int length)
{
if ( !bufferIsLongString() ) init();
setLongShortStringFlag ( (length <= SHORT_STRING_CAPACTY) ? 0 : 1);
if ( bufferIsLongString() ) {
Serial.print("copy ftion check long string: "); Serial.println( cstr );
if (!reserve(length)) {
Serial.println( length );
Serial.println( "invalidate" );
invalidate();
return *this;
}
setLen(length);
strcpy(buffer.longString.data, cstr);
return *this;
} else { //shortString
if ( length == SHORT_STRING_CAPACTY ) buffer.shortString.len = 0; //trick to use all 12 byte for buffer
else buffer.shortString.len = length;
strcpy( buffer.shortStringArray.data, cstr);
//Serial.println("copy short");
return *this;
}
}
MyString & MyString::copy(const __FlashStringHelper *pstr, unsigned int length)
{
/* if (!reserve(length)) {
invalidate();
return *this;
}
len = length;
strcpy_P(buffer, (PGM_P)pstr);*/
return *this;
}
#if __cplusplus >= 201103L || defined(__GXX_EXPERIMENTAL_CXX0X__)
void MyString::move(MyString &rhs)
{
//handle to short string
if ( rhs.length() <= SHORT_STRING_CAPACTY ) { //handle from long string
if ( bufferIsLongString() && buffer.longString.data ) {
free( buffer.longString.data );
}
init();
setLen(rhs.length());
strcpy(buffer.shortStringArray.data, rhs());
} else { // handle to long string
init();
setLongShortStringFlag(LONG_STRING);
setLen(rhs.length());
buffer.longString.data = rhs.buffer.longString.data;
rhs.buffer.longString.data = nullptr;
}
/*
if (buffer) {
if (rhs && capacity >= rhs.len) {
strcpy(buffer, rhs.buffer);
len = rhs.len;
rhs.len = 0;
return;
} else {
free(buffer);
}
}
buffer = rhs.buffer;
capacity = rhs.capacity;
len = rhs.len;
rhs.buffer = NULL;
rhs.capacity = 0;
rhs.len = 0;*/
}
#endif
unsigned int MyString::shortToLong( MyString & str, int newLen ) {
if ( str.bufferIsLongString() ) return 1;
char * buf = new char[newLen +1];
strcpy ( buf, str() );
init();
str.buffer.longString.data = buf;
str.buffer.longString.capacity = newLen;
str.buffer.longString.len = newLen;
return 1;
}
MyString & MyString::operator = (const MyString &rhs)
{
if (this == &rhs) return *this;
Serial.print("check copyable: ");
Serial.println(rhs());
if ( !rhs.bufferIsLongString() ) {
if ( buffer.longString.data ) free( buffer.longString.data );
Serial.println("copy short to short");
buffer = rhs.buffer;
} else {
if ( !bufferIsLongString() ){
init();
Serial.println("init");
}
setLongShortStringFlag( LONG_STRING );
Serial.println("copy long string");
copy(rhs.buffer.longString.data, rhs.length());
}
return *this;
/*if ( bufferIsLongString() && !rhs.bufferIsLongString() ) {
free(buffer.longString.data); //clear buffer if switching from long to short
setLongShortStringFlag(SHORT_STRING);
}
if (!bufferIsLongString() && rhs.bufferIsLongString()) {
if (rhs.buffer.longString.data) {
init();
setLongShortStringFlag(LONG_STRING);
copy(rhs.buffer.longString.data, rhs.length());
}
else invalidate();
} else {
this->buffer = rhs.buffer;
}
return *this;*/
/*old version
if (this == &rhs) return *this;
if (rhs.buffer) copy(rhs.buffer, rhs.len);
else invalidate();
return *this;
*/
}
MyString & MyString::operator = (const char *cstr)
{
if ( cstr ) {
int len = strlen(cstr);
if ( bufferIsLongString() && len <= SHORT_STRING_CAPACTY) {
free(buffer.longString.data); //clear buffer if switching from long to short
setLongShortStringFlag(SHORT_STRING);
}
else if ( !bufferIsLongString() && len > SHORT_STRING_CAPACTY ) {
init();
setLongShortStringFlag(LONG_STRING);
}
copy(cstr, len);
}
return *this;
/* old version
if (cstr) copy(cstr, strlen(cstr));
else invalidate();
return *this;
*/
}
MyString & MyString::operator = (const __FlashStringHelper *pstr)
{
if (pstr) copy(pstr, strlen_P((PGM_P)pstr));
else invalidate();
return *this;
}
#if __cplusplus >= 201103L || defined(__GXX_EXPERIMENTAL_CXX0X__)
MyString & MyString::operator = (MyString &&rval)
{
if (this != &rval) move(rval);
//old version
// if (this != &rval) move(rval);
return *this;
}
/*
String & String::operator = (StringSumHelper &&rval)
{
if (this != &rval) move(rval);
return *this;
}*/
#endif
/*********************************************/
/* concat */
/*********************************************/
unsigned char MyString::concat(const MyString &s)
{
return concat( s(), s.length() );
//return concat(s.buffer, s.len); //old version
}
unsigned char MyString::concat(const char *cstr, unsigned int clength)
{
if ( clength == 0 ) return 1;
unsigned int len = length();
unsigned int newlen = len + clength;
if ( newlen > SHORT_STRING_CAPACTY ) { //handle long string
//////////////need short to long routine....
if ( !bufferIsLongString() && length() > 0) { //move short string to long string buffer
shortToLong( *this, newlen);
}
else if (!reserve(newlen)) return 0;
setLongShortStringFlag( LONG_STRING );
strcpy(buffer.longString.data + len, cstr);
} else {// handle still fit short string
strcpy(buffer.shortString.data + len, cstr);
}
setLen( newlen );
return 1;
//old version
/* unsigned int newlen = len + length;
if (!cstr) return 0;
if (length == 0) return 1;
if (!reserve(newlen)) return 0;
strcpy(buffer + len, cstr);
len = newlen;
return 1;*/
}
unsigned char MyString::concat(const char *cstr)
{
if (!cstr) return 0;
return concat(cstr, strlen(cstr));
}
unsigned char MyString::concat(char c)
{
char buf[2];
buf[0] = c;
buf[1] = 0;
return concat(buf, 1);
}
unsigned char MyString::concat(unsigned char num)
{
char buf[1 + 3 * sizeof(unsigned char)];
itoa(num, buf, 10);
return concat(buf, strlen(buf));
}
unsigned char MyString::concat(int num)
{
char buf[2 + 3 * sizeof(int)];
itoa(num, buf, 10);
return concat(buf, strlen(buf));
}
unsigned char MyString::concat(unsigned int num)
{
char buf[1 + 3 * sizeof(unsigned int)];
utoa(num, buf, 10);
return concat(buf, strlen(buf));
}
unsigned char MyString::concat(long num)
{
char buf[2 + 3 * sizeof(long)];
ltoa(num, buf, 10);
return concat(buf, strlen(buf));
}
unsigned char MyString::concat(unsigned long num)
{
char buf[1 + 3 * sizeof(unsigned long)];
ultoa(num, buf, 10);
return concat(buf, strlen(buf));
}
unsigned char MyString::concat(float num)
{
char buf[20];
char* string = dtostrf(num, 4, 2, buf);
return concat(string, strlen(string));
}
unsigned char MyString::concat(double num)
{
char buf[20];
char* string = dtostrf(num, 4, 2, buf);
return concat(string, strlen(string));
}
/*unsigned char String::concat(const __FlashStringHelper * str)
{
if (!str) return 0;
int length = strlen_P((const char *) str);
if (length == 0) return 1;
unsigned int newlen = len + length;
if (!reserve(newlen)) return 0;
strcpy_P(buffer + len, (const char *) str);
len = newlen;
return 1;
}*/
MyStringSumHelper & operator + (const MyStringSumHelper &lhs, const MyString &rhs)
{
MyStringSumHelper &a = const_cast<MyStringSumHelper&>(lhs);
if (!a.concat(rhs(), rhs.length())) a.invalidate();
return a;
}
MyStringSumHelper & operator + (const MyStringSumHelper &lhs, const char *cstr)
{
MyStringSumHelper &a = const_cast<MyStringSumHelper&>(lhs);
if (!cstr || !a.concat(cstr, strlen(cstr))) a.invalidate();
return a;
}
MyStringSumHelper & operator + (const MyStringSumHelper &lhs, char c)
{
MyStringSumHelper &a = const_cast<MyStringSumHelper&>(lhs);
if (!a.concat(c)) a.invalidate();
delay(1000);
return a;
}
MyStringSumHelper & operator + (const MyStringSumHelper &lhs, unsigned char num)
{
MyStringSumHelper &a = const_cast<MyStringSumHelper&>(lhs);
if (!a.concat(num)) a.invalidate();
return a;
}
/*
MyStringSumHelper & operator + (const MyStringSumHelper &lhs, int num)
{
MyStringSumHelper &a = const_cast<MyStringSumHelper&>(lhs);
if (!a.concat(num)) a.invalidate();
return a;
}
MyStringSumHelper & operator + (const MyStringSumHelper &lhs, unsigned int num)
{
MyStringSumHelper &a = const_cast<MyStringSumHelper&>(lhs);
if (!a.concat(num)) a.invalidate();
return a;
}
MyStringSumHelper & operator + (const MyStringSumHelper &lhs, long num)
{
MyStringSumHelper &a = const_cast<MyStringSumHelper&>(lhs);
if (!a.concat(num)) a.invalidate();
return a;
}
MyStringSumHelper & operator + (const MyStringSumHelper &lhs, unsigned long num)
{
MyStringSumHelper &a = const_cast<MyStringSumHelper&>(lhs);
if (!a.concat(num)) a.invalidate();
return a;
}
MyStringSumHelper & operator + (const MyStringSumHelper &lhs, float num)
{
MyStringSumHelper &a = const_cast<MyStringSumHelper&>(lhs);
if (!a.concat(num)) a.invalidate();
return a;
}
MyStringSumHelper & operator + (const MyStringSumHelper &lhs, double num)
{
MyStringSumHelper &a = const_cast<MyStringSumHelper&>(lhs);
if (!a.concat(num)) a.invalidate();
return a;
}
*/
/*
MyStringSumHelper & operator + (const MyStringSumHelper &lhs, const __FlashStringHelper *rhs)
{
MyStringSumHelper &a = const_cast<StringSumHelper&>(lhs);
if (!a.concat(rhs)) a.invalidate();
return a;
}
*/