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:
#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:
enum 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):
static 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:
String 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