0

我有一个可以生成一些事件的 c++ 共享库。我有一个监听器接口和一个能够注册观察者和触发事件的类。

该库可用于 java、C# 和 C++ 代码(使用不同的编译器编译),因此我有两个文件头:*.h 用于 ANSI C 接口,*.hpp 用于直接从 C++ 代码使用库。现在我无法弄清楚如何使用类 C 接口导出观察者模式。

这是代码结构的一小部分。

// hpp file
class IListener
{
public:
    virtual ~IListener() {}

    virtual void event1( int ) = 0;
    virtual void event2() = 0;
};

using IListenerPtr = std::shared_ptr< IListener >;

class Controller
{
public:
    Controller( IListenerPtr listener );

    void addListener( IListenerPtr listener );

private:
    void threadFunc()
    {
        while ( true )
        {
            // an event occured

            for ( auto& e : mListeners )
                e->event1( 2 );

            for ( auto& e : mListeners )
                e->event2();
        }
    }


private:
    std::vector< IListenerPtr > mListeners;

};

// h file

#if defined( __MSCVER ) || defined( __MINGW32__ ) || defined( __MINGW64__ )
#   define LIB_CALLBACK __stdcall
#   define LIB_CALL __cdecl
#   if defined( AAMS_EXPORTS )
#       define LIB_API __declspec( dllexport )
#   else
#       define LIB_API __declspec( dllimport )
#   endif
#else
#   define LIB_API
#endif // WIN32

typedef int libError;

LIB_API libError LIB_CALL libInit( ???? );

如何使这个库可以从 C 代码中使用?第一次尝试可能是:

typedef struct libListenerTag
{
    typedef void (LIB_CALLBACK *Event1Func)( int );
    typedef void (LIB_CALLBACK *Event2Func)();

    Event1Func Event1;
    Event2Func Event2;

} libListener;

LIB_API libError LIB_CALL libInit( libListener* listener );

并以某种方式绑定libListenerIListener

// cpp file
class CListener : public IListener
{
public:
    CListener( libListener* listener 
        : mListener( listener )
    {
    }

    void event1( int i ) { mListener->Event1( i ); }
    void event2() {  mListener->Event12(); }

private:
    libListener* mListener;
}

Controller* g_controller = nullptr;

LIB_API libError LIB_CALL libInit( libListener* listener )
{
    g_controller = new Controller( make_shared< CListener >( listener ); 

    // ...
}

这种方法对我来说看起来不太好。有更好的方法来实现这一点吗?

4

1 回答 1

1

您缺少 C 事件回调中的标准内容:上下文指针。

在 C++ 中,您的IListener子类在其回调中获得一个隐式this指针,这意味着它可以在实例中存储状态和上下文信息。

对于自由函数,你没有这个,所以你需要添加一个显式参数。

/* c_interface.h */
typedef void (*EventCallback1)(void *context, int arg);
typedef void (*EventCallback2)(void *context);

struct Listener; /* opaque type */

struct Listener *create_listener(EventCallback1, EventCallback2, void *context);
void destroy_listener(struct Listener*);

因此,从 C 方面来说,您将所需的任何状态打包到一个结构中,将其create_listener作为您的上下文传递,并在调用它们时将其传递回您的函数。

// c_implementation.cpp
extern "C" {
#include "c_interface.h"

struct Listener: public IListener {
  EventCallback1 cb1;
  EventCallback2 cb2;
  void *context;

  Listener(EventCallback1 e1, EventCallback2 e2, void *c)
    : cb1(e1), cb2(e2), context(c)
  {}

  virtual void event1(int arg) {
    (*cb1)(context, arg);
  }
  virtual void event2() {
    (*cb2)(context);
  }
};

Listener *create_listener(EventCallback1 cb1, EventCallback2 cb2, void *context) {
    return new Listener(cb1, cb2, context);
}
}

客户端代码看起来像

#include "c_interface.h"

struct MyContext {
    int things;
    double needed;
    int to_handle;
    char callbacks[42];
};

void cb0(void *context) {}
void cb1(void *context, int arg) {
    struct MyContext *c = (struct MyContext *)context;
    c->things = arg;
}

void foo() {
    struct MyContext *context = malloc(sizeof(*context));
    struct Listener *l = create_listener(cb1, cb0, context);
    ...
于 2013-07-30T13:30:29.490 回答