有没有一种便携式方法可以通过指向虚拟表成员的指针来获取虚拟表结构的指针

时间:2019-12-14 10:51:41

标签: c++

在我的io完成端口实现中,我是通过从OVERLAPPED结构继承的结构中的操作码上的操作码上的switch语句传递通知的,期望OVERLAPPED基距所导出的偏移量为0。

后来,我在派生的结构中使用了虚拟方法,而不是使用带开关的操作码,这使设计更容易,更清晰。我仍然认为OVERLAPPED结构的偏移量为0,所以我只是使用类似的方法来执行io操作:

WSASend(ns, &wsbuf, 1, &sent, 0, (LPWSAOVERLAPPED)(std::addressof(CTX)), nullptr);

其中CTXBaseIoCtx类型的结构,即:

BaseIoCtx : public OVERLAPPED
{

    void *fd;

    virtual void HandleIocpPacket(io::iocp::CompletionResult& IocpPacket) = 0;

    virtual ~BaseIoCtx() = default;
};

,然后在io循环中回退,我使用了以下代码:

// BaseIoCtx inherits from OVERLAPPED
// result is an OVERLAPPED_ENTRY
BaseIoCtx *ctx = reinterpret_cast<BaseIoCtx*>(result.lpOverlapped);
ctx->HandleIocpPacket(result); // the virtual function that delivers the notification

我认为这应该运作良好,并且运作了一个多月!

昨天进行了一些测试之后,我在这一行发生了奇怪的崩溃:

ctx->HandleIocpPacket(result); // the virtual function that delivers the notification

调试器说:read access violation ... ctx-> was (nullptr, 0XFFFFFFFF, any strange address)

无论如何,我将ctx本身放在Visual Studio调试器中的有效地址上,并在OVERLAPPED基数中包含正确的传输长度,但是__vfptrnullptr或一个非常奇怪的地址,例如0xFFFFFFF或0xCCCCCCCCCCC因此,我在提交io请求之前在行上设置了一个断点,并看到__vfptr指向有效的vtable并包含BaseIoCtx的两个虚函数,所以我发现在io请求期间Windows内部函数在__vfptr处写入并破坏了它。

我想知道是因为我虽然基本结构不是在派生类的开头,但不是__vfptr,但是在对offsetof进行了一些实验之后,我发现__vfptr的位置是偏移量0!

但是我然后将BaseIoCtx更改为:

struct BaseIoCtx : public IoContext
{

    OVERLAPPED ov = { 0 };

    static BaseIoCtx * from_ov_ptr(OVERLAPPED *ov_ptr) noexcept
    {
        constexpr std::size_t ov_offset = offsetof(BaseIoCtx, ov);
        return reinterpret_cast<BaseIoCtx*>(reinterpret_cast<char*>(ov_ptr) - ov_offset);
    }

    void *fd;

    virtual void HandleIocpPacket(io::iocp::CompletionResult& IocpPacket) = 0;

    virtual ~BaseIoCtx() = default;
};

以及我使用的提交中:

WSASend(ns, &wsbuf, 1, &sent, 0, (LPWSAOVERLAPPED)(std::addressof(CTX.ov)), nullptr);

然后在io循环中,我将结构取回:

BaseIoCtx * ctx = BaseIoCtx::from_ov_ptr(result.lpOverlapped);

,并且效果很好。现在的问题是:第一个实现如何在同一个编译器上长期运行!新方法是否被认为是C ++中的一种可移植方式,而不是hack方法?如果是hack方法,还有更好的方法吗?

0 个答案:

没有答案