How to concatenate strings in global section?

I would like to build multiple c-strings for MQTT topics in the global section of my sketch. All start with a prefix which I want to configure at one place.
What is the best way to do that, since I cannot use strcat and that sort of stuff in the global section?

I tried to experiment with std::string, where I could just use the + operator, however, I cannot really get that to run.
This compiles, but does not output the right string. In my productive code, it even crashes the ESP. Without the template line, it does not compile. Eventually, I need a const char* for the PubSubLibrary.

#include <string>
template class std::basic_string<char>;

std::string test = "fu";
const char* test2 = (test+"bar").c_str();

void setup() {
  Serial.begin(115200);
  Serial.print("My string: ");
  Serial.println(test2);
}

void loop() {
  // put your main code here, to run repeatedly:

}

What is the best way to do that, since I cannot use strcat and that sort of stuff in the global section?

In a function that you call from setup().

Eventually, I need a const char* for the PubSubLibrary.

No, you need a char *, which is a pointer to a block of memory. The const qualifier simply means that methods of the class will not modify the pointer, or the pointed to data, depending on where the const qualifier is positioned.

A char * is a pointer to a block of memory. An array is a block of memory. The array name is a pointer to the first element of the array. So, create and populate arrays, and forget about the fact that they are usually accessed using pointers.

ElCaron: I would like to build multiple c-strings for MQTT topics in the global section of my sketch. All start with a prefix which I want to configure at one place. What is the best way to do that, since I cannot use strcat and that sort of stuff in the global section?

Maybe it would be simpler to print the strings in parts - something like

Serial.print(prefix);
Serial.println(bodyA);

and on another occasion

Serial.print(prefix);
Serial.println(bodyB);

...R

PaulS: Thanks, I guess I'll do that.

robin2: I do not intent to print this in the productive code. As said, I need a char* to pass to e.g. the publish method of the PubSubClient library.

Still, I would be interested if my example should work and is an issue with the ESP, or if I did something wrong. I have no experience with std::string, but what I did grasp from C by know I don't really understand how memory would be handled in that case.

I have no experience with std::string

So, why are you trying to use it on a system that has so little memory? There is NOTHING that the String class, or the std::string class, can do that can not be done with c strings (NULL terminated arrays of chars).

I am not clear why the OP has tried to do things as he has. As presented in the original code he appears to be trying to declare a pointer to a string by concatenating two constant strings. Can someone please explain why you would want to do this when this would appear to produce the same result ? ?

const char* test2 = "fubar";

Can someone please explain why you would want to do this when this would appear to produce the same result ?

If you had a “foo”, and a “cat” and a “dog”, and you wanted a “foocat” and a “foodog”, but then changed your mind, and wanted a “barcat” and a “bardog”, you’d only need to change “foo” to “bar” in one place, if strings could be concatenated the way OP tried.

Thanks Paul, that makes sense, but you could also do it by declaring "foocat" and "foodog" and doing a search and replace in the editor if you decided that you really wanted "barcat" and "bardog".

I wasn't aware that std::string is also a memory hog. I just knew Sring is. Usually, I use c strings, but in this case the p + operator seemed handy.

UKHeliBob: Sure you can do that. But then you cannot keep the configuration in one place, namely another file that does not go to a public github repository.

I skimmed the thread quickly, but unless I'm misunderstanding what is needed, there is a simple solution to this problem.

By using a define to store the configuration name instead of an actual declaration, you can take advantage of a C++ rule regarding sequential string literals.

In translation phase 6 (2.2), adjacent string literals are concatenated.

This means that the compiler sees: "Hello" "World" as "HelloWorld". And as simple defines are a basic text substitution its a simple way to use this feature.

#define test "foo"

const char* test1 = test "bar";
const char* test2 = test "baz";

void setup() {
  Serial.begin(9600);
  Serial.println(test1);
  Serial.println(test2);
}

void loop() {}

If you want to make the code look a bit more natural you could create some combinations of defines:

#define test(str) "foo" str

//Gives the impression of a function call.
const char* test1 = test("bar");
const char* test2 = test("baz");

Both examples print:

foobar foobaz

Oh, and pick a better name for the define, 'test' is not that great, I only used it as a lazy example.

Awesome. i also experimented with preprocessor stuff, but I failed, since I did not know that adjacent string literals are concatenated. I will use that, thanks