2012-01-17 75 views
2

问题

我已经从C库下面的C回调签名称为Foo实例方法:混合使用C++使用C回调函数

void (* RequestCallbackFunc)(int) 

该库还提供了一个效用函数为注册说回调。

extern void SetRequestCallback(RequestallbackFunc request_cbf); 

我有一个C++ App类与实例方法HandleRequest。调用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 
} 

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++?

+1

这是真正的代码?你真的有一个整数参数回调?它应该做什么? – 2012-01-17 21:31:35

+0

请注意,在您的代码中,注册回调的'A'不是接收回调的'A'。 – 2012-01-17 21:37:07

+0

这不会编译:“static std :: auto_ptr A(new A());”,因为没有“A”类型。至少在提问前通过编译器运行你的代码。 – SigTerm 2012-01-17 21:39:24

回答

0

为什么使用指针?

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

(同去的Bar),但说实话,你还不如说在Bar直接的方法(除非需要通过A重定向)

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

btw。一些apis使用一种模式,在注册期间你可以传递一个指向某些用户数据的指针,这些数据在回调函数中传递,例如可以是一个指向A的实例的指针来处理回调 - 这样你就可以避免所有的单身商业...

+0

完整签名是无效的(* RequestCallbackFunc)(LIBOBJ,int)。在库代码中,LIBOBJ参数被转换为名为“_LIBOBJ”的类型。这个结构的确有一个成员“void * userPointer”。那么今天我学到了一些东西。 – 2012-01-17 22:00:22

+0

@Ncarlson:所以你可以使用“void * userPointer”来存储你的类实例的“this”指针,并将调用转发给你的App :: HandleRequest()方法。 – 2012-01-17 22:25:00

5

使用成员函数时,没有简单的解决方案来兼容C风格的回调。 FAQ概述了常用的方法。

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

这是一种单身人士。由于它是反模式,所以它是不好的。

我会创建一个函数和一个全局变量或类型的应用程序。事情是这样的:

//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); 
} 
+0

是什么?一个“全局变量”而不是单身(erm,区别在于?) – Nim 2012-01-17 21:30:05

+0

@Nim Singleton是全局变量的隐藏实例,因此很愚蠢。 – 2012-01-17 21:33:45

+1

我认为单身人士是不好的,因为他们实际上是全球性的。你所做的,就是我认为不称之为模式的坏事。 – pmr 2012-01-17 21:40:07

0

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

2

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

精心设计的C接口的API通常提供一种方法来通过一个“状态”的参数为C回调函数(其可被用于传递象C++类实例this指针一些信息),如CreateThreadLPVOID lpParameter