I have been trying to pass a callback to the setConnectCallback() function in the Bluefruit Library. When I pass the function names connect_callback into setConnectCallback()
I am getting the error invalid use of non-static member function of type 'void (AumeBluetooth::)()'
The function setConnectCallback() looks like it is asking for a function pointer:
exerpt from Adafruit_BLE Arduino Library:
/******************************************************************************/
/*!
#brief Set handle for connect callback
#param[in] fp function pointer, NULL will discard callback
*/
/******************************************************************************/
void Adafruit_BLE::setConnectCallback( void (*fp) (void) )
{
this->_connect_callback = fp;
install_callback(fp != NULL, EVENT_SYSTEM_CONNECT, -1);
}
I have a class "AumeBluetooth" defined as such, which I attempted to implement a function pointer to call connect_callback:
.h
class AumeBluetooth {
public:
bool isConnected = false;
Adafruit_BluefruitLE_SPI *_ble;
void error(const __FlashStringHelper*err);
void begin();
AumeBluetooth();
void loop();
void connect_callback(void);
};
.cpp
#include "AumeBluetooth.h"
#include <SPI.h>
#include "Adafruit_BLE.h"
#include "Adafruit_BluefruitLE_SPI.h"
#include "Adafruit_BluefruitLE_UART.h"
#include "BluefruitConfig.h"
AumeBluetooth::AumeBluetooth() {
}
void AumeBluetooth::begin() {
isConnected = false;
Adafruit_BluefruitLE_SPI ble(BLUEFRUIT_SPI_CS, BLUEFRUIT_SPI_IRQ, BLUEFRUIT_SPI_RST);
_ble = &ble;
if ( !_ble->begin() )
{
error(F("Couldn't find Bluefruit, make sure it's in CoMmanD mode & check wiring?"));
}
_ble->echo(false);
_ble->info();
_ble->setMode(BLUEFRUIT_MODE_DATA);
void (AumeBluetooth::*cc)(void) = &AumeBluetooth::connect_callback;
ble.setConnectCallback(this->*cc);
}
void AumeBluetooth::connect_callback(void) {
Serial.print("BLUETOOTH IS CONNECTED");
isConnected = true;
}
}
Not sure what to do try next. Thanks!
setConnectCallback is looking for a static function pointer. As the error message says, you are passing it a non-static function pointer.
Your callback function must be a static function - either a free function, or a class function that is specifically designated 'static' and therefore has no access to class variables.
This is a tricky API, because it also looks like the function parameter list is (void), which means you don't have a way to pass in an index or a pointer to tie it to a class instance. You only get one callback to a static function, and it is up to your code to know which class instance the callback might be for.
So, your connect_callback function won't be able to set a class variable isConnected inside the callback. You will only be able to access global/static variables.
I would expect the begin() and loop() function calls also to be static, non-class functions. It looks like maybe you are trying to put a class wrapper around code that doesn't have to be a class.
Related
I'd like to have my class take a function "the callback stub", add some stuff that processes before and after the stub, and then pass the whole thing as a callback to an interrupt. Different stubs could be attached to different instances of the class.
I'm getting errors as shown in the comments below. It works to take a function pointer, store it as an attribute, and then pass it to the attachInterrupt function. However, it doesn't work to add the little prelude.
In setInterrupt(), I get a pointer to a bound function may only be used to call the functionC/C++(300)
altSetInterrupt() however compiles fine.
Question: Is there a combination of * & or -> that can make this work?
EDITED: to remove a silly mistake as pointed out by Jerry Jeremiah
#include <Arduino.h>
#define MYPIN 1
// Declarations
class myClass {
public:
myClass();
void setInterrupt();
void altSetInterrupt();
void setCallBackStub( void (*cb)());
private:
void (*myCallBackStub)();
void myCallBack();
};
void mySillyCB();
// Definitions
myClass::myClass():
myCallBackStub(nullptr)
{}
void myClass::setCallBackStub( void (*cb)()) {
myCallBackStub = cb;
}
void myClass::myCallBack() {
Serial.println("Wohoo");
myCallBackStub();
}
void myClass::setInterrupt() {
attachInterrupt(MYPIN, this->myCallBack, HIGH); // a pointer to a bound function may only be used to call the functionC/C++(300)
}
void myClass::altSetInterrupt() {
attachInterrupt(MYPIN, this->myCallBackStub, HIGH); // no compiler error here
}
void mySillyCB() {
Serial.println("Called Back");
}
// Code
myClass theThing;
void setup() {
theThing.setCallBackStub( mySillyCB);
theThing.setInterrupt();
}
void loop() {}
In myCallBack(), I get identifier "myCallBackStub" is undefinedC/C++(20)
You have defined this as a free function rather than implementing a method. You have:
void myCallBack() {
But you should have:
void myClass::myCallBack() {
Because of this, there is no such name myCallBackStub in scope.
In setInterrupt(), I get a pointer to a bound function may only be used to call the functionC/C++(300)
this->myCallBack is not a first-class value in C++; it is a member function that is bound to a particular instance (*this). The only thing you can do with this is invoke it (like this->myCallBack()).
Notably, you cannot convert it to a function pointer. This is because non-static member functions need an instance on which to invoke the function, and a free function pointer (what attachInterrupt accepts) has no additional mechanism to convey this state. Even if you used the correct syntax for a pointer-to-member-function (&myClass::myCallBack) this still would not work since you cannot convert between pointer-to-function and pointer-to-member-function.
You can pass this->myCallBackStub because this is actually a function pointer value.
Since attachInterrupt() state is global anyway, you don't gain a whole lot by not making your mechanism global. The primary challenge you are running into is that the callback function accepts no arguments so you can't even tell which pin/mode was used.
To get around this, you need a different non-member callback function per pin/mode combination, and these must be known in advance at compile time, since you (presumably) don't want to get into the complexity of emitting trampolines at runtime.
This can be done at compile time with the use of macros. Each invocation of the macro should do several things:
Declare a global callback function.
Declare a global vector of callback function objects that will be invoked by the global callback function.
Possibly invoke attachInterrupt() depending on whether this can be done during static initialization.
Here is a sample implementation of this concept, which may need tweaking to be useful in production. You would create a header like interrupts.hpp with the following:
#include <functional>
#include <vector>
// These types may need adjusting to match Arduino's types.
using Pin_t = int;
using Mode_t = int;
using InterruptCallback_t = std::function<void()>;
using InterruptCallbackList_t = std::vector<InterruptCallback_t>;
using RawInterruptCallback_t = void (*)();
#define DECLARE_INTERRUPT(pin, mode) DECLARE_INTERRUPT_(pin, mode)
#define DECLARE_INTERRUPT_(pin, mode) \
void add_interrupt_callback_##pin##_##mode (InterruptCallback_t);
DECLARE_INTERRUPT(0, 0)
// And so on...
Then in the implementation file interrupts.cpp:
#include <utility>
#include "interrupts.hpp"
static void apply_interrupt_callback(InterruptCallbackList_t const & cbList) {
for (const auto & cb : cbList) {
cb();
}
}
#define IMPLEMENT_INTERRUPT(pin, mode) IMPLEMENT_INTERRUPT_(pin, mode)
#define IMPLEMENT_INTERRUPT_(pin, mode) \
static InterruptCallbackList_t generated_interrupt_callbacks_##pin##_##mode ; \
static void generated_interrupt_callback_##pin##_##mode() { \
apply_interrupt_callback( generated_interrupt_callbacks_##pin##_##mode ); \
} \
void add_interrupt_callback_##pin##_##mode (InterruptCallback_t cb) { \
( generated_interrupt_callbacks_##pin##_##mode ).emplace_back(std::move(cb)); \
} \
static int generated_interrupt_registration_##pin##_##mode = ( \
attachInterrupt(pin, generated_interrupt_callback_##pin##_##mode, mode), \
0 \
);
IMPLEMENT_INTERRUPT(0, 0)
// And so on...
This will generate a set of functions and global variables for each pin/mode combination you provide. The interface provided by interrupts.hpp will expose add_interrupt_callback_N_M where N and M are the pin and mode constants, respectively. You can pass any function or functor (with the help of std::function<void()> to this function and it will be called when the interrupt occurs.
Note that there is no mutex in this example code. For the sake of simplicity, I assume a single-threaded environment, where an interrupt callback cannot occur preemptively. The code will need to be changed if these assumptions are not true.
I'm trying to wrap, in a C++ class, a server that I wrote using mongoose (a C library). The problem is that I'm trying to pass the function ev_handler to the mg_create_server(), which create the instance of the server in mongoose. But it gives a casting error I believe:
src/Server.cpp:16:44: error: cannot convert 'Server::ev_handler' from
type 'int (Server::)(mg_connection*, mg_event)' to type 'mg_handler_t
{aka int (*)(mg_connection*, mg_event)}' server =
mg_create_server(NULL, ev_handler);
I tried to make ev_handler static but it has send_index_page(conn) that has to be inside the wrapper class.
void Server::start() {
struct mg_server *server;
int numberOfObjects;
_application = new Application();
_application->start();
// Create and configure the server
server = mg_create_server(NULL, ev_handler);
//... more code here ...
}
int Server::ev_handler(struct mg_connection *conn, enum mg_event ev) {
switch (ev) {
case MG_AUTH: return MG_TRUE;
case MG_REQUEST: return send_index_page(conn);
default: return MG_FALSE;
}
}
Your problem is that you're passing a C++ member function to parameter that wants a free function pointer.
Mongoose is a C API and all of its callback parameters are C style functions, which in C++ are free (not member) functions.
A member function pointer is different from a free function pointer in that it needs the this , or the object on which the method is being called, in order to be called.
In your case, you are passing a member function pointer on the Server class.
When interacting which C APIs, it's common to pass a void* context object which is then passed to the callback. You then pass a pointer to a free function or a static class method (which has no this and can therefore work with C APIs). When the callback is invoked, you then cast the context object to the correct type and call a member function to get back into the object context. I can't see any such facility in Mongoose. Maybe it's there and I'm just not finding it.
You may want to try the already exising Mongoose C++ which forks the original Mongoose project to work better with C++: https://github.com/Gregwar/mongoose-cpp
The callback needs to be static, then you should use a static stub to redirect to the class instance.
Storing the instance of your class in server_param attribute of mg_server will allow to get it back in a static stub and forward it to this instance.
This could be achieve like this :
class Server
{
public:
void start() {
mg_create_server(this, ev_handlerStub);
}
static int ev_handlerStub(struct mg_connection *conn, enum mg_event ev) {
((Server*)conn->server_param)->ev_handler(conn, ev);
}
int ev_handler(struct mg_connection *conn, enum mg_event ev) {
// job to do with the class instance
}
};
Proceeding like this, allow access to class instance inside its ev_handler method.
I am very new to the boost libraries.
I was trying to accomplish something for a graphical program, by binding the callbacks passed
to glutDisplayFunc(), etc to a single class.
I wanted to accomplish this without having some constant global class object.
To explain in code:
class CallbackHolder {
public:
void dostuff(void) {
// etc.
}
};
void bind() {
glutIdleFunc((new CallbackHolder())->dostuff);
}
I know this is possible through the usage of boost::bind and boost::function.
One issue I did see however was converting the boost::function back to a normal function pointer.
How would you accomplish this?
You can't convert from boost::function to a normal function pointer, and you can't convert from a member function pointer to a normal function pointer. There are workarounds for functions accepting callback where you can provide user data.
Unfortunately the glut interface doesn't let you provide user data. This means you're stuck with the ugliest solution, using a global variable and a normal function.
class CallbackHolder {
public:
void dostuff(void) {
// etc.
}
};
CallbackHolder * g_callbackHolder = NULL;
void call_callback_holder(void) {
if(g_callbackHolder) g_callbackHolder->dostuff();
}
void bind() {
g_callbackHolder = new CallbackHolder();
glutIdleFunc( &call_callback_holder );
}
I'm using an API that requires me to pass a function pointer as a callback. I'm trying to use this API from my class in C++ but I'm getting compilation errors.
The API definition is:
typedef void (__stdcall *STREAM_CALLBACK)(void *userdata);
__declspec(dllimport) int __stdcall set_stream_callback(
STREAM_CALLBACK streamCB, void *userdata);
One example file, provided by the third party, is:
void __stdcall streamCB(void *userdata)
{
// callback implementation
}
int main(int argc, const char argv[])
{
int mid = 0;
set_stream_callback(streamCB, &mid);
}
And that works fine.
However when I try to use that in a class, I have an error:
error C3867: 'MyClass::streamCB': function call missing argument list;
use '&MyClass::streamCB' to create a pointer to member
The suggestion to use
&MyClass::streamCB
doesn't work.
I understood that the set_stream_callback only accepts a non-member function.
The problem is very similar to
How can I pass a class member function as a callback?
in which Johannes makes a concise suggestion, however I do not understand it very well. Could anyone expand a bit, if I am correct that it is relevant to this question?
I have tried:
void __stdcall MyClass::streamCB(void *userdata)
{
// callback implementation
}
static void MyClass::Callback( void * other_arg, void * this_pointer ) {
MyClass * self = static_cast<ri::IsiDevice*>(this_pointer);
self->streamCB( other_arg );
}
//and in the constructor
int mid = 0;
set_stream_callback(&MyClass::Callback, &mid);
But
error C2664: 'set_stream_callback' : cannot convert parameter 1 from
'void (__cdecl *)(void *,void *)' to 'STREAM_CALLBACK'
How do I get around this?
Edit1: Also, I want to use userdata inside the streamCB callback.
The idea of calling a member function from a callback taking only non-member functions is to create a wrapper for you member function. The wrapper obtains an object from somewhere and then calls the member function. If the callback is reasonably well designed it will allow you to pass in some "user data" which you'd use to identify your object. You, unfortunately, left out any details about your class so I'm assuming it looks something like this:
class MyClass {
public:
void streamCB() {
// whatever
}
// other members, constructors, private data, etc.
};
With this, you can set up your callback like so:
void streamCBWrapper(void* userData) {
static_cast<MyClass*>(userData)->streamCB()
}
int main() {
MyClass object;
set_stream_callback(&streamCBWrapper, &object);
// ...
}
There are various games you can play with how to create the streamCBWrapper function (e.g., you can make it a static member of your class) but all come down to the same: you need to restore your object from the user data and call the member function on this object.
You can achieve what you want to do by turning the userdata into a property of MyClass. Then you don't have to pass it to MyClass::Callback, which would be impossible, since you can only pass one parameter, and it would be the object instance.
Here's an example.
void __stdcall MyClass::streamCB()
{
// callback implementation
}
static void MyClass::Callback(void * this_pointer ) {
MyClass * self = static_cast<MyClass>(this_pointer);
self->streamCB();
}
MyClass::MyClass(void *userdata) {
// do whatever you need to do with userdata
// (...)
// and setup the callback at C level
set_stream_callback(&MyClass::Callback, (void *)this);
}
In your example, the int mid variable would become a property of that class, and thus be accessible from the callback implementation streamCB.
I would like to access the data within this member function that is static. Right now the member function is static so that I can use it with a third party API written in C that has typdef function pointer for callback purposes. Based on the info below, what is the best way to get around the need to create a static function in order to use the data from the following function member within other member functions of my class that are non-static. Maybe there is a way to still use this static function but still overcome the inability to mix static with non-static variables. My code does works as is but with no ability to access the data in the following callback function.
void TextDetect::vtrCB(vtrTextTrack *track, void *calldata) /*acts as a callback*/
{
/*specifically would like to take data from "track" to a deep copy so that I don't loose scope over the data withing that struct */
}
In an associated API written in C, there are the following two lines of code that I am forced to use:
typedef void (*vtrCallback)(vtrTextTrack *track, void *calldata);
int vtrInitialize(const char *inifile, vtrCallback cb, void *calldata);
Here is the header of my class:
#include <vtrapi.h>
#include <opencv.hpp>
class TextDetect {
const char * inifile;
vtrImage *vtrimage;
int framecount;
public:
TextDetect();
~TextDetect();
static void vtrCB(vtrTextTrack *track, void *calldata);
int vtrTest(cv::Mat);
bool DrawBox(cv::Mat&);
};
TextDetect::TextDetect() : inifile("vtr.ini")
{
if (vtrInitialize(inifile, vtrCB /*run into problems here*/, NULL) == -1)
std::cout << "Error: Failure to initialize" << std::endl;
vtrimage = new vtrImage;
framecount = 0;
}
void TextDetect::vtrCB(vtrTextTrack *track, void *calldata) /*acts as a callback*/
{
/*specifically would like to take data from "track" to a deep copy so that I don't loose scope over the data withing that struct */
}
I am not sure I understand your precise situation, but here is the standard idiom for wrapping a C++ method into a C callback API:
/*regular method*/
void TextDetect::vtrCB(vtrTextTrack *track)
{
// do all the real work here
}
/*static method*/
void TextDetect::vtrCB_thunk(vtrTextTrack *track, void *data)
{
static_cast<TextDetect *>(data)->vtrCB(track);
}
and then, assuming the function that should call vtrInitialize is also a TextDetect method, you write the call like this:
vtrInitialize(inifile, TextDetect::vtrCB_thunk, static_cast<void *>(this));