调用指针时c ++ typedef访问冲突

异味

我有这个代码:(exe)

#include <Windows.h>
#pragma comment(lib, "user32.lib")

class Dummy;
typedef void(Dummy::*Referece)(int i);
typedef void(*InitCall)(void*, Referece);

class Dummy
{
public:
    Dummy(){}
    void callMe(int val)
    {
        MessageBoxA(0, "ok", "ok", 0);
    }
};

int main()
{
    Dummy* obj = new Dummy();

    HMODULE ha= LoadLibraryA("aa.dll");
    InitCall val = (InitCall)GetProcAddress(ha, "Init");

    val(obj, &Dummy::callMe);
}

和我的 dll: (.h)

#pragma once  

#define DLL_EXPORT __declspec(dllexport)

class Test;
typedef void (Test::*Reference)(int a);

#ifdef __cplusplus
extern "C"
{
#endif
    void DLL_EXPORT Init(Test* Object, Reference reference);

#ifdef __cplusplus
}
#endif

(.cpp)

#include "your.h"

void DLL_EXPORT Init(Test * Object, Reference reference)
{
    (Object->*reference)(1);
}

我复制了系统,应该是这样的,因为我无法在一侧更改代码。为什么我会遇到访问冲突?调用 "val(obj, ref)" 我希望有一个指向类的指针 + 方法调用的偏移量。

Angew 不再以 SO 为荣

指向成员的指针不是“类中的偏移量”。没有这样的事情。某些情况下(例如指向具有简单继承层次结构的类中的虚拟成员函数的指针),它的实现可以由这样的偏移量(加上可能的其他一些数据位)组成。

但是,对于非虚拟函数(如您的示例中),它可能有一个指向其下方的函数的普通指针。非虚拟函数不存储在任何带有“偏移量”的“表”中(至少没有理由以这种方式存储它们),它们很可能作为普通的沼泽标准函数实现,名称错误且前置参数.

指向成员的指针是 C++ 的一个有点棘手的部分,主要是因为没有明显的实现概念映射,并且不同的编译器可以以不同的方式处理它们。相信 avoid (Dummy::*)(int)void (Test::*)(int)是二进制兼容是非常脆弱的。

通常,您不能期望指针的二进制表示与指向Dummy::callMe成员函数的指针的二进制表示有任何相似之处Test,因为它可能过多地依赖于 和 的定义,Dummy以及Test编译器如何实现指向成员的指针.

最重要的是,Visual Studio 的编译器在默认情况下处理指向成员的指针的方式是不符合标准的(因此,从大多数角度来看,这是错误的)。这种默认处理是为了正确形成指向类成员的指针,编译器需要看到类的定义。原因是成员指针的最通用实现非常大(我相信是 4 个原生词),因为它必须考虑虚拟继承等。没有 virtuals 的单基类的最常见情况可以放在本地词中。

因此,如果您想可靠地使用完全标准的C++ 构造,例如接受指向其定义在站点上不可见的类成员的指针,则必须使用编译标志/vmg这样,将始终使用最通用的表示。

默认行为 ,根据 的定义/vmb优化二进制表示(包括大小!)因此,不可能在这种行为生效的情况下创建像您这样的 typedef。A::*A


至于你的选择是什么:

  • 如果您绝对必须通过 C 风格的接口,强制使用 C 风格的函数作为调用回调的一侧的回调,并在注册侧创建一个包装 C 风格的函数。像这样的东西:

    class Dummy
    {
      void callMe(int) {}
    };
    
    extern "C" void fw_Dummy_callMe(void *self, int i)
    { static_cast<Dummy*>(self)->callMe(i); }
    

    #ifdef __cplusplus
    extern "C"
    {
    #endif
        void DLL_EXPORT Init(void* Object, void (*reference)(void*, int));
    
    #ifdef __cplusplus
    }
    #endif
    
  • 如果您可以在接口中使用 C++(即 DLL 接口两侧的编译器和版本将始终相同),您可以使用指向成员函数的指针,前提是:

    1. 双方不会看到类的不同定义。不过,如果其中一个只有非定义声明,那也没关系。要 100% 符合 C++,类的名称应该相同。
    2. /vmg在构建 DLL 及其客户端时使用。

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章