Go Down

Topic: What does the F() do exactly? (Read 7 times) previous topic - next topic

liudr

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 :) I can use the code to learn how to use doxgen :)

robtillaart


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.


Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

Coding Badly

Quote
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...

Code: [Select]
# 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...

Code: [Select]
class Print
{
...
    size_t print(const __FlashStringHelper *);
...
    size_t print(const char[]);


Code: [Select]
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.

mkwired

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 [FLASH].  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.
Code: [Select]

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


This works.
Code: [Select]

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

johnwasser

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.
Send Bitcoin tips to: 1L3CTDoTgrXNA5WyF77uWqt4gUdye9mezN
Send Litecoin tips to : LVtpaq6JgJAZwvnVq3ftVeHafWkcpmuR1e

Go Up