Reputation: 193
I'm using a static class called websocket
which listens on a websocket (hence the name) and calls a callback function once data is received. I created the following declarations
typedef int (*cb)(Json::Value &json);
static void websocket::init();
static void websocket::listen(cb callback, const char* address);
I also have a class called app
which should contain the callback as a member function, because it is using a member property connecting to the database:
int app::callback(Json::Value& json)
{
this->db->insert(json);
};
I'm calling the websocket::listen()
function inside app
like this:
void app::start()
{
websocket::listen(std::mem_fn(&app::callback), this->path);
}
I get an error saying
error: cannot convert 'std::_Mem_fn<int (app::*)(Json::Value&)>' to 'cb' {aka 'int (*)(Json::Value&)'}
. To be able to keep the code as generic as possible so that I can call websocket::listen();
with other callbacks as well, I don't want to change the cb typedef.
I can also not make app::callback
static as it requires this->db
. What is the best way to deal with this problem?
Upvotes: 2
Views: 1322
Reputation: 51956
There are two problems:
std::mem_fn(&app::callback)
takes an instance of app
as the first parameter.[this] (Json::Value& json) { return callback(json); }
but these cannot be converted to function pointers, which makes implementing generic containers for storing and dynamically dispatching to them extremely difficult to do.One possible approach is to amend your websocket
interface and accept an optional void*
context parameter like this:
using cb = int(*)(Json::Value &json, void* ctx);
static void websocket::listen(cb callback, const char* address, void* ctx = nullptr)
{
...
}
int app::callback(Json::Value& json)
{
return db->insert(json);
}
void app::start()
{
websocket::listen([] (Json::Value &json, void* ctx) -> int {
return static_cast<app*>(ctx)->callback(json);
}, path, this);
}
If you prefer more type safety for this erasure, you can consider std::any
:
using cb = int(*)(Json::Value &json, std::any ctx);
static void websocket::listen(cb callback, const char* address, std::any ctx = {})
{
...
}
int app::callback(Json::Value& json)
{
return db->insert(json);
}
void app::start()
{
websocket::listen([] (Json::Value &json, const std::any& ctx) -> int {
return std::any_cast<app*>(ctx)->callback(json);
}, path, this);
}
The std::any_cast
will throw a std::bad_any_cast
if ctx
was empty or storing a pointer that wasn't a type erased app*
.
Upvotes: 1