Imroy / pubsubclient

A client library for the ESP8266 that provides support for MQTT
MIT License
433 stars 115 forks source link

Using an instance method as callback. #59

Closed MichMich closed 8 years ago

MichMich commented 8 years ago

First of all, thanks for the awesome library. It really helps me in the development of my MQTT projects.

I'm currently trying to use an instance method as the callback, but unfortunately, the set mqtt.set_callback(mqttCallback) method expects a general function. mqtt.set_callback(this.mqttCallback) or something like mqtt.set_callback(MyObj::mqttCallback, this) does not work.

Do you know a way around this, without making my instance static?

av1024 commented 8 years ago

Look at std:bind:

#include<functional>
mqtt.set_callback(std::bind(&MyClass::myMethod, MyClassInstance))

//Or lambda:

set_callback([]() { return myInstance.get(); })
MichMich commented 8 years ago

Thanks for the info. I tried the std::bind route:

mqtt.set_callback(std::bind(&DataManager::mqttCallback, this));

Where DataManager is my class. And this refers to the instance.

But I get the following compiler error:

src/DataManager.cpp:49:67: error: no matching function for call to 'PubSubClient::set_callback(std::_Bind_helper<false, void (DataManager::)(const MQTT::Publish&), DataManager const>::type)'

Any idea?

av1024 commented 8 years ago

I'm not guru in c++ :( do just an idea.

May be wrong definition of mqttCallback.

And at least wrong usage: You shouldn't pass 'this' to std::bind, but instance of global variable. There is a way to store this pointer in lambdas (http://en.cppreference.com/w/cpp/language/lambda) but I tried with no success (and looks like it is very similar with passing global).

As an a way:

For now with my meteo sensor I use following syntax:

sens[0].setReadFn([] () { return _bmp.readTemperature(); } ); //less mem usage, but equivalent to: std::bind(&BME280::readTemperature, _bmp));

UPD: btw, look at pubsubclient.h - there is ifdef switching between functional and plain syntax - _GLIBC_FUNCTIONAL

MichMich commented 8 years ago

Tried using a lambda (I'm not a real C++, programmer, but is seems to look a lot like closures which other languages use.) This solved the issue. The following works:

// define the callback method for the object
void DataManager::mqttCallback(const MQTT::Publish& pub) {
    Serial.print("Received data: ");
    Serial.println(pub.payload_string());
}
// register the callback using the lambda
mqtt.set_callback([this](const MQTT::Publish& pub) {
    this->mqttCallback(pub);
});

Of course, the above two could also be combined to.

mqtt.set_callback([](const MQTT::Publish& pub) {
    Serial.print("Received data: ");
    Serial.println(pub.payload_string());
});

(Note the tiny difference that it doesn't capture this between the [] since this isn't used in the lambda anyway.)

So with this, the issue is solved. Thanks for your help!