Pointer to class Preferences not working

I'v been breaking my head this afternoon over why the next code is not working properly; what am I missing?

/* **main.cpp***/
#include <Arduino.h>
#include <changeRadio.h>
#include <Preferences.h>

Preferences preferences;
ChangeRadio changeRadio(&preferences);

void setup() 
  vTaskDelay(300 / portTICK_PERIOD_MS);
  // This is working perfectly!
  preferences.begin("RadioStation", false);
  someVar = preferences.getChar("prevRadio", 0);
  Serial.printf("1b. Previous someVar value: %d\n\n", someVar);
  preferences.putChar("prevRadio", someVar);
  vTaskDelay(5000 / portTICK_PERIOD_MS);
} // end setup()

void loop() {} // end loop()

#if !defined(CHANGE_RADIO_H)
#include <Preferences.h>
#include <iostream>
using namespace std;

class ChangeRadio
    Preferences *localPrefs;
    uint8_t _sIndex;

        ChangeRadio(Preferences *globalPrefs)
                this->localPrefs = globalPrefs;
                //globalPrefs->begin("RadioStation", false);
                localPrefs->begin("RadioStation", false);
                //_sIndex = globalPrefs->getChar("prevRadio", 0);
                _sIndex = localPrefs->getChar("prevRadio", 0);
                std::cout << "\n0. Constructor _sIndex: \n" << _sIndex << std::endl;

};  // end class ChangeRadio

#endif // CHANGE_RADIO_H

The only output from the constructor is the text: "0. Constructor _sIndex: ."
Any help is most welcome.

uint8_t is an alias for unsigned char. By printing it to cout, _sIndex will be converted to a character (most likely an unprintable one). If you want to print it as a number, you can add a unary plus to convert it to an integer:

std::cout << "\n0. Constructor _sIndex: \n" << +_sIndex << std::endl;

Thank you. Now it prints a zero. That's some progress.
But... I'm quit sure that there is a value other than zero in preferences [I've checked with the commented out code in main.cpp]
So where do I go wrong with the pointers?

That's impossible to say without seeing your complete code.

This is the complete code.

Where is <Preferences.h>?

I appreciate your trying to help, but I completely missed your point.

You cannot be shure, that preferences is already valid when you instantiate changeRadio in this case. You don't know the order in which global instantiations are done. You must instantiate changeRadio in setup to be sure that preferences is valid.

Preferences preferences;
//ChangeRadio changeRadio(&preferences);
ChangeRadio *changeRadioP ;

in setup:

  changeRadioP = new ChangeRadio(&preferences);

P.S. An alternative may be - as it is in many classes/libraries - to have a 'begin' method that ist to be called in setup. And then you call preferences in the begin method rather than in the constructor.

Yes you can, dynamic initialization of global variables within the same translation unit are ordered.


Ordered dynamic initialization, which applies to all other non-local variables: within a single translation unit, initialization of these variables is always sequenced in exact order their definitions appear in the source code. Initialization of static variables in different translation units is indeterminately sequenced.

Otherwise, things like the following would be pretty problematic as well:

int a = 5;
int b = a + 1;

@PieterP & MicroBahner,
Thank you both, this opens up a new line of thinking.

And, even better, it works! Output now is:
0. Constructor _sIndex:

Coming to think of it: isn't rather weird that I, the programmer, can't fully control the sequence program statements are executed?

How would that work across translation units? Which translation unit should come first? Which libraries? You do have full control within a single translation unit.

Thank you. I'm beginning to see your point, but I'm also lost in terminology. "Translation units", libraries I know of, well you see my point, I trust.
Just lack of knowledge. Maybe you will/can point me in the right direction?

If this would be the case, than it would work as pagemaker made it in the first attempt. But this is not the case - it did not work. That's also the reason why nearly all libraries have a 'begin' or 'init' method.
Maybe the real problem is not the creation of the objects itself, but the initialisation of the Hardware. And obviously you don't have control when this is done. If the objects wouldn't be created in order, then the pointer to preferences would be invalid, and it would probably crash and not only give wrong values.
The Preferences class also has a begin method, and that should also be called first in setup().

Basically every implementation file that is compiled separately is a separate translation unit. In other words, every .cpp file is its own translation unit. The concatenation of all .ino files is also a translation unit.

You might be confusing this with the static initialization order fiasco, which can happen between translation units. However:

Within a single translation unit, the fiasco does not apply because the objects are initialized from top to bottom.

There are good reasons to use a begin() method, but the order of initialization of preferences and changeRadio is not the issue here, the order is well-defined and correct.

No, you have already convinced me that the objects are created in the order specified. As I have already written, otherwise the pointer to preferences would be invalid here
ChangeRadio changeRadio(&preferences);
and the program would crash when calling methods of preferences in the constructor of changeRadio. But it doesn't crash, it only returns the wrong values. So the object pointer must be ok.

The question is when the hardware is initialized. And this is surely done in another translation unit, and it depends how the core and the base system is organized. Only in setup you can be sure that the hardware is completely initialized.

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.