<EDIT - RESOLVED> (properly; no hacks )
The following tests work, using '%S' rather than '%s' (and where can people find the documentation about this?)
test(T1272_GoldStandard_Test_snprintf_9) {
char buff128[128] = "?";
auto str1 = "Resolved";
auto str2 = F("Hello9");
auto str3 = F("not this");
str3 = F("World");
snprintf(buff128, ARRAY_SIZE(buff128), "<%s><%S><%S>", str1, str2, str3);
assertEqual("<Resolved><Hello9><World>", buff128);
}
test(T1271_GoldStandard_Test_snprintf_P_8) {
char buff128[128] = "?";
auto str1 = "Resolved";
auto str2 = F("Hello8");
auto str3 = F("not this");
str3 = F("World");
// CAUTION fails //snprintf_P(buff128, ARRAY_SIZE(buff128), "<%s><%S><%S>", str1, str2, str3); // CAUTION fails
snprintf_P(buff128, ARRAY_SIZE(buff128), (const char*)F("<%s><%S><%S>"), str1, str2, str3);
assertEqual("<Resolved><Hello8><World>", buff128);
}
Ignore my following hack ...
</EDIT - RESOLVED>
I know that you can print F() strings via Serial (etc), but I wanted to use them as parameters in snprintf. Ideally, I wanted something like the C#/dotNet 'yield', but it seemed too hard.
So I created a macro that did the hard work, copying the flash string to a temporary (stack) buffer, then used as an argument to snprintf.
The bad news is that the temporary buffer has to be the maximum size; this could be avoided by using heap memory, which I decided to avoid. The good news is that it prints a Serial message if the buffer would be overflowed (it doesn't; the string is truncated).
#define TEMP_BUFF_FOR_FSTRING(TempName, MaxStrLen, FString) \
char TempName[MaxStrLen+1]; \
{ size_t ix = 0; const char* asPChar = (const char *)FString; size_t actLen = strlen_P(asPChar); \
if(actLen > MaxStrLen) { Serial.print(F("!! " #TempName " needs ")); Serial.print(actLen, DEC); Serial.print(F(" but only ")); Serial.println(MaxStrLen, DEC) ; } \
for(ix = 0; ix < MaxStrLen; ++ix){ TempName[ix] = pgm_read_byte(asPChar+ix); if(TempName[ix] == '\0') break; }\
TempName[MaxStrLen] = '\0'; \**strong text**
}
I'm sure that improvements could be made for other people's situations,, but IWFM.
Examples of usage are in some following unit tests (you will notice that the second usage displays a warning message at run time):
test(T1100_Test_TempVarOption) {
{
TEMP_BUFF_FOR_FSTRING(tmp1, 20, F("HelloWorld"));
assertEqual("HelloWorld", tmp1);
}
{
TEMP_BUFF_FOR_FSTRING(tmp2, 5, F("HelloWorld"));
assertEqual("Hello", tmp2);
}
}
Notice that putting curly braces over the shortest chunks of code will free stack space sooner.
BTW my actual usage is not simply inserting literal strings, but variables holding pointers to a range of possible values (it's opcodes for an Arduino AVR decompiler I've written).
Feel free to comment, but please refrain from ones like 'why didn't you do it another way?' I worked pragmatically with limited information and time.
#EDIT# It seems that I am getting replies that don't address the issue. To save time, when suggesting improvements or alternatives, please try and get the following test to pass, perhaps by changing the 'snprintf_P' line of code:
test(T1270_GoldStandard_Test_snprintf_P_7) {
char buff128[128] = "?";
auto str1 = "TheQuote";
auto str2 = F("Hello7");
auto str3 = F("not this");
str3 = F("World");
snprintf_P(buff128, ARRAY_SIZE(buff128), (const char*)F("<%s><%s><%s>"), str1, str2, str3); // change this to provide required answer
assertEqual("<TheQuote><Hello7><World>", buff128);
}