Declaring a Spark.function() in the constructor of a subclass

I’m working on a larger project with both Cores and Photons and we have a base class with a number of child classes. The base class has a method called update which pretty much just calls another pure virtual method called doUpdate. Each of the child classes implements doUpdate and none of them implement update.

With that said, we’d like to be able to do as the reference docs do and add a Spark.function within the constructors of those child classes.

The constructor, etc. essentially looks like this:

// ChildClassName.cpp

OurNamespace::ChildClassName:ChildClassName(...) : OurNamespace::BaseClassName(...) {
  // Bunch of initialization
  
  Spark.function("a_function_name", &OurNamespace::ChildClassName::update, this);
}

...

int OurNamespace::ChildClassName:doUpdate(String args) {
  // do whatever
}

// BaseClassName.cpp
int OurNamespace::BaseClassName:update(String args) {
  // do whatever
  return doUpdate(args);
}

Now when I try to compile this, I get errors similar to this:

OurNameSpace_ChildClassName.cpp: In constructor 'OurNameSpace::ChildClassName::ChildClassName(int, std::list<int>*)':
OurNameSpace_ChildClassName.cpp:45:80: error: no matching function for call to 'CloudClass::function(const char [7], int (OurNameSpace::BaseClassName::*)(String), OurNameSpace::ChildClassName*)'
     Spark.function("ChildClassName_1", &OurNameSpace::ChildClassName::update, ((OurNameSpace::ChildClassName*)this));
                                                                                ^
OurNameSpace_ChildClassName.cpp:45:80: note: candidates are:
In file included from ../wiring/inc/spark_wiring.h:46:0,
                 fCompile failed. Exiting.
rom ./inc/application.h:36,
                 from OurNameSpace_BaseClassName.h:15,
                 from OurNameSpace_Thermostat.h:14,
                 from OurNameSpace_ChildClassName.h:14,
                 from OurNameSpace_ChildClassName.cpp:1:
../wiring/inc/spark_wiring_cloud.h:52:17: note: static bool CloudClass::function(const char*, int (*)(String))
     static bool function(const char *funcKey, user_function_int_str_t* func)
                 ^
../wiring/inc/spark_wiring_cloud.h:52:17: note:   candidate expects 2 arguments, 3 provided
../wiring/inc/spark_wiring_cloud.h:57:17: note: static bool CloudClass::function(const char*, user_std_function_int_str_t, void*)
     static bool function(const char *funcKey, user_std_function_int_str_t func, void* reserved=NULL)
                 ^
../wiring/inc/spark_wiring_cloud.h:57:17: note:   no known conversion for argument 2 from 'int (OurNameSpace::BaseClassName::*)(String)' to 'user_std_function_int_str_t {aka std::function<int(String)>}'
../wiring/inc/spark_wiring_cloud.h:75:17: note: template<class T> static void CloudClass::function(const char*, int (T::*)(String), T*)
     static void function(const char *funcKey, int (T::*func)(String), T *instance) {
                 ^
../wiring/inc/spark_wiring_cloud.h:75:17: note:   template argument deduction/substitution failed:
OurNameSpace_ChildClassName.cpp:45:80: note:   deduced conflicting types for parameter 'T' ('OurNameSpace::BaseClassName' and 'OurNameSpace::ChildClassName')
     Spark.function("ChildClassName_1", &OurNameSpace::ChildClassName::update, ((OurNameSpace::ChildClassName*)this));
                                                                                ^
make[1]: *** [../build/target/user/platform-6OurNameSpace_ChildClassName.o] Error 1
make: *** [user] Error 2

-- Configuring done
-- Generating done
-- Build files have been written to: /some/place

Now, I’ve tried casting the this to see if it helps and I get the same thing. Also note that I’m only instantiating a single copy of this child class, so I don’t think that the string not being unique is the issue either (I would think it wouldn’t be regardless as these are compiler errors not runtime errors, anyhow). Additionally, I run into this no matter whether I’m compiling for Core or for Photon.

Any suggestions?

Actually, I figured it out on my own. The solution is to provide a template value to Spark.function. So, the call in the constructor now looks like:

Spark.function<OurNameSpace::ChildClassName>("a_function_name", &OurNameSpace::ChildClassName::update, this);

Hope this helps someone!

PS: You should probably provide a function for generating a unique name for each new Spark function, otherwise I would expect runtime errors as you add new instances of a given child class.

1 Like

You can also do it using a lambda function that you pass to Spark.function() - the lambda calls this-update();

Spark.function(this->functionName(), [this]->{ this->update(); });

This will work with multiple subclasses (“child” classes) allowing each one to provide the function name and the virtual update() function.

1 Like

Would that work with more than one layer of subclasses? I.e. if I had that in the constructor of A and B is a subclass of A and C is a subclass of B, it’ll use the update function of B for B and of C for C?

Yes, because update() is virtual so it always calls the correct one

1 Like

Ah, that makes sense. I’ll give it a shot :smile:

Thanks!

I’m getting this sort of error when I try it as @mdma is suggesting:

error: expected '{' before '->' token
     Spark.function( this->functionName(), [this]->{ this->update; } );

Which seems to indicate I should use a ‘.’ instead of a ‘->’. Turns out, you actually want something more like this:

Spark.function( this->functionName(), [this](String args) -> int { update(args); } );

The reason is because (in addition to the pointer thing) the lambda must match the expected form of user_function_int_str_t (i.e. the expected format for functions being called by Spark.function, take in a String and spit out an int).

Unfortunately, I’m still playing around with this because of that first parameter. We use information specific to the child class to create the function name and virtual methods shouldn’t be called in constructors.

This seems like it might be a really spiffy way to do things, though, so I’m going to keep digging on it.

Hope that helps someone.

I wrote that syntax off the top of my head. You’ll probably need to change it to something like:

[this](const String& s) -> int { return update(); }

(ah, I see you wrote the same.)

I’d use a const String& rather than simply String so you avoid a copy operation.

EDIT: To avoid calling this in the base class constructor, instead make a method:

int register(const char* name)
{
    return Particle.function(name, [this](const String& s) -> int { return this->update(s); });
}

Then in each concrete subclass call register from the constructor:

SomeDerivedClass()
{
   register(functionName());
}

This gets around calling virtual functions in base classes before the subclasses are fully initialized.

An alternative is to use 2-phase construction (e.g. a separate virtual initialize() method.) You can ensure clients always call the initialize method by using a static factory method and making the constructor private.

1 Like

No worries, I had a feeling it was something like that :wink:

Looks like it’s probably not going to work in my case because of the aforementioned virtual-functions-in-the-constructor issue, which is too bad because that’s pretty slick.

you probably didn’t see my edit above

Posted it before your edit, methinks…

That’s pretty neat too. I’ll give it a shot.

Yup, that seems to be the way to go. Thanks for the help!

1 Like