Смешивание методов экземпляра C ++ с функциями обратного вызова C - PullRequest
3 голосов
/ 18 января 2012

проблема

У меня есть следующая сигнатура обратного вызова C из библиотеки C с именем Foo:

void (* RequestCallbackFunc)(int)

Эта библиотека также предоставляет служебную функцию для регистрации указанного обратного вызова.

extern void SetRequestCallback(RequestallbackFunc request_cbf);

У меня есть класс C ++ App с методом экземпляра HandleRequest. Как правильно вызвать HandleRequest при вызове request_cbf?

мое наивное решение

App.hpp

#include <Foo.h> // include my C library

#include <Bar.hpp>

class App {
    public:
        App();
        ~App();

        void HandleRequest(int x);
    public:
        Bar * bar_; // raw pointer for demonstration purposes
}

App.cpp

#include "App.hpp"

extern "C" {
    static void handle_request(int x);
}

static void handle_request(int x) {
    static std::auto_ptr<App> my_app( new App() );
    my_app->HandleRequest(x);
}

App::App() {
    SetRequestCallback( handle_request );
    bar_ = new Bar();
}

App::~App() {
    delete bar_;
}

void App::HandleRequest(int x) {
    bar_->DoSomething( x );
    // more 'work'...
}

Правильно ли я подхожу к этой проблеме? Существуют ли дополнительные способы взаимодействия C ++ с обратными вызовами C?

Ответы [ 5 ]

2 голосов
/ 18 января 2012

Какова цель этого параметра int в обратном вызове?

Хорошо разработанные API-интерфейсы интерфейса C обычно предоставляют способ передачи параметра «status» в обратный вызов функции C (который можно использовать для передачи некоторой информации, например, указателя экземпляра класса C ++ this указатель), например CreateThread х LPVOID lpParameter.

0 голосов
/ 04 февраля 2015

Попробуйте использовать трюк cify и попытайтесь предложить лямбда-объект C, но вам нужно быть осторожным:

https://codereview.stackexchange.com/questions/79612/c-ifying-a-capturing-lambda

0 голосов
/ 18 января 2012

зачем использовать указатели?

static void handle_request(int x) {
    static A instance;
    instance.HandleRequest(x);
}

(То же самое относится и к Bar), хотя, если честно, вы можете также вызвать метод в Bar напрямую (если перенаправление через A

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

кстати.некоторые API используют шаблон, где во время регистрации вы можете передать указатель на некоторые пользовательские данные, которые передаются в обратном вызове, это может быть, например, указатель на экземпляр A для обработки обратного вызова - таким образом вы можете избежатьвесь синглтон бизнес ...

0 голосов
/ 18 января 2012

Я бы создал другой класс, к которому можно обращаться статически, и который регистрирует различные объекты для управления обратным вызовом. Используемый объект может быть определен параметром x (или, если нет необходимости, вы можете зарегистрировать один обработчик).

// Pseudocode
class Middleman {
    // map to select the handler object
    std::map<int, App> handlersMap_;

public:

    registerNewHandler(int x, const App& a) {
        // add new entry to the map
        [...]
    }

    static std::map<int, App> getHandlersMap() {
        return handlersMap_;
    }
}

Управление обратным вызовом:

static void handle_request(int x) {
    Middleman::getHandlersMap[x].HandleRequest(x);
}

С этим дизайном у вас есть большая гибкость. Карта посредника может быть заменена одним обработчиком, который регистрируется с использованием того же метода registerNewHandler.

0 голосов
/ 18 января 2012
static void handle_request(int x) {
    static std::auto_ptr<App> A( new A() );
    A->HandleRequest(x);
}

Это своего рода синглтон.Так как это анти-шаблон, следовательно, это плохо.

Я бы создал функцию и глобальную переменную или тип App.Примерно так:

//App.hpp
#include <Foo.h> // include my C library

#include <Bar.hpp>

class App {
    public:
        App();
        ~App();

        void HandleRequest(int x);
    public:
        Bar * bar_; // raw pointer for demonstration purposes
}

extern App app;
void HandlerRequest(int x);

Тогда реализация:

//App.cpp
#include "App.hpp"

App::App() : bar(new Bar) {
    SetRequestCallback( handle_request );
}

App::~App() {
    delete bar_;
}

void App::HandleRequest(int x) {
    bar_->DoSomething( x );
    // more 'work'...
}
App app;
void HandlerRequest(int x)
{
  app.HandlerRequest(x);
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...