In hindsight, I think maybe the type alias stuff and the
const part wasn’t strictly necessary, and I probably should have included
reinterpret_cast in the list, but in fairness I was rushing at the time.
F macro trick combines several techniques, which is why it’s a bit awkward to explain it in simple terms without using too much ‘jargon’.
I think the best place to start is why the
F macro exists.
Normally, if you use a bare string literal in Arduino code, the string is actually located in RAM, and what effectively happens is that the string will be stored in progmem and then copied into RAM before it’s used, which is typically inefficient.
Fortunately it’s possible to create a macro that places a string literal in progmem and suppresses the creation of the RAM string, which is more efficient. avr-libc, the library that underpins AVR implementations of the Arduino library, provides this ability as a macro named
PSTR, which the
F macro uses internally to store a string literal in progmem. However, putting a string literal in progmem is only half the problem.
The other half of the problem is that normally there’s no way to differentiate between a pointer that points to RAM and a pointer that points to progemem, both are represented as a normal pointer. This is a problem because different CPU instructions are required for reading from progmem.
In most cases you can get away by having separate functions for reading from each, but for the printing mechanism used in Arduino, the authors wanted a way to be able to use
The solution they chose was to create a ‘dummy’ type,
class __FlashStringHelper;, which is declared but never defined, which means it’s possible to create pointers to such a type, but those pointers cannot be dereferenced. Pointers to
__FlashStringHelper are then used to signify a string that’s stored in progmem, which in turn allows the same function name to be used to handle both a string in RAM (
const char *) and a string in progmem (
const __FlashStringHelper *).
Due to the fact the
__FlashStringHelper type doesn’t occur ‘naturally’, there has to be an extra conversion (a cast) inserted somewhere so what would normally be a
const char * can be interpreted as a
const __FlashStringHelper *. And that’s the other thing that the
F macro does - performing that type conversion.
With that all said, the following two lines in
WString.h should now more or less make sense:
And if you look over at
Print.h, you will see the progmem-accepting
const __FlashStringHelper * overload of
const char  overload of
Does that all make sense?