将成员函数从C++CLI传递到本机C回调

pass member function from C++ CLI to native C callback

本文关键字:本机 回调 成员 函数 C++CLI      更新时间:2023-10-16

我在将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;

欢迎使用任何其他关于如何在非静态成员函数中使用事件的想法。

提前感谢!

这只能通过静态类函数来完成,因为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);,您可以从插件中设置。你可以在这个args空间中设置对象,这样在de sOnConnectStatusChangeEvent中你会有这样的东西:

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

对于托管C++,它应该是这样的,但是我无法编译它,因为它不喜欢模板括号

wrapper.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;
}