0

I have a class which has a const char * property:

    class A
    {
    public:
        const PROGMEM char* text;
    };

    void setup()
    {
        // A a{"Hello World!"};
        // A a{PSTR("Hello World!")};

        A a;
        a.text = PSTR("Hello World!");

        Serial.begin(9600);
        delay(30);
        Serial.println(a.text);
    }

    void loop()
    {
    }

I’d like to spare some RAM using PROGMEM.

How to initialize a object then?

Obviously, it can’t be like this:

    A a{PSTR("Hello World!")};

This will do:

    A a;
    a.text = PSTR("Hello World!");

However, I need to pass string to the constructor.

  • does a.text = F("Hello World!"); do what you want? – dandavis Feb 22 '20 at 21:03
  • @dandavis, it does not. you've missed that a.text is not const __FlashStringHelper* - it's const char* – zhekaus Feb 22 '20 at 21:59
3

It is a shame that gcc only supports the __flash qualifier in C mode, not in C++, so we have to use PROGMEM instead. Unlike __flash, which qualifies a variable just like const, the PROGMEM attribute only has effect when allocating room for a variable. Once the allocation is done, the compiler forgets about the attribute. In particular, a declaration such as

const PROGMEM char* text;

does not allocate flash space, so it generates the warning

warning: ‘__progmem__’ attribute ignored [-Wattributes]
     const PROGMEM char* text;
                         ^

You can thus forget the attribute when declaring a pointer, as there is no such thing as a “pointer to PROGMEM”. You just use a const char * instead.

Now, the second issue is that, as far as Serial.println() is concerned, the pointer above is a plain const char *, so it will interpret it as an address in RAM, and print garbage. If you want Serial.println() to know you are giving it an address in flash, you should provide it with a const __FlashStringHelper* pointer.

Here is the solution I propose. Tested on an Uno-compatible board:

class A
{
public:
    A(const char* s)
        : text(reinterpret_cast<const __FlashStringHelper *>(s)) {}
    const __FlashStringHelper* text;
};

void setup()
{
    A a{PSTR("Hello World!")};
    Serial.begin(9600);
    Serial.println(a.text);
}

void loop(){}

Edit: After seeing the last version of Juraj’s answer, I must say that I agree with him. Since we are using the Arduino API, it makes more sense for the constructor to take a const __FlashStringHelper*, and for the caller to use the F() macro.

  • your solution works fine! thank you very much! – zhekaus Feb 22 '20 at 21:56
3

const char* text; is a pointer to constant not a constant pointer (char * const text is a constant pointer). So you can assign a pointer to a constant char array to const char* text; even a pointer to an array in PROGMEM.

The compiler doesn't know the difference between a PROGMEM pointer and a pointer in SRAM. It is on you to work in code with a pointer to PROGMEM the right way.

so remove PROGMEM from const PROGMEM char* text;


add constructor to initialize an object.

A(const char* _text) {
  text = _text;
}

and then

A a(PSTR("Hello World!"));

EDIT:

you could use Arduino's F() macro and __FlashStringHelper type, because it is supported by Serial print and co.

class A
{
public:
  A(const __FlashStringHelper* _text) {
    text = _text;
  }
  const __FlashStringHelper* text;
};

void setup()
{
    A a(F("Hello World!"));

    Serial.begin(9600);
    delay(30);
    Serial.println(a.text);
}

void loop()
{
}

__FlashStringHelper type is trick to distinguish PROGMEM strings from char arrays in SRAM..

  • This is not an answer on my question. How to store strings in flash memory? Strings must be passed to the contructor. – zhekaus Feb 22 '20 at 16:48
  • 1
    it is unclear what is what you don't know. I enhanced the answer – Juraj Feb 22 '20 at 17:25
  • Hum... Your solution won't let use Serial.println. And added user-defined constructor does not any difference here. – zhekaus Feb 22 '20 at 22:03
  • @zhekaus, yes, your question combined 3 problems. Edgar got them all, but the true solution is to use F macro, not PSTR, if possible. I enhanced the answer – Juraj Feb 23 '20 at 6:18

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.