2

I am using the arduinoWebSockets library in my own Arduino library. I'm having trouble with trying to assign a class member as the websocket library onEvent callback function.

MyClass.cpp:

#include <WebSocketsClient.h>
#include "Arduino.h"
#include "MyClass.h"

void MyClass::connect(String host, int port) {
  webSocket.begin(host, port, "/");
  webSocket.setReconnectInterval(5000);

  // tried all these variations
  // webSocket.onEvent(incomingEventHandler);
  // webSocket.onEvent(MyClass::incomingEventHandler);
  webSocket.onEvent(this->incomingEventHandler);
}

void MyClass::loop() {
  webSocket.loop();
}

void MyClass::incomingEventHandler(WStype_t type, uint8_t *payload, size_t length) {
  // do stuff with incoming data
  // need to access other member functions and class variables
}

MyClass.h:

#ifndef MyClass_h
#define MyClass_h

#include <WebSocketsClient.h>
#include "Arduino.h"

class MyClass {
  public:
    void connect(String host, int port);
    void loop();
    void incomingEventHandler(WStype_t type, uint8_t *payload, size_t length);
  private:
    WebSocketsClient webSocket;
};

#endif

The error returned is:

sketch/MyClass.cpp: In member function 'void MyClass::connect(String, int)': MyClass.cpp:14:47: error: no matching function for call to 'WebSocketsClient::onEvent()'
webSocket.onEvent(this->incomingEventHandler);


From what I understand it seems like I need to create a pointer to the class member, I have tried a few different ways, this one seemed the most promising, but still failed:

typedef void (MyClass::*MyClassMemFn)(WStype_t type, uint8_t *payload, size_t length);

void MyClass::connect(String host, int port) {
  webSocket.begin(host, port, "/");
  webSocket.setReconnectInterval(5000);

  MyClassMemFn onEventCallback = &MyClass::incomingEventHandler;
  webSocket.onEvent(MyClassMemFn onEventCallback);
}

The above returns this error:

sketch/MyClass.cpp: In member function 'void MyClass::connect(String, int)': MyClass.cpp:15:34: error: expected primary-expression before 'onEventCallback' webSocket.onEvent(MyClassMemFn onEventCallback);


I have also tried to use std::bind:

void MyClass::connect(String host, int port) {
  webSocket.begin(host, port, "/");
  webSocket.setReconnectInterval(5000);

  webSocket.onEvent(std::bind(&MyClass::incomingEventHandler, this));
}

Error:

sketch/MyClass.cpp: In member function 'void MyClass::connect(String, int)': MyClass.cpp:12:47: error: no matching function for call to 'WebSocketsClient::onEvent()'
webSocket.onEvent(this->incomingEventHandler);


If I move the incomingEventHandler function outside the class definition, it works as expected, however then I can't call other class members or access class properties in the same scope.

In arduino c++ how can I pass non-static class member properties as a callback? Is there a better way to approach this?

1
  • 1
    @asked This is actually a question about how C++ works. The magic is in the handling of this. Member functions have a "hidden" parameter. This is necessary to allow access to the member data and virtual function table. The short answer to your question is therefore - a callback cannot be a member function. Commented Sep 12, 2018 at 7:29

1 Answer 1

7

Yes, a callback can be a member function. The error in your std::bind call is that while you do bind the first hidden this paramater to an object instance, in this case this object, you must also use placeholders for all your other function parameters.

Since you declared

void incomingEventHandler(WStype_t type, uint8_t *payload, size_t length)

You must bind as

webSocket.onEvent(std::bind(&MyClass::incomingEventHandler, this,  std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));

Note that this webSocket.onEvent call only works if you are on a non-AVR platform, because the library defines

void WebSocketsClient::onEvent(WebSocketClientEvent cbEvent) {
    _cbEvent = cbEvent;
}

with

#ifdef __AVR__
        typedef void (*WebSocketClientEvent)(WStype_t type, uint8_t * payload, size_t length);
#else
        typedef std::function<void (WStype_t type, uint8_t * payload, size_t length)> WebSocketClientEvent;
#endif

i.e., it is a raw function pointer in __AVR__ but a std::function on non-AVR platforms -- std::bind will give you a std::function. Since you're on an ESP8266, this should work fine.

Refer to https://en.cppreference.com/w/cpp/utility/functional/bind (and maybe my old question)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.