【问题标题】:pass member function from C++ CLI to native C callback将成员函数从 C++ CLI 传递到本机 C 回调
【发布时间】:2012-08-22 10:00:18
【问题描述】:

我在将 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 或函数所属的对象一无所知...
  • 以后有什么方法可以从静态类函数中调用成员函数吗?我需要访问事件中类的一些动态部分。
  • 看看this other TS question。那里的人们使用一个由客户端 ID 索引的容器,并对其进行索引以获取一个对象实例以对其进行操作。或者,您可以使用单例对象和静态方法。鉴于teampeak API 没有为您提供不透明的“上下文”参数,我看不出有任何其他明智的方法可以解决这个问题。
  • 您必须创建一个static 包装器,例如:static MyClass::wrapper(MyClass *thisone, Argtype args) { thisone->memfunc(args); },并找到一种方法使回调调用除了传递成员的正常参数之外还传递一个MyClass* ptr。另请注意,此时的参数应该是微不足道的(只有当它们是基元时,没有引用和非指针数据类型,即没有 struct/class/union...)所以 C/C++ 肯定同意他们。
  • 使用 Marshal::GetFunctionPointerForDelegate()。不需要静态方法。但请务必存储委托对象,以免被垃圾回收。

标签: c++ c function callback command-line-interface


【解决方案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 *argsvoid (*onConnectStatusChangeEvent)(void *args); 一样,则另一种可能性是您可以从插件中设置。您可以在此 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;
}

【讨论】:

  • 好吧,听起来不错,我将它添加到我的代码中,但有一个问题:MyClass::initTeamspeak() { CCallback *obj = new CCallback(); //Now comes the problem, the initClientLib needs the funcs pointer to proceed. if((error = ts3client_initClientLib(&amp;funcs, NULL, LogType_USERLOGGING, NULL, "")) != ERROR_ok) { char* errormsg; [...] } 所以唯一的方法是如果 CCallback 环绕 SDK 的每个功能?在我的 C++ CLI 代码中,我可以像这样使用 CCallback 吗? CCallback^ obj = new CCallback(); obj-&gt;OnConnectStatusChangeEvent += gcnew EventHandler(...);
  • 我正在为托管 C++ 重写它,但我的托管 C++ 有点生疏,只做过一次嘿嘿...
  • @user1616552 我用托管 C++ 示例编辑了这篇文章......但是当我尝试编译它时,它在static gcroot&lt;ManagedObject^&gt; m_objManagedObject; 声明中失败,也许我正在监督一些事情,因为这应该可以工作并且在我复制的另一个项目中工作... =)
  • 我认为缺少#include &lt;gcroot.h&gt;。仍然有一些错误,但我看看是否可以解决它们。但是,如果包含也有帮助,您也可以在您的系统上尝试。感谢您迄今为止的承诺帮助!
  • 我能够解决错误,但一个问题仍然存在:struct ClientUIFunctions funcs; m_objManagedObject = obj; memset(&amp;funcs, 0, sizeof(struct ClientUIFunctions)); funcs.onConnectStatusChangeEvent = sOnConnectStatusChangeEvent; if((error = ts3client_initClientLib(funz, NULL, LogType_USERLOGGING, NULL, "")) != ERROR_ok) { [...] 错误 C2664 'ts3client_initClientLib': 无法将参数 1 从 'const Test::ClientUIFunctions *' 转换为 'const ClientUIFunctions * '`这个是在TeamspeakSDK的clientlib.h中定义好的,我觉得不需要重定义
猜你喜欢
  • 1970-01-01
  • 2016-08-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-03-29
  • 2014-11-17
相关资源
最近更新 更多