I undertstand the trim() function change the String in place:
The trim() function modifies the String in place rather than returning a new one.
Sometimes this is very annoying, i.e. when you just need a trimmed version to be displayed but you don't want to mess with the original String. Example:
String s = "My string";
// ...
String display_s = s;
display_s.trim();
my_show_func(display_s)
if you have a lot of this stuff is more handy to use a trimmed() function:
String s = "My string";
my_show_func(s.trimmed());
if Arduino does not provide this function, is it possible to "add" it in some way so it will be enabled for the String variables in my code?
To be clear, of course I can just write something like:
String trimmed(String s)
{
s.trim();
return s;
}
but I wonder if using the magic of C++ is possible to add this function to the String class.
can you please give an example? I don't get the operator overloads working in the inherited class (or the constructor with assignment)
class MyString : public String {
public:
String getClean() {
String clean = (*this);
clean.trim();
return clean;
}
};
void setup() {
Serial.begin(115200);
MyString s;
Serial.println(s.getClean()); //
//s = " My string "; // error: no match for 'operator='
Serial.println(s.getClean());
}
void loop() {
}
sketch.ino:17:7: error: no match for 'operator=' (operand types are 'MyString' and 'const char [12]')
s = " My string "; // error: no match for 'operator='
^~~~~~~~~~~~~
found that comment:
*With the exception of the assignment function operator =( ), all overloaded operator functions for class X are inherited by classes derived from X, *
class MyString : public String {
public:
String getClean() {
String clean = (*this);
clean.trim();
return clean;
}
MyString & operator = (const char *cstr) {
if (cstr) copy(cstr, strlen(cstr));
else invalidate();
return *this;
}
// other = operator overloads need to be added
};
void setup() {
Serial.begin(115200);
MyString s;
Serial.println(s.getClean()); //
s = " My string "; // now ok (only that type of assignement!)
Serial.println(s.getClean());
s +=" test ";
Serial.println(s.getClean());
}
void loop() {
}
may be a with a further hack to call the String::String & operator =
to avoid the copied code?
Can you read and display only the chars needed directly from the stored text? IIRC you can but it's a pain and you're not supposed to! You're supposed to waste RAM on a copy, like you have unlimited RAM.
Question is Why Use C++ Strings in a small RAM environment?
Ibet the answer has to do with not knowing better and conveniences!
/*
SafeSting length(), trim()
by Matthew Ford
Copyright(c)2020 Forward Computing and Control Pty. Ltd.
This example code is in the public domain.
www.forward.com.au/pfod/ArduinoProgramming/SafeString/index.html
*/
#include "SafeString.h"
void setup() {
// Open serial communications and wait a few seconds
Serial.begin(115200);
Serial.println();
Serial.println(F("SafeString length(), trim() functions"));
Serial.println(F("SafeString::setOutput(Serial); // verbose == true"));
// see the SafeString_ConstructorAndDebugging example for debugging settings
SafeString::setOutput(Serial); // enable full debugging error msgs
Serial.println();
// here's a String with empty spaces at the end (called white space):
createSafeString(stringOne, 32, " Hello! ");
createSafeString(stringTwo, 32);
stringOne.debug();
Serial.print("#");
Serial.print(stringOne);
Serial.println("#");
Serial.print(F("stringOne.length() = "));
Serial.println(stringOne.length());
Serial.println();
stringTwo = stringOne;
stringTwo.trim();
stringTwo.debug(F("stringTwo.trim(); => "));
Serial.println("stringTwo.trim() done");
Serial.print("#");
Serial.print(stringTwo);
Serial.println("#");
Serial.print(F("stringTwo.length() = "));
Serial.println(stringTwo.length());
Serial.println();
Serial.println(F(" c_str() gives to access to the underly char array as a const char* so you cannot change the contents"));
Serial.println();
Serial.print(F("stringTwo.c_str() = #"));
Serial.print(stringTwo.c_str());
Serial.println("#");
Serial.println();
Serial.println();
}
void loop() {
}
#include <time.h> // time() ctime()
time_t now; // this is the epoch
tm myTimeInfo; // the structure tm holds time information in a more convient way
#include <SafeString.h>
createSafeString(myDemo_SS, 32);
createSafeString(mySecondDemo_SS, 32);
#define MaxTimeStampLength 64
createSafeString(TimeStamp_SS, MaxTimeStampLength);
createSafeString(myTitle_SS, 64); // reserve 64 bytes for the SafeString-variable
createSafeString(myString_SS, 32); // reserve 32 bytes for the SafeString-variable
int myInteger = -1234;
float myFloat = -987.009;
unsigned long myCounter;
//useful function for non-blocking timing
boolean TimePeriodIsOver (unsigned long &periodStartTime, unsigned long TimePeriod) {
unsigned long currentMillis = millis();
if ( currentMillis - periodStartTime >= TimePeriod )
{
periodStartTime = currentMillis; // set new expireTime
return true; // more time than TimePeriod) has elapsed since last time if-condition was true
}
else return false; // not expired
}
unsigned long MyTestTimer = 0; // variables MUST be of type unsigned long
const byte OnBoard_LED = 13; // Arduino-Uno Onboard-LED is IO-pin 13
void setup() {
Serial.begin(115200);
Serial.println( F("Setup-Start") );
myCounter = 0;
myDemo_SS = "Hello world!";
myString_SS = F("fixed text directly assigned");
Serial.print( F(" #") ); // leading double-cross "#" to show where the string starts
Serial.print(myString_SS);
Serial.println(F("#") ); // trailing double-cross "#" to show where the string REALLY ends
myTitle_SS = F("content of an integer:");
myString_SS = myInteger;
Serial.println(myTitle_SS);
Serial.print( F(" #" )); // leading double-cross "#" to show where the string starts
Serial.print(myString_SS);
Serial.println( F("#") ); // trailing double-cross "#" to show where the string REALLY ends
myTitle_SS = F("content of a float:");
myString_SS = myFloat;
Serial.println(myTitle_SS);
Serial.print( F(" #" )); // leading double-cross "#" to show where the string starts
Serial.print(myString_SS);
Serial.println( F("#") ); // trailing double-cross "#" to show where the string REALLY ends
myTitle_SS = F("you can append more text with the +=-operator ");
myString_SS = F("text ");
myString_SS += myInteger;
myString_SS += F(" ,");
myString_SS += myFloat;
Serial.println(myTitle_SS);
Serial.print( F(" #" )); // leading double-cross "#" to show where the string starts
Serial.print(myString_SS);
Serial.println( F("#") ); // trailing double-cross "#" to show where the string REALLY ends
}
void loop() {
myCounter++;
// loop is running very fast counting up very fast
// but only once every 1234 milliseconds print
if ( TimePeriodIsOver(MyTestTimer,5234) ) {
mySecondDemo_SS = myDemo_SS; // assigning a SafeString to another SafeString
mySecondDemo_SS += " "; // append a SPACE
mySecondDemo_SS += myCounter; // append integer-number
Serial.println(mySecondDemo_SS);
StoreTimeStampIntoSS(TimeStamp_SS);
Serial.println(TimeStamp_SS);
Serial.println(TimeStamp_SS.c_str() );
}
}
void StoreTimeStampIntoSS(SafeString& p_RefToSS) {
time(&now); // read the current time
localtime_r(&now, &myTimeInfo); // update the structure tm with the current time
//p_RefToSS = " ";
p_RefToSS = myTimeInfo.tm_year + 1900;
p_RefToSS += ".";
if (myTimeInfo.tm_mon + 1 < 10) {
p_RefToSS += "0";
}
p_RefToSS += myTimeInfo.tm_mon + 1;
p_RefToSS += ".";
if (myTimeInfo.tm_mday < 10) {
p_RefToSS += "0";
}
p_RefToSS += myTimeInfo.tm_mday;
p_RefToSS += "; ";
if (myTimeInfo.tm_hour < 10) {
p_RefToSS += "0";
}
p_RefToSS += myTimeInfo.tm_hour;
p_RefToSS += ":";
if (myTimeInfo.tm_min < 10) {
p_RefToSS += "0";
}
p_RefToSS += myTimeInfo.tm_min;
p_RefToSS += ":";
if (myTimeInfo.tm_sec < 10) {
p_RefToSS += "0";
}
p_RefToSS += myTimeInfo.tm_sec;
}
@StefanL38 @GoForSmoke
The question is not about using a String or not, but about inheritance. Why the "=" operator, defined in parent class, doesn't available in the child class?
In Python or javascript, you can achieve the functionality by defining indeed a method and then attaching it to a class or an instance dynamically (unless it's passed) because you have a runtime.
C++ is statically compiled, you can't achieve the same thing.
"using" does the trick. Not only for the constructor but also for the = operators.
Thank you. Learn something new every day.
class MyString : public String {
public:
using String::String; // Inherit constructors from the String class and = operator overload
MyString getClean() {
MyString clean = (*this);
clean.trim();
return clean;
}
};
void setup() {
Serial.begin(115200);
MyString s;
Serial.println(s.getClean()); //
s = " My string "; // now ok
Serial.println(s.getClean());
s +=" test ";
Serial.println(s.getClean());
}
void loop() {
}