What does the F() do exactly?

I am trying to understand exactly what F() does when its used in something like Serial.print(F("Hello from PROGMEM"));

Here is all I could find in WString.h

class __FlashStringHelper;
#define F(string_literal) (reinterpret_cast<__FlashStringHelper *>(PSTR(string_literal)))

Found this in pgmspace.h

define PSTR(s) ((const PROGMEM char *)(s))

There is no other things besides a simple class name and this macro.

Can someone explain to me the syntax in the F() macro please? My OOP is not strong enough to understand this line :~

BTW, realized the lib author was using doxgen :slight_smile: I can use the code to learn how to use doxgen :slight_smile:

reinterpret_cast is a cast that can convert any type into any pointer type.
In this case it takes care that the PSTR() "pstring" is converted to a pointer to __FlashStringHelper * class.

Most important in practice is that this construction prevents the compiler from throwing all kinds of warnings/errors.

Found this in pgmspace.h
# define PSTR(s) ((const PROGMEM char *)(s))

That is the definition used in the documentation. The definition used when compiling is a few lines below...

# define PSTR(s) (__extension__({static char __c[] PROGMEM = (s); &__c[0];}))

PSTR works somewhat like a function. Storage (an array of characters named __c) for the string constant is created in Flash (PROGMEM tells the compiler and linker to place __c in Flash). The address of the storage (of __c) is "returned".

From Print.h...

class Print
{
...
    size_t print(const __FlashStringHelper *);
...
    size_t print(const char[]);
class __FlashStringHelper;
#define F(string_literal) (reinterpret_cast<__FlashStringHelper *>(PSTR(string_literal)))

PSTR returns a char*. If we do nothing special, print(const char[]) would be called which is not what we what. print(const char[]) does not know how to print strings stored in Flash.

By type-casting, we force the compiler to use print(const __FlashStringHelper *) which does know how to print strings stored in Flash.

2 Likes

Reading data (ex. a string) from FLASH, requires the use of some functions. The AVR is a Harvard architecture. Code and Data are stored separately. The following statement will compile but will not print the string. The reason is because the member function print is passed an argument of type char* which is the base address of the string that's stored in FLASH. The problem is that de-referencing a char* returns the char stored in the Data space (RAM) and not from the Code space . The F() macro changes the type from char* to __FlashStringHelper*. Now, that the argument is a different type, one can create functions that accept that type and called the correct functions to retrieve the data from Code space. The member functions print and println from the class Serial have these overloads.

This does not work.

Serial.print(PSTR("Hello, World"));

This works.

Serial.print(F("Hello, World"));
2 Likes

The class __FlashStringHelper has no body, just a type.

The "reinterpret_cast" tells the compiler that you know that the value being cast is not compatible with the destination type. Unlike a regular cast, no conversion is done. The ONLY safe operation is to cast the value BACK to what it was before, which is what the print function does.

Coould this F() syntax be used in place of rather cumbersome:

strcpy_P(buffer, (char*)pgm_read_word(&(str_table[index])));

Could any of these work for example

Serial.print(F(str_table(2)));
or
Serial.print(F(str_2));

(where
str_2 prog_char str_2[] PROGMEM = "Hello World":wink:

This thread is the best explanation of the F() macro I found so far... Should be made sticky or turned into some official doc page...

1 Like

ninja2:
Coould this F() syntax be used in place of rather cumbersome:

strcpy_P(buffer, (char*)pgm_read_word(&(str_table[index])));

I don't think so because the argument to F() has to be a string constant.
What I think you CAN do is:

Serial.print(reinterpret_cast<__FlashStringHelper *>(str_table[index]));

Passing __FlashStringHelper * to .print or .println tells it that the argument is a pointer to a character string in FLASH.

ninja2:
Coould this F() syntax be used in place of rather cumbersome:

strcpy_P(buffer, (char*)pgm_read_word(&(str_table[index])));

This may help...
http://arduiniana.org/libraries/flash/

mromani:
This thread is the best explanation of the F() macro I found so far... Should be made sticky or turned into some official doc page...

I second that! Thanks so much everyone! So it comes down to:

ATMEGA328P uses Harvard architecture thus separates data and code memory space into SRAM and PROGMEM (AKA FLASH). Accessing data in data memory requires nothing special. Accessing data in code memory requires special functions such as pgm_read_byte and pgm_read_word. String pointers to address in SRAM (data memory) are proper for print but string pointers to PROGMEM (code memory) are not proper for print because the pointer points to code memory. A special print version that handles code memory pointers with pgm_read_byte or pgm_read_word needs to be called. So the F() tells the compiler, through a complex but understandable ( :sweat_smile: ;)) way, that the string is to be stored only in PROGMEM (code memory) to save SRAM (data memory) and the special print that handles code space pointer is to be called.

I'll continue to work on this explanation for a typical arduino user to understand. I for one, have finally understood it (minus how to tell compiler to only store a string in PROGMEM and whether the compiler optimizes duplicates) thanks to the dozen replies!

1 Like

as one who's been a light weight dabbler in code over the years, but never C or C++ until recently, I am constantly amazed at the depth of the C language, how compact yet powerful it is, and how much I still have to learn when I see a command like this (which I understand what it does in principle, but not all the components):

johnwasser:
Serial.print(reinterpret_cast<__FlashStringHelper *>(str_table[index]));

does C need, or interpret variables with leading underscores, like ___THIS, or is this just a software weenie posey naming convention ?

I believe it's just a technique to avoid using "words" that people might already have in their program
who in their right mind would use variables like __fred?!? :slight_smile:

ahah, that makes sense.

although ... the technique seems to be used by lots of C coders in lots of programs, which theoretically undermines their objective for using it :.

If you want to learn if the compiler optimizes duplicates you could just create a sketch that contains duplicates. Then use avr-objdump in order to look into the .elf file and see if the output contains the duplicates.

or create a sketch with duplicates
then make a one character change
if the sketch stays the same size you had duplicates
if it increases by the length of the string - then it was being optimised

void setup(void)
{
  Serial.begin(19200);
}
void loop(void)
{
  Serial.println(F("abcdefg"));
  Serial.println(F("abcdefg"));
}

and

void setup(void)
{
  Serial.begin(19200);
}
void loop(void)
{
  Serial.println(F("abcdefg"));
  Serial.println(F("xbcdefg"));
}

both compiled to 2040 bytes
so it looks like no optimisation

Thanks for finding out the answer mmcp42. I guess F() is a part of the solution to SRAM problem but eventually becomes part of the FLASH problem. Does anyone know how to turn on optimization option for the compiler while using arduino ide?

I believe it's just a technique to avoid using "words" that people might already have in their program
who in their right mind would use variables like __fred?!?

Right. One would have to count (underscores) instead of read (words) :slight_smile:

this is a bit klunky, but might help
(like I said, who in their right mind actually types __ :slight_smile: )

__FlashStringHelper *myStrings[3];

void setup(void)
{
  Serial.begin(19200);
  myStrings[0] = F("abcdefg");
  myStrings[1] = F("xbcdefg");
}
void loop(void)
{
  Serial.println(myStrings[0]);
  Serial.println(myStrings[1]);
  delay(1000);
}

I think you can even initialize the array at compile time rather than run time.

__FlashStringHelper *myStrings[] = {F("abcdefg"), F("xbcdefg")};

void setup(void)
{
  Serial.begin(19200);
}
void loop(void)
{
  Serial.println(myStrings[0]);
  Serial.println(myStrings[1]);
  delay(1000);
}

:slight_smile: