1

C++ CLI クラスのメンバ関数をライブラリからネイティブ C コールバックに渡すときに問題が発生しました。

正確には、Teamspeak 3 SDK です。

次のコードを使用して、問題なく非メンバー関数を渡すことができます。

struct ClientUIFunctions funcs;

/* Initialize all callbacks with NULL */
memset(&funcs, 0, sizeof(struct ClientUIFunctions));
funcs.onConnectStatusChangeEvent        = onConnectStatusChangeEvent;

ただし、メンバー関数へのポインターを渡す必要があります。たとえば、次のようになります。

    funcs.onConnectStatusChangeEvent        = &MyClass::onConnectStatusChangeEvent;

非静的メンバー関数内でイベントを使用する方法については、他のアイデアを歓迎します。

前もって感謝します!

4

1 に答える 1

1

これは、静的クラス関数を介してのみ行うことができます。これは、C が vtable や関数がどのオブジェクトの一部であるかについて何も知らないためです。C++ およびマネージ C++ の例については、以下を参照してください。

ただし、これは回避策になる可能性があります。必要なすべてのコールバックを処理するラッパー クラスを構築してください。

#include <string.h>

struct ClientUIFunctions
{
     void (*onConnectStatusChangeEvent)(void);
};

class CCallback
{
public:
     CCallback()
      {
           struct ClientUIFunctions funcs;

           // register callbacks
           my_instance = this;

           /* Initialize all callbacks with NULL */
           memset(&funcs, 0, sizeof(struct ClientUIFunctions));
           funcs.onConnectStatusChangeEvent        = sOnConnectStatusChangeEvent;

      }

     ~CCallback()
      {
           // unregister callbacks
           my_instance = NULL;
      }

     static void sOnConnectStatusChangeEvent(void)
      {
           if (my_instance)
            my_instance->OnConnectStatusChangeEvent();
      }

private:
     static CCallback *my_instance;

     void OnConnectStatusChangeEvent(void)
      {
           // real callback handler in the object
      }
};

CCallback *CCallback::my_instance = NULL;

int main(int argc, char **argv)
{
    CCallback *obj = new CCallback();

    while (1)
    {
         // do other stuff
    }

    return 0;
}

別の可能性は、コールバックがプラグインから設定できるものをvoid *argsサポートしている場合です。void (*onConnectStatusChangeEvent)(void *args);この引数スペースにオブジェクトを設定できるので、de sOnConnectStatusChangeEvent では次のようになります。

static void sOnConnectStatusChangeEvent(void *args)
    {
           if (args)
              args->OnConnectStatusChangeEvent();
    }

マネージ C++ の場合、このようなものにする必要がありますが、テンプレート ブラケットが気に入らないため、コンパイルできません。

ラッパー.h:

using namespace std;
using namespace System;
using namespace System::Runtime::InteropServices;
using namespace System::Text;

namespace Test
{
    struct ClientUIFunctions
    {
        void (*onConnectStatusChangeEvent)(void);
    };

    public delegate void ConnectStatusChangeEvent(void);

    public ref class ManagedObject
    {
    public:
        // constructors
        ManagedObject();

        // destructor
        ~ManagedObject();

        //finalizer
        !ManagedObject();

        event ConnectStatusChangeEvent^ OnConnectStatusChangeEvent {
            void add(ConnectStatusChangeEvent^ callback) {
                m_connectStatusChanged = static_cast<ConnectStatusChangeEvent^> (Delegate::Combine(m_connectStatusChanged, callback));
            }

            void remove(ConnectStatusChangeEvent^ callback) {
                m_connectStatusChanged = static_cast<ConnectStatusChangeEvent^> (Delegate::Remove(m_connectStatusChanged, callback));
            }

            void raise(void) {
                if (m_connectStatusChanged != nullptr) {
                    m_connectStatusChanged->Invoke();
                }
            }
        }

    private:
        ConnectStatusChangeEvent^ m_connectStatusChanged;   
    };

    class CCallback
    {
    public:
        static void Initialize(ManagedObject^ obj);
        static void DeInitialize(void);

    private:
        static void sOnConnectStatusChangeEvent(void);

        static gcroot<ManagedObject^> m_objManagedObject;
    };
}

ラッパー.cpp:

#include <string.h>
#include "wrapper.h"

using namespace System;
using namespace Test;

void CCallback::Initialize(ManagedObject^ obj)
{
    struct ClientUIFunctions funcs;

    // register callbacks
    m_objManagedObject = obj;

    /* Initialize all callbacks with NULL */
    memset(&funcs, 0, sizeof(struct ClientUIFunctions));
    funcs.onConnectStatusChangeEvent        = sOnConnectStatusChangeEvent;

}

void CCallback::DeInitialize(void)
{
    // unregister callbacks
    m_objManagedObject = nullptr;
}

void CCallback::sOnConnectStatusChangeEvent(void)
{
    if (m_objManagedObject != nullptr)
        m_objManagedObject->OnConnectStatusChangeEvent();
}


// constructors
ManagedObject::ManagedObject()
{
    // you can't place the constructor in the header but just for the idea..
    // create wrapper
    CCallback::Initialize(this);          
}

// destructor
ManagedObject::~ManagedObject()
{
    this->!ManagedObject();
}

//finalizer
ManagedObject::!ManagedObject()
{
    CCallback::DeInitialize();        
}

gcroot<ManagedObject^> CCallback::m_objManagedObject = nullptr;

int main(array<System::String ^> ^args)
{
     ManagedObject^ bla = gcnew ManagedObject();

     while (1)
     {
      // do stuff
     }

     return 0;
}
于 2012-08-22T10:28:32.670 に答える