0

这个问题不是关于以标准方式引用派生类的基指针。相反,我需要在我的代码中支持两种不同的 IOCTL 结构(SCSI_PASS_THROUGH_DIRECT_EXSCSI_PASS_THROUGH_DIRECT供参考)。这些结构是封装结构的一部分(每个结构都是独立的)。我已将这些封装结构细分为一个共同的基础。

对于没有使用过 IOCTL 的人来说,处理 IOCTL 需要一个指向该结构的指针。在这种情况下,这是预期的封装结构。

我的问题不在于基指针是否可以指向派生类(我知道它们可以)。我想知道的是,我怎样才能可靠地指向作为这个类层次结构的内存区域中派生类的开始?下面的代码说明了我的策略并在 VS 2010 和 Cygwin (GCC 4.5.3) 中编译。GCC 对 offsetof() 宏的使用不当提出了一些抱怨(从我所阅读的所有内容来看,这似乎可能是真的),但代码似乎反映了我在运行时所追求的。

有没有更好的方法来做到这一点?

#include <iostream>
#include <stdint.h>
#include <cstddef>

enum IoctlType {
    T1,
    T2
};

struct ptd1 {
    uint32_t Version;
    uint32_t Size;
    uint32_t OffsetOfStuff;
    uint8_t ary[1];
};

struct ptd2 {
    uint32_t Version;
    uint32_t Size;
    uint32_t OffsetOfStuff;
    uint8_t ary[16];
};

struct base {
    IoctlType it;
    virtual void* GetPtr()=0;
};

struct derv1 : base {
    ptd1 passThrough;
    uint8_t ary[15]; // with ptd.ary[1], array is 16 elements
    uint32_t fluff; // realign to DWORD boundary
    uint32_t stuff;

    derv1() {
        it = T1;
        passThrough.Version = 33;
        passThrough.Size = sizeof(ptd1);
        passThrough.OffsetOfStuff = offsetof(derv1, stuff);
    }

    // as the first member of the derv1 struct, this pointer should be the "start"
    virtual void* GetPtr() { return &passThrough; }
};

struct derv2 : base {
    ptd2 passThrough;
    uint32_t stuff;

    derv2() {
        it = T2;
        passThrough.Version = 22;
        passThrough.Size = sizeof(ptd2);
        passThrough.OffsetOfStuff = offsetof(derv2, stuff);
    }

    // as the first member of the derv2 struct, this pointer should be the "start"
    virtual void* GetPtr() { return &passThrough; }
};

int main() {
    base* pBase = new derv1();

    std::cout << "Now, check the results" << std::endl;
    std::cout << "size of base " << sizeof(base) << std::endl;
    std::cout << "size of derv " << sizeof(derv1) << std::endl;
    std::cout << "addr of struct " << pBase << std::endl;
    std::cout << "addr of passThrough " << pBase->GetPtr() << std::endl;
    std::cout << "offset of passThrough " << offsetof(derv1, passThrough)
        << std::endl;

    base* pBase2 = new derv2();

    std::cout << "Now, check the results" << std::endl;
    std::cout << "size of base " << sizeof(base) << std::endl;
    std::cout << "size of derv " << sizeof(derv2) << std::endl;
    std::cout << "addr of struct " << pBase2 << std::endl;
    std::cout << "addr of passThrough " << pBase2->GetPtr() << std::endl;
    std::cout << "offset of passThrough " << offsetof(derv2, passThrough)
        << std::endl;

    return 0;
}
4

1 回答 1

0

经过多次内部辩论,我认为 Slava 的评论是最有帮助的;不要这样做。为此,我基本上制作了一个包装器结构,它知道要使用哪种结构。标准是:

  • 这是 Windows 7 及更早版本,还是 Windows 8 及更高版本
  • 如果是 Windows 8 及更高版本,这是 64 位操作系统上的 32 位 proc

然后,有适当的函数来获取/设置所需的值。最终产品看起来像这样:

struct ScsiPassThruExWBuffers {
    SCSI_PASS_THROUGH_DIRECT_EX  passThru;
    unsigned char cdbExt[259]; // 260 by SCSI Standard
    int fluff;  // realign
    unsigned char senseInfo[256];
    STOR_ADDR_BTL8 addrInfo;
};

struct ScsiPassThru32ExWBuffers {
    SCSI_PASS_THROUGH_DIRECT32_EX  passThru;
    unsigned char cdbExt[259]; // 260 by SCSI Standard
    int fluff;  // realign
    unsigned char senseInfo[256];
    STOR_ADDR_BTL8 addrInfo;
};

struct ScsiPassThruWBuffers {
    SCSI_PASS_THROUGH_DIRECT  passThru;
    unsigned char senseInfo[256];
};

enum PassThruType {
    PassThru,
    PassThruEx,
    PassThru32Ex
};

struct Wrapper {
    void * pStruct;
    PassThruType ptt;
    unsigned int ioctlCode;
    unsigned int aggregateStructSize;

    // ctors/dtors
    Wrapper();
    Wrapper(Wrapper const& source); // copy c-tor
    ~Wrapper(); // delete memory allocated for pStruct;

    // several setters/getters for the properties in the structures

    private:
    Wrapper& operator=(Wrapper const& rhs); // no implementation
};

这将传递结构的所有聚合或封装结构留下为 POD,并且可靠地确定偏移量的能力是可靠的。

于 2013-11-14T17:38:20.580 回答