Your new topic does not fit any of the above??? Check first. Then post here. Thanks.

Moderator: igrr

User avatar
By anotherjoe
#48157 I recently went on a code refactor crusade to try and improve the situation in my project where free heap memory as reported by ESP.getFreeHeap() had dropped to around 19Kb. I knew that all of the hardcoded strings were a bad idea not helping this depleting heap memory problem so I decided to go after this particular problem.

After much scratching around and reading what other people do, in the end I came up with what seems to be an easily manageable code pattern that seems to have improved the situation by about 6Kb where free heap memory is now reported as being around 25Kb.

Not being a C/C++ expert myself, I'd like to ask for opinions on this code pattern from those with more experience to try and workout if what I've ended up with is ok or if it's a complete disaster.

It works like this:
I've created a string table that gets processed by the pre-processor to create individual PROGMEM strings, a string array of these strings and an associated enum that I can use in code to retrieve a PROGMEM string from flash. The string table looks like this:
Code: Select all#define STRING_TABLE \
   ENTRY(enumStrVal1, "string1") \
   ENTRY(enumStrVal2, "string2") \
   ENTRY(enumStrVal3, "string3") \
   etc...


The contents of this string table then gets converted to code generated PROGMEM strings and a populated enum by some more pre-processor magic:
Code: Select allenum appStrType {
#define ENTRY(enumVal, str) enumVal,
   STRING_TABLE
#undef ENTRY
};


#define ENTRY(enumVal, str) extern const char CLI_STR_ ## enumVal[];
STRING_TABLE
#undef ENTRY

#define ENTRY(enumVal, str) const char CLI_STR_ ## enumVal[] PROGMEM = str;
STRING_TABLE
#undef ENTRY

const char* const appStrings[] PROGMEM = {
#define ENTRY(enumVal, str) CLI_STR_ ## enumVal,
      STRING_TABLE
#undef ENTRY
};


And then finally I've introduced a little function that does the work to pull a PROGMEM string from flash (the part I'm not too sure about):
Code: Select allstatic char appStrBuffer[70];

String getAppStr(appStrType strType) {

   strcpy_P(appStrBuffer, (char*)pgm_read_dword(&(appStrings[strType])));
   return String(appStrBuffer);
}


So this is all well and good, it gives me a decent code pattern that I can easily extend by simply extending the string table as required and in application code where I need a string, I can just do this sort of thing:
Code: Select allString string1 = getAppStr(appStrType::enumStrVal1);


As I mentioned at the start, this does seem to have improved the depleting heap memory problem that I had and I like this pattern because I'm shielded from having to do any dynamic memory management with malloc / free etc which keeps code clean / maintainable / readable. However I'm not sure if this is a "good pattern" because it obviously has the potential to create a lot of String objects (on the stack?). On the flip side I don't believe this setup is leaking any memory.

So there you have it C/C++ experts - I welcome feedback on this pattern

Thanks
Joe