PlatformIO Community

[SOLVED] Switching from String to std::string is hell!

Hello !

I am currently trying to switch from using String to std::string, because even if it’s WAY easier to use String, I understand that it plays with realloc() and can cause “heap fragmentation”.

I would like some help with moving from String to std::string.

I have a void function that takes as argument a std::string string1 and puts it in a ring buffer but also prints it on the Serial.

void logOutput(std::string string1) {
	delay(500);
	circle.push(string1);	
	Serial.println(string1.c_str());	
}

And I have problems concatenating char elements to a std::string:

const char* ssid = "ssid";
const char* password = "password";
WiFi.begin(ssid, password); delay(2000);
if(WiFi.status() == WL_CONNECTED) {
	logOutput("Connected to: " + ssid + " with IP: " + WiFi.localIP().toString().c_str());
} 

logOutput("Connected to: " += ssid + " with IP: " += WiFi.localIP().toString().c_str()); does not work either !

So how am I supposed to send one beautiful string to logOutput so that it can add it to the ring buffer ? I can’t send pieces of it, because they’ll be put in a ring buffer that, later on, will print them on different lines.

If I can’t use + operator to concatenate char to a string how should I do it ?

Not even a simple for works properly:

	for(int i = 1; i<=10;i++){
		logOutput("Linia " + i);
	}

It outputs: inia nia ia a. What is wrong with std::string ?
String is a champion and easy to use. I have to write a lot of additional lines for a std::string to work properly.

	while(WiFi.status() != WL_CONNECTED && k<20) {
		delay(1000);
		k++;
		logOutput("Connecting to WiFi");
	}

Outputs:

Connecting to WiFi
onnecting to WiFi
nnecting to WiFi
necting to WiFi

For reference:

Circular Buffer:

std::string strlog; // global variable

struct ring_buffer
{
    ring_buffer(size_t cap) : buffer(cap) {}

    bool empty() const { return sz == 0 ; }
    bool full() const { return sz == buffer.size() ; }

    void push( std::string str )
    {
        if(last >= buffer.size()) last = 0 ;
        buffer[last] = str ;
        ++last ;
        if(full()) 
			first = (first+1) %  buffer.size() ;
        else ++sz ;
    }
    void print() const {
		strlog = ""; // empty its previous content
		if( first < last )
			for( size_t i = first ; i < last ; ++i ) {
				strlog.append(buffer[i]);
				strlog.append("<br>");
			}	
		else
		{
			for( size_t i = first ; i < buffer.size() ; ++i ) {
				strlog.append(buffer[i]);
				strlog.append("<br>");
			}
			for( size_t i = 0 ; i < last ; ++i ) {
				strlog.append(buffer[i]);
				strlog.append("<br>");
			}
		}
	}

    private:
        std::vector<std::string> buffer ;
        size_t first = 0 ;
        size_t last = 0 ;
        size_t sz = 0 ;
};

I use this circular buffer to send what ever string I would output with Serial.println(); to a webpage using ESPAsyncWebServer library’s processor(); function:

String processor(const String& var) { 
	circle.print();
	if  (var == "PLACEHOLDER_1")
		return strlog.c_str();
	return String();
}

I think you might get more responses from Arduino community forum. The problems you’re having are not related to PlatformIO in any way.

But you might want to try out PString library. It works somewhat similarly as String but you define a constant size for it so it won’t cause cause any fragmentation. I’m a novice with C++ but it looks like your code is perhaps overwriting the memory. I ran into a similar issue at some point when trying to learn to use char* instead of String.

The standard library string likely uses heap also BTW. If you want to avoid using heap you need to use some buffer that has its size defined staticly. PString which I suggested will do this for you.

This is a 3 weeks old topic. Since then I’ve found out that moving from String to std::string won’t make a difference. If I would want to make a difference I should start using char.
And since I’m using an ESP32 that has 512 KB of SRAM and just a few Strings in my project I don’t really need to move away from String.

Thank you !

1 Like

Hi,

You are in essence mixing pointer calculation in C with operator overloading in C++.

in C the concept of an array does not really exist. An array is just a pointer. Constant strings in C therefor are just pointers as they are arrays of characters. When you write

Serial.print("linia");

Under the hood, "linia " is stored in the data of the program, a constant pointer (let’s call it __s) to it is created and the string in line is replaced by that pointer. So the line now looks like.

Serial.print(__s);

So

Serial.print("linia" + i);

is translated into

Serial.print(__s + i);

Which brings you into pointer calculation. It becomes a pointer to the i-th element of the string.

The class String has redefined the + operator as concatenator. So the second line

String str = "linia";
String s = str + 1;

is converted to

String s = operator+(str, 1)

That function will convert 1 to a string and append it to str to produce s.

To do what you want to do, you need to look at the string library, with functions like sprintf or strcat. Make sure that your buffers are large enough.

I hope the above is clear. Regards Paul.

1 Like