Global single instance object (similar to EEPROM from Particle)

Hi,

I want to have global access to certain objects in my code and guarantee there is only ever one instance. This is mainly for access to hardware peripherals as I have gotten bored of all the boiler plate code needed for dependency injection.

I am looking for something like the EEPROM functionality provided by Particle that allows me to call EEPROM.write() etc from anywhere in my code.

Should I be using global objects, singletons or something else?

Thanks

Something like a singleton is best in this case.

I usually create a separate cpp/h file for this object, and have all members be static.

A good example is the spark_wiring_rgb.cpp/h files. Take a look:


Just remember to create your object, i would usually do it in the cpp file.
ie: RGBClass RGB ; usually goes at the end of my cpp file. Now any file that I #include "spark_wiring_rgb.h" knows that the object RGB exists and can access it.

1 Like

Thanks for the link, I will definitely create an object.

I don’t think I quite understand how it should be used though…

  • Where should the constructor/deconstructor go (public/private) and how do I ensure it gets called on first use only?
  • What does the extern RGBClass RGB; line do and do I need it?
  • Does adding RGBClass RGB; at the end of the cpp file instantiate an instance and allow me to access it like RGB.control()?

Cheers

These three questions answer eachother when just look at them last to first

Q3

RGBClass RGB; // at the end of the cpp file instantiates AND constructs a global object

Q2

extern RGBClass RGB; // in the header tells everybody who uses it that there already is a global object to use 

application.h includes it, hence every module including it (so all) will be informed

Q1
answered by above

1 Like

OK its starting to make sense.

When will the constructor get called? I ask because my code seems to be working but does not printf any messages from the constructor.

Cheers

EDIT

Here is my class, please let me know if I have done anything incorrectly, if there is a better way to do it and any potential issues I may have - mainly around the order constructors are called.

ExternalEeprom.cpp

#ifndef __EXTERNAL_EEPROM_CPP_
#define __EXTERNAL_EEPROM_CPP_

#include "ExternalEeprom.h"

ExternalEeprom::ExternalEeprom()
{
    _spieep = new SPIEEP(24, 256, 131072);
    _spieep->begin_spi(A2);

    if(!_spieep->test_chip()) Serial.printf("Eeprom NOT FOUND!!\r\n");
    else Serial.printf( "Finished Eeprom initialisation\r\n");
}

ExternalEeprom::~ExternalEeprom() {}

void ExternalEeprom::readSettings(byte* settings, uint16_t size)
{
    _spieep->readn(0, settings, size);
}

void ExternalEeprom::writeSettings(byte* settings, uint16_t size)
{
    _spieep->writen(0, settings, size);
}

ExternalEeprom _externalEeprom;

#endif

ExternalEeprom.h

#ifndef __EXTERNAL_EEPROM_H_
#define __EXTERNAL_EEPROM_H_

#include "application.h"
#include "SPIEEP.h"

class ExternalEeprom
{
  public:
    ExternalEeprom();
    virtual ~ExternalEeprom();

    void readSettings(byte* settings, uint16_t size);
    void writeSettings(byte* settings, uint16_t size);

  private:
    SPIEEP* _spieep;
};

extern ExternalEeprom _externalEeprom;

#endif

I use this from other classes like this: _externalEeprom.readSettings(_buffer, 64);

Thanks for your advice :smiley: )

Constructors of seperate modules will be executed in an “unpredictable” order.
So your constructor might be called before Serial object gets instantiated.
This is the reason why you should initialize your object but not do work in the constructor on these devices - hence most classes feature a begin() methode to be called in setup() or before first use.

OK cool, so with a begin() method controlling init order is this pattern generally accepted to be correct and safe?

Is there any way to throw a compile time error if the class is used before begin() has been called?

unfortunately not at compile time. That would be equivalent to solving the halting problem.

any other workaround or solutions? I can check that a begin flag has been set in every function but then i’m back to boiler plate code.

Cheers