将C++实例方法与C回调函数混合使用

Mixing C++ instance methods with C callback functions

本文关键字:函数 混合 回调 C++ 实例方法      更新时间:2023-10-16

问题

我有来自一个名为Foo:的C库的以下C回调签名

void (* RequestCallbackFunc)(int)

这个库还提供了一个用于注册所述回调的实用函数。

extern void SetRequestCallback(RequestallbackFunc request_cbf);

我有一个带有实例方法HandleRequest的C++类App。当调用request_cbf时,调用HandleRequest的正确方法是什么?

我天真的解决方案

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
}

应用.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回调接口?

回调中int参数的用途是什么?

设计良好的C接口API通常提供一种将"status"参数传递给C函数回调的方法(可以用于传递一些信息,如C++类实例this指针),如CreateThread的LPVOID lpParameter

为什么要使用指针?

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

Bar也是如此),但老实说,你也可以直接调用Bar中的方法(除非需要通过A重定向)

或者,不是一个自由函数,而是传递一个指向A中匹配相同签名的静态函数的指针-在这个指针中,您可以访问A的单个实例来使用。。。

顺便说一句。一些api使用一种模式,在注册期间,您可以传入指向某些用户数据的指针,这些数据在回调中传递,例如,这可以是指向A实例的指针来处理回调-这样您就可以避免所有的单例业务。。。

我会创建另一个类,它可以静态访问,并注册不同的对象来管理回调。要使用的对象可以由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);
}

有了这种设计,你就有了极大的灵活性。Middleman的映射可以由一个单独的处理程序代替,该处理程序使用相同的registerNewHandler方法进行注册。

尝试使用cify技巧并尝试向C提供lambda对象,不过,您需要小心:

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

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