Pubsubclient: Allowing use of lambda expressions in callbacks on ESP32

Created on 31 May 2018  路  8Comments  路  Source: knolleary/pubsubclient

Can you please add ESP32 support!

#if defined(ESP8266) || defined(ESP32)
#include <functional>
#define MQTT_CALLBACK_SIGNATURE std::function<void(char*, uint8_t*, unsigned int)> callback
#else
#define MQTT_CALLBACK_SIGNATURE void (*callback)(char*, uint8_t*, unsigned int)
#endif
enhancement question

Most helpful comment

I use pubsubclient as a member of a class.

class MyClass
{
  protected:
    PubSubClient client;
    void memberCallback(char *, uint8_t *, unsigned int);
};

I want to pass a member function to pubsubclient.

inline MyClass::MyClass()
{
    client = PubSubClient(espClient);
    client.setCallback([this](char *t, byte *p, unsigned int l) { memberCallback(t, p, l); });
}

I am calling a member function via anonymus function. It works with lambdas because I can share the current instance's context ([this]) with anonym function. The other way around member function is out of scope.
std::functions makes it possible which is available on ESP platforms.
My code, based on your lib, perfectly works with ESP8266. would love to try on ESP32.

Solution

(based on TaskScheduler library by @arkhipenko):
switchable std::functions via macros for ESP8266 and ESP32

//header

#if !defined (ARDUINO_ARCH_ESP8266) && !defined (ARDUINO_ARCH_ESP32)
#ifdef CALLBACK_STD_FUNCTION
    #error Support for std::function only for ESP8266 or ESP32 architecture
#undef CALLBACK_STD_FUNCTION
#endif // CALLBACK_STD_FUNCTION
#endif // ARDUINO_ARCH_ESP8266

//impl

#ifdef CALLBACK_STD_FUNCTION
#include <functional>
typedef std::function<void(char *, uint8_t *, unsigned int)> MQTT_CALLBACK_SIGNATURE;
#else
typedef void (*MQTT_CALLBACK_SIGNATURE)(char *, uint8_t *, unsigned int);
#endif

Your current code already uses <functional> for ESP8266. To avoid breaks simply add:

#ifdef ARDUINO_ARCH_ESP8266
#define CALLBACK_STD_FUNCTION
#endif

In this case I recommend you to immediately sign it depricated, because this hack is just ugly af.

Did I clarify properly the need and advantages of this feature? If not, happy to answer!

Thanks, Marci

All 8 comments

I tried this lib on ESP32 this week, seems to work...

That will change the callback signature for the ESP32 - if, as per @TLS1000 comment, the other fallback function works, then changing the signature will break existing users. That is something I try to avoid.

I'm not familiar with this principle of 'callback signature'... But I've tried the lib on ESP32, connected to Mosquitto running on raspberry. The esp could publish to a topic, and receive a message(published by mosquito client).

I'm using lambdas within classes, this is why I need callbacks this way.
Can u maybe add a macro option - to make it configurable - as @arkhipenko did in his TaskScheduler library:
https://github.com/arkhipenko/TaskScheduler/blob/master/src/TaskScheduler.h#L158

Maybe this issue could do with a better title, something like "allowing use with lambdas within classes"? (not sure if that's a useful suggestion, but it seems to be about something more specific than "ESP32 support")

Just to add another data point, general usage seems to work fine with ESP32, I've been happily using it with both ESP32 and MKR1000 boards recently.

@amcewen
Valid! Thanks, just updated title.
Yes, general usage works fine, but not reallz useful for advanced solutions.
@knolleary
My suggestions allows both. This way existing users dont break, also there is an option to extend functionality.

Sorry, I'm really out of touch with esp development these days and my C/C++ simply isn't good enough to understand at a glance the full consequences of your proposal.

For a user already running this library on esp32, will this change effect them? Will they have to change anything? What does this allow that can't be done today?

I use pubsubclient as a member of a class.

class MyClass
{
  protected:
    PubSubClient client;
    void memberCallback(char *, uint8_t *, unsigned int);
};

I want to pass a member function to pubsubclient.

inline MyClass::MyClass()
{
    client = PubSubClient(espClient);
    client.setCallback([this](char *t, byte *p, unsigned int l) { memberCallback(t, p, l); });
}

I am calling a member function via anonymus function. It works with lambdas because I can share the current instance's context ([this]) with anonym function. The other way around member function is out of scope.
std::functions makes it possible which is available on ESP platforms.
My code, based on your lib, perfectly works with ESP8266. would love to try on ESP32.

Solution

(based on TaskScheduler library by @arkhipenko):
switchable std::functions via macros for ESP8266 and ESP32

//header

#if !defined (ARDUINO_ARCH_ESP8266) && !defined (ARDUINO_ARCH_ESP32)
#ifdef CALLBACK_STD_FUNCTION
    #error Support for std::function only for ESP8266 or ESP32 architecture
#undef CALLBACK_STD_FUNCTION
#endif // CALLBACK_STD_FUNCTION
#endif // ARDUINO_ARCH_ESP8266

//impl

#ifdef CALLBACK_STD_FUNCTION
#include <functional>
typedef std::function<void(char *, uint8_t *, unsigned int)> MQTT_CALLBACK_SIGNATURE;
#else
typedef void (*MQTT_CALLBACK_SIGNATURE)(char *, uint8_t *, unsigned int);
#endif

Your current code already uses <functional> for ESP8266. To avoid breaks simply add:

#ifdef ARDUINO_ARCH_ESP8266
#define CALLBACK_STD_FUNCTION
#endif

In this case I recommend you to immediately sign it depricated, because this hack is just ugly af.

Did I clarify properly the need and advantages of this feature? If not, happy to answer!

Thanks, Marci

Was this page helpful?
0 / 5 - 0 ratings