gpt4 book ai didi

调用指针时出现 C++ typedef 访问冲突

转载 作者:行者123 更新时间:2023-11-30 03:32:23 25 4
gpt4 key购买 nike

我有这段代码:(执行)

#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);
}

和我的动态链接库:(.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)”我希望有一个指向类的指针 + 方法调用的偏移量。

最佳答案

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

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

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

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

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

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

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


至于你的选择是什么:

  • 如果您绝对必须通过 C 风格的接口(interface),在调用回调的一侧强制使用 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
  • 如果接口(interface)中可以有 C++(即 DLL 接口(interface)两边的编译器和版本将始终相同),则可以使用指向成员函数的指针,前提是:

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

关于调用指针时出现 C++ typedef 访问冲突,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43490306/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com