Function pointer with extra data
I have class which handles packages:
typedef void (*FCPackageHandlerFunction)(FCPackage*);
class FCPackageHandlers{
...
void registerHandler(FCPackage::Type type, FCPackageHandlerFunction handler);
void handle(FCPackage* package);
...
QHash<FCPackage::Type, FCPackageHandlerFunction> _开发者_JS百科handlers;
};
Then I have a server class who receive packages. Now I want to register a function who handles the packages. But this function must have a instance of the server for other variables.
So i try this:
struct FCLoginHandler{
FCServer* server;
FCLoginHandler(FCServer* server){
this->server = server;
}
void operator()(FCPackage* package){
std::cout << "Received package: " << package->toString().data() << "\n";
}
};
...
FCServer::FCServer(){
_handlers.registerHandle(FCPackage::Login, FCLoginHandler(this));
}
But then I get this error:
error: no matching function for call to 'FCPackageHandlers::registerHandler(FCPackage::Type, FCLoginHandler)'
note: candidates are: void FCPackageHandlers::registerHandler(FCPackage::Type, void (*)(FCPackage*))
Does anybody know the right solution?
You are trying to store a function object in a function pointer, and that's not possible. You should store a std::tr1::function
instead:
#include <functional>
typedef std::tr1::function<void(FCPackage*)> FCPackageHandlerFunction;
class FCPackageHandlers{
...
void registerHandler(FCPackage::Type type, FCPackageHandlerFunction handler);
void handle(FCPackage* package);
...
QHash<FCPackage::Type, FCPackageHandlerFunction> _handlers;
};
There's a similar function
class in Boost in case you don't have access to std::tr1 yet.
Also, consider using boost::bind
and a regular function to avoid the boilerplate of having to create your own function objects such as FCLoginHandler
:
void handle_FC_login(FCServer* server, FCPackage* package)
{
std::cout << "Received package: " << package->toString().data() << "\n";
// You can use server if you need it
}
FCServer::FCServer()
{
_handlers.registerHandle(FCPackage::Login,
std::tr1::bind(&handle_FC_login, this, _1));
}
std::tr1::bind
is also available in Boost, and if you don't have access to that either you can always use std::bind2nd
.
EDIT: Since you can't modify the type of FCPackageHandlerFunction
, your best shot might be to add another hash that stores data associated to each function pointer:
typedef void (*FCPackageHandlerFunction)(FCPackage*);
class FCPackageHandlers{
...
void registerHandler(FCPackage::Type type, FCPackageHandlerFunction handler,
FCServer * func_data);
void handle(FCPackage* package);
...
QHash<FCPackage::Type, FCPackageHandlerFunction> _handlers;
QHash<FCPackageHandlerFunction, FCServer*> _handler_func_data;
};
// The server will be passed by the package handler which will
// extract it from the _handler_func_data hash
void handle_FC_login(FCServer* server, FCPackage* package)
{
std::cout << "Received package: " << package->toString().data() << "\n";
}
FCServer::FCServer(){
_handlers.registerHandle(FCPackage::Login, &handle_FC_login, this );
}
Presumably this is how you'll have to implement FCPackageHandlers::handle
:
void FCPackageHandlers::handle(FCPackage * package)
{
// Query function
FCPackageHandlerFunction func = _handlers[package->GetType()];
// Query associated data
FCServer * server = _handler_func_data[func];
// Call handler
func(server, package);
}
精彩评论