【问题标题】:How do I call a C++ Class member function with the correct 'this' pointer like a normal C function? (pointer to class member function)如何使用正确的“指向正常的C函数”调用C ++类成员函数的C ++类成员函数? (指向类成员函数的指针)
【发布时间】:2014-10-03 14:34:08
【问题描述】:

我试图在 boost 的帮助下像普通 C 函数一样调用类成员函数,但是我的 efforts into looking how to 只是失败了,所以我想出了我自己的东西,听起来很直观的东西......我想。

但问题是,当调用指向 boost::function - fooAfooB 的指针时,它只会不断崩溃。

对其进行一些测试:

#include <boost/bind.hpp>
#include <boost/function.hpp>

class MyOtherClass{
public:
    MyOtherClass(){}
};

class MyClass
{
    std::string name;
public:
    void MyFunction(MyOtherClass *data)
    {
        std::cout << "NAME:" << name << std::endl;
    }
    boost::function<void(MyOtherClass *)> bindMyFunction;
    MyClass(std::string _name)
        : name(_name)
    {
        bindMyFunction = std::bind1st(std::mem_fun(&MyClass::MyFunction), this);
    }
};

然后我在 main 中只执行一次代码来尝试它,我希望看到 NAME:a\nNAME:b\n 三次,但两次后它就崩溃了:

int main()
{
    MyClass a("a");
    MyClass b("b");
    MyOtherClass c;

    //make sure we can save the Class::bindMyFunction variable
    void ** someTestA = (void**)&a.bindMyFunction;
    void ** someTestB = (void**)&b.bindMyFunction;

    //'convert' it to the needed type, just like function-pointers work
    //The library uses this below
    //                             Now the correct assignment is the hard part
    void(*fooA)(MyOtherClass*) = (void(*)(MyOtherClass*))(*someTestA);
    void(*fooB)(MyOtherClass*) = (void(*)(MyOtherClass*))(*someTestB);

    //Works
    a.MyFunction(&c);
    b.MyFunction(&c);

    //Works
    a.bindMyFunction(&c);
    b.bindMyFunction(&c);

    //execute the boost::function by calling it like a function
    //Crash
    fooA(&c);
    fooB(&c);
    return 0;
}

不幸的是,我使用了一个只接受参数void(*)(MyOtherClass*) 的库,但我需要将它封装在一个类中。该库使用回调来通知任何更改。所以我需要想办法将boost::function&lt;void(*)(MyOtherClass*)&gt; 转换为void(*)(MyOtherClass*) 类型,然后让库能够call(parameters) 它。

但这似乎是一项很难完成的任务。有什么方法可以正确地做到这一点吗?


编辑:根据要求,我会让这个问题更具体,但我担心这个问题会以这种方式过于本地化,但在这里:

我正在尝试创建一个封装了 RakNet 的类。将所有内容都放入类中(甚至是 RakNet 回调)。该类的每个对象实例都是一个自己的客户端,它将连接到游戏服务器:

class MyClass
{
    RakClientInterface* pRakClient;
    void MyFunction(RPCParameters *pParams){}
    boost::function<void(RPCParameters *)> bindMyFunction;
public:
    MyClass()
    {   
        pRakClient = RakNetworkFactory::GetRakClientInterface();
        if (pRakClient == NULL)
            return;

        bindMyFunction = std::bind1st(std::mem_fun(&MyClass::MyFunction), this);

        //void RakClient::RegisterAsRemoteProcedureCall( int* uniqueID, void ( *functionPointer ) ( RPCParameters *rpcParms ) )
        pRakClient->RegisterAsRemoteProcedureCall(&RPC_ServerJoin, /*<need to pass boost::function here somehow>*/);
    }
};

【问题讨论】:

  • 您的代码首先获取boost::function 的对象表示的4 个字节,然后告诉编译器这是一个普通函数指针。因此,您的代码依赖于编译器。您的程序表现出未定义的行为。
  • 好吧,我希望它能起作用,我真的很拼命地寻找一种能够以某种方式执行boost::function&lt;T&gt;(params) 的方法,方法是使用来自void(*fooA)(MyOtherClass*) 变量的数据并将其称为fooA(params)
  • 这看起来相似吗? stackoverflow.com/questions/25212423/…
  • 顺便说一句,您可以将不捕获任何内容的 lambda 静态转换为函数指针。 static_cast&lt;DoItRet (*)(MyOtherClass*)&gt;( [](MyOtherClass* p){return ((ActualClass*)p)-&gt;DoIt();} );
  • 您使用的库看起来设计得很糟糕。它根本不会将任何上下文信息传递给回调 - 没有任何东西可以挂起 this 指针。我已经看到了一种解决这种情况的方法——一个动态生成的机器代码 thunk,其中嵌入了this 指针——但我没有资格实际编写一个,而且它非常不便携。在任何情况下,您使用boost::function 进行的游戏都将无法运行 - 无论您将多少奇怪的演员串在一起,您都无法凭空提取丢失的信息。

标签: c++ class pointers c++11 boost


【解决方案1】:

如果MyClass 只是一个单例,那么你可以很容易地使用静态成员来做到这一点。不幸的是,由于您要创建多个 MyClass 实例,因此您没有太多选择。您要么需要一些东西来悬挂this 指针,就像 Igor Tandetnik 说的那样,要么您需要一个可以安全地转换为函数类型的闭包,而 C++11 不提供。它需要动态创建的机器代码来实际实现类似的东西,而 GNU C(不是 C++)编译器是我所知道的唯一可以为你做到这一点的编译器。

我不熟悉 RakNet,但我在网络上进行了一些搜索,看起来它几乎没有给你一些东西来悬挂 this 指针,可以这么说。 RPC 函数传递的RPCParameters 指针包含一个名为recipientRakPeerInterface 指针,它应该指向同一个RakClient 对象,该对象由您用来注册RPC 函数的RakClientInterface 指针所指向。假设MyClass 的每个实例只有一个RakClient 对象,您可以使用RakClient 的地址映射到创建它的MyClass 对象。

我已经发布了一个完整的示例来演示如何执行此操作。它没有实际使用 RakNet,而是使用相关类的框架实现。我不清楚您使用的是哪个版本的 RakNet(大多数实现似乎有 uniqueID 类型的参数 char const * 而不是 int *),但只要 RPCParamters 有一个 recipient 指针,这种技术应该工作。

#include <stdint.h>
#include <assert.h>
#include <stdio.h>
#include <map>

struct RakPeerInterface;

struct RPCParameters {
    RakPeerInterface *recipient; 
};

typedef void (*rakrpcfunc)(RPCParameters *rpcParams);

struct RakPeerInterface {
    virtual void RegisterAsRemoteProcedureCall(int *, rakrpcfunc) = 0;

};

struct RakPeer: RakPeerInterface {
    virtual void
    RegisterAsRemoteProcedureCall(int *, rakrpcfunc func) {
        RPCParameters params;
        params.recipient = this;
        func(&params);
    }
};

struct RakClientInterface {
    virtual void RegisterAsRemoteProcedureCall(int *, rakrpcfunc) = 0;
};

struct RakClient: RakPeer, RakClientInterface {
    virtual void
    RegisterAsRemoteProcedureCall(int *id, rakrpcfunc func) {
        return RakPeer::RegisterAsRemoteProcedureCall(id, func);
    }
};

struct RakNetworkFactory {
    static RakClientInterface *
    GetRakClientInterface() { 
        return new RakClient;
    }
};

class MyClass;
typedef void (MyClass::*rpcmethod)(RPCParameters *rpcParams);

template <rpcmethod method>
class rak_thunk {
    static std::map<RakClient *, MyClass *> clientmap;

    static void thunk(RPCParameters *rpcParams) {
        RakPeerInterface *pip = rpcParams->recipient;
        RakClient *cp = dynamic_cast<RakClient *>(pip);
        return (clientmap[cp]->*method)(rpcParams);
    }

public:
    static rakrpcfunc
    make_thunk(MyClass *this_, RakClientInterface *cip) {
        RakClient *cp = dynamic_cast<RakClient *>(cip);
        assert(clientmap[cp] == NULL || clientmap[cp] == this_);
        clientmap[cp] = this_;
        return thunk;
    }
};

template <rpcmethod method>
std::map<RakClient *, MyClass *> rak_thunk<method>::clientmap;

int RPC_ServerJoin;

class MyClass {
    RakClientInterface *pRakClient;

    void
    MyFunction(RPCParameters *pParams) {
        printf("MyClass(%s)::MyFunction called\n", name);
    }

    typedef void (MyClass::*rpcmethod)(RPCParameters *rpcParams);

    char const *name;

public:
    MyClass(char const *s): name(s)  {   
        pRakClient = RakNetworkFactory::GetRakClientInterface();
        if (pRakClient == NULL)
            return;

        rakrpcfunc p = (rak_thunk<&MyClass::MyFunction>
                ::make_thunk(this, pRakClient));

        pRakClient->RegisterAsRemoteProcedureCall(&RPC_ServerJoin, p);
    }
};

int
main() {
    MyClass a("a");
    MyClass b("b");
}

此示例在 Windows 下使用 GNU C++ 4.8.1 和 Visual Studio 2013 C++ 编译器进行编译和运行。值得注意的是,它没有使用 Boost 或任何花哨的 C++11 新特性。

【讨论】:

    猜你喜欢
    • 2010-11-02
    • 1970-01-01
    • 2011-12-09
    • 1970-01-01
    • 1970-01-01
    • 2012-08-20
    • 2011-08-25
    • 1970-01-01
    • 2013-01-26
    相关资源
    最近更新 更多