Указатель на функцию с дополнительными данными - PullRequest
2 голосов
/ 09 февраля 2010

У меня есть класс, который обрабатывает пакеты:

typedef void (*FCPackageHandlerFunction)(FCPackage*);
class FCPackageHandlers{
    ...
    void registerHandler(FCPackage::Type type, FCPackageHandlerFunction handler);
    void handle(FCPackage* package);
    ...
    QHash<FCPackage::Type, FCPackageHandlerFunction> _handlers;
};

Тогда у меня есть серверный класс, который получает пакеты. Теперь я хочу зарегистрировать функцию, которая обрабатывает пакеты. Но эта функция должна иметь экземпляр сервера для других переменных.

Итак, я попробую это:

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));
}

Но тогда я получаю эту ошибку:

error: no matching function for call to 'FCPackageHandlers::registerHandler(FCPackage::Type, FCLoginHandler)'
note: candidates are: void FCPackageHandlers::registerHandler(FCPackage::Type, void (*)(FCPackage*))

Кто-нибудь знает правильное решение?

1 Ответ

2 голосов
/ 09 февраля 2010

Вы пытаетесь сохранить объект функции в указателе функции, но это невозможно. Вы должны хранить std::tr1::function вместо:

#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;
};

Существует похожий класс function в Boost, если у вас еще нет доступа к std :: tr1.

Также рассмотрите возможность использования boost::bind и обычной функции, чтобы избежать необходимости создавать собственные функциональные объекты, такие как 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 также доступен в Boost, и если у вас нет к нему доступа, вы всегда можете использовать std::bind2nd.

РЕДАКТИРОВАТЬ: Поскольку вы не можете изменить тип FCPackageHandlerFunction, лучше всего добавить еще один хеш, который хранит данные, связанные с каждым указателем функции:

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 );
}

Предположительно, это то, как вам нужно реализовать 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);
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...