Doing Particle.subscribe in a class constructor?

Hey folks,

I’ve got an interesting problem with my architecture in a little electron project. The way I’ve been working on my project so-far is to create a driver layer for all my hardware interfaces (like debounced buttons, decoded analog inputs, SPI outputs to a matrix display, etc…), and construct these driver objects as static global singletons at the top of my .ino sketch. It’s a bit of a hack, but I’ve been having scoping issues because of the whole loop(){…} structure that goes on not giving me anywhere else to put stuff that needs to exist for the entire run-time of the application, particuarly things like DMX control that need a background timer running the whole time.

Tonight I wrote a driver that’s supposed to handle all my particle cloud transactions, including subscribing to a couple of external event feeds. A minimal test case for my CloudDriver looks something like this:

/* this is at the top of main.ino */
SYSTEM_THREAD(ENABLED);
SYSTEM_MODE(SEMI_AUTOMATIC);
MyCloudDriver CloudDrv;

/* In CloudDriver.cpp */
MyCloudDriver::MyCloudDriver(void):
{
  Particle.variable("GameStat", StateParams);                         // Works Fine
  Particle.function("SetMode", &MyCloudDriver::SetMode, this);        // Works Fine
  Particle.subscribe("MyEvent", &MyCloudDriver::EventHandler, this);  // Doesn't ever call the event handler
  Particle.connect();
}

void MyCloudDriver::EventHandler(const char* name, const char* data)
{
  digitalWrite(D7, HIGH);      // Should turn on the LED when the event handler gets called
}

I’ve currently got a work-around by adding a RegisterEventHooks function that I call from startup() back in main.ino:

MyCloudDriver::RegisterEventHook(void)
{
  Particle.subscribe("MyEvent", &MyCloudDriver::EventHandler, this);  // This one actually works!
}

If I do that, everything works as expected. According to the docs I should be subscribing before I call connect to minimise the data use on the Electron that’s running this project. I’ve also done a test case where I try and subscribe immediately after executing connect in the constructor, but again that doesn’t work, so it seems to be specific to putting the subscribe call in the constructor for my driver class.

Am I missing something obvious, or have I stumbled across a known (or unknown) issue?

One factor playing a role here is the indeterminism of execution order of constructors - including system objects.
If your code has got dependencies on other objects, these cannot reliably be set up in a constructor, since the required dependency might not have been constructed by that time.

Hence the common practice is to implement an init() or begin() method which can be called at a time where all required constructors have been executed alreardy.

1 Like

Thanks Scruff. Always a pleasure to hear from you :wink:

The thing that’s doing my head in is that the variable and function registrations are both working, but the event subscription isn’t. I’ve had the RAII paradigm drummed into me many times over the years, so it just seems wrong to have a separate setup method to call. The little technically correct programmer that sits on my shoulder screams at me that I should do all my resource allocation and acquisition in the constructor, and all my cleanup in the destructor. Since I’m only allowed up to 4 subscriptions, that screams ‘limited resource that needs to be carefully manged’ to me.

I guess in this context it’s one of those situations of ‘break the rules when you have a good reason for it’, but it would be nice if the particle.subscribe call worked in the same way as the particle.variable and particle.function calls.