Callback to a non-static C++ Member Function

Hi guys,

I need help. I want to wrap this mqtt library in a class, and I’m getting this compilation issue:

running command$ particle compile photon /home/user/particle/mqttInClass --saveTo /home/user/particle/mqttInClass/firmware.bin
Compiling code for photon
Including:
    /home/user/particle/mqttInClass/MQTT.h
    /home/user/particle/mqttInClass/mqttClient.h
    /home/user/particle/mqttInClass/MQTT.cpp
    /home/user/particle/mqttInClass/mqttClient.cpp
attempting to compile firmware 
Compile failed. Exiting.
mqttClient.cpp: In constructor 'c_mqttClient::c_mqttClient()':
mqttClient.cpp:9:56: error: no match for call to '(MQTT) (const char [15], uint16_t, <unresolved overloaded function type>)'
  mqttCli("www.sample.com", uint16_t(1883), mqttCallback);
                                                        ^
make[1]: *** [../build/target/user/platform-6mqttClient.o] Error 1
make: *** [user] Error 2

This is the code that gets that issue:

file mqttClient.cpp:

#include "application.h"
#include "mqttClient.h"

void c_mqttClient::mqttCallback(char* topic, uint8_t* payload, unsigned int length) {
  Particle.publish("mqtt message received", "", 60, PRIVATE);
}

c_mqttClient::c_mqttClient() {
 mqttCli("www.sample.com", uint16_t(1883), mqttCallback);
}

file mqttClient.h:

#ifndef MQTTCLIENT_H
#define MQTTCLIENT_H

#include "application.h"
#include "MQTT.h"

class c_mqttClient {

    MQTT mqttCli;

 public:
    c_mqttClient();

    void mqttCallback(char* topic, byte* payload, unsigned int length);

};

#endif


I searched google, this community, c forums and I believe the issue is due to the following fact:
Pointers to non-static members are different to ordinary C function pointers since they need the this-pointer of a class object to be passed. Thus ordinary function pointers and non-static member functions have different and incompatible signatures!
Source is here.

So I went ahead and tried the wrapper approach described here (second answer) and here. I ended up with this issue:


running command$ particle compile photon /home/user/particle/mqttInClass --saveTo /home/user/particle/mqttInClass/firmware.bin
Compiling code for photon
Including:
    /home/user/particle/mqttInClass/MQTT.h
    /home/user/particle/mqttInClass/mqttClient.h
    /home/user/particle/mqttInClass/MQTT.cpp
    /home/user/particle/mqttInClass/mqttClient.cpp
attempting to compile firmware 
Compile failed. Exiting.
mqttClient.cpp: In constructor 'c_mqttClient::c_mqttClient()':
mqttClient.cpp:19:63: error: no match for call to '(MQTT) (const char [15], uint16_t, void (&)(char*, uint8_t*, unsigned int))'
 mqttCli("www.sample.com", uint16_t(1883), mqttCallback_wrapper);
                                                               ^
make[1]: *** [../build/target/user/platform-6mqttClient.o] Error 1
make: *** [user] Error 2

File mqttClient.cpp:

#include "application.h"
#include "mqttClient.h"

void* pt2Object;

void c_mqttClient::mqttCallback_wrapper (char* topic, uint8_t* payload, unsigned int length){
   // explicitly cast to a pointer to c_mqttClient
   c_mqttClient* mySelf = (c_mqttClient*) pt2Object;

   // call member
   mySelf->mqttCallback(topic, payload, length);
}

void c_mqttClient::mqttCallback(char* topic, uint8_t* payload, unsigned int length) {
  Particle.publish("mqtt message received", "", 60, PRIVATE);
}

c_mqttClient::c_mqttClient() {
mqttCli("www.sample.com", uint16_t(1883), mqttCallback_wrapper);
}

File mqttClient.h:

#ifndef MQTTCLIENT_H
#define MQTTCLIENT_H

#include "application.h"
#include "MQTT.h"

class c_mqttClient {
    MQTT mqttCli;
 public:
    c_mqttClient();
    void mqttCallback(char* topic, uint8_t* payload, unsigned int length);
    static void mqttCallback_wrapper(char* topic, uint8_t* payload, unsigned int length);
};

#endif

I’ve been trying variations of this code for a couple of weeks now without success.
Can someone spot what I’m doing wrong here by any chance?
Thank you!
Gustavo.
PS: I do not expect “www.sample.com” to work, I just wanted to simplify my code in this post (so I don’t need to add a byte array to define the mqtt broker ip)

That is a common thing when trying to use a non-static class member.
Since non-static members need to be called in their own context (object instance) you’d need to pass an object pointer along with the non-static member reference to allow the resolution.

To get around this withouth having to create a new overload for mqttCli() that also takes an object pointer, you could declare the callback static (providing it does only use static fields and methods too).
But judging by the looks of it, you may have to implement several overloads that will take a this pointer.

Gus, are you trying to call this MQTT constructor:

MQTT(char* domain, uint16_t port, void (*callback)(char*,uint8_t*,unsigned int));

here:

c_mqttClient::c_mqttClient() {
mqttCli("www.sample.com", uint16_t(1883), mqttCallback_wrapper);

yes, I am trying to call that one

Hey, thanks for taking a look

I think I need to use non-static vars…

maybe that is something that I can try, thanks for the tip.

may you have any material to read, any examples or whatever you think can help me move forward?

thanks again!

Since there is a 1:1 dependency on the MQTT class, have you thought about a derived class?

C++ (11) makes instantiating the base class easy using Delegation. If you plan on using only the one base class constructor, it will make it a lot easier, I would think.

here is an awkward example with two base classes:

class Pig {
  public:
    Pig(){Serial.println("Oink");};
    Pig(int numOinks){for(int i = 0; i < numOinks; i++)Serial.println("Oink");};
    void oink(void){Serial.println("Oink");};
    void poo(){crap();};
  private:
    void crap(){Serial.println("Pig Poo");};
};

class Cow {
  public:
    Cow(){Serial.println("Moo");};
    Cow(int numMoos){for(int i = 0; i < numMoos; i++)Serial.println("Moo");};
    void moo(void){Serial.println("Moo");};
    void poo(){crap();};
  private:
    void crap(){Serial.println("Cow Poo");};
};

class Zoo : public Pig, public Cow {  // Take note of the order here and how it affects the construction of the two base classes
  public:
    Zoo(){};
    Zoo(int numOinks, int numMoos) : Pig(numOinks), Cow(numMoos){}; // constructor may pass args to the base classes
    using Cow::poo;
};


void setup() 
{
  Serial.begin(9600);
  Zoo zoo(3, 1);
  Serial.print(F("Making my zoo Oink: "));
  zoo.oink();
  Serial.print(F("Making my zoo Moo: "));
  zoo.moo();
  Serial.print(F("and if my zoo poos, I get "));
  zoo.poo();
}

void loop() {
  // put your main code here, to run repeatedly:

}

looks promising, thanks for the example!
will take a look later on.

thanks again James

Hi, I was looking at derived classes, but can’t understand how they can help me fix my issue of assigning a callback to a non static member function.
would you mind to elaborate a bit why delegation would help me here?
thanks!

Have you already taken a look at this tutorial? This is what I had in mind.

oh, there I see it… “the callback class”. Perfect! I’ll give it a shot, thanks a bunch!
Gustavo.