将通用 Class 句柄转换为具有多重继承的基 Class (即接口)

Casting a Generic Class Handle to Base Class (i.e. interface) with multiple inheritance

我正在为 C++ 库开发 c-api 包装器。我也是c++库的维护者。

到目前为止,我的基本策略是为库中的每个 c++ class 设置一个不透明句柄。 我将句柄声明为结构:

struct Handle_;
typedef struct Handle_ *Handle;

然后在我的 c-api 包装器 header 中,我为 class 的每个成员函数都有一个函数,包括创建和销毁。我已经按照 post 中的描述实现了它:

但是知道我有以下问题:

注意:要重现问题,不需要 c-api 包装器。都是选角。

假设我有一个 class 继承自两个抽象 class (即接口):

class T_IDeviceInfo {
public:
    virtual int getDeviceType() = 0;
};

class T_IDeviceControl {
public:
    virtual void open() = 0;

};

class T_Device : public T_IDeviceInfo, public T_IDeviceControl {
public:
    int getDeviceType() override {
        std::cout << "T_Device::getDeviceType()" << std::endl;
        return 0;
    }
    void open() override {
        std::cout << "T_Device::open()" << std::endl;
    }
};

在下面的代码中,我使用上面声明的 class:

int main(){
    //Prints the device type message and the open message from T_Device
    T_Device* device = new T_Device();
    device->getDeviceType();
    device->open();

    //works
    std::cout << "cast as derived class" << std::endl;
    reinterpret_cast<T_IDeviceInfo*>(device)->getDeviceType();
    reinterpret_cast<T_IDeviceControl*>(device)->open();

    std::cout << std::endl;

    void* handle = reinterpret_cast<void*>(device);

    //works
    std::cout << "cast handle as derived class" << std::endl;
    reinterpret_cast<T_Device*>(handle)->getDeviceType();
    reinterpret_cast<T_Device*>(handle)->open();

    //does not work
    std::cout << "cast handle as base class (i.e. interface)" << std::endl;
    reinterpret_cast<T_IDeviceInfo*>(handle)->getDeviceType();
    reinterpret_cast<T_IDeviceControl*>(handle)->open();

}

输出如下所示:

T_Device::getDeviceType()
T_Device::open()
cast as derived class
T_Device::getDeviceType()
T_Device::open()

T_Device::getDeviceType()
T_Device::open()
cast handle as derived class
T_Device::getDeviceType()
T_Device::open()
cast handle as base class (i.e. interface)
T_Device::getDeviceType()
T_Device::getDeviceType()

所以问题是最后一节。当我将我的通用句柄投射到基础 class (接口)时,它的函数被声明在那里。似乎它总是调用继承顺序中第一个的 class 的函数。 到目前为止我读到的是,演员表现在不了解 class 大小并且计算偏移量错误。这样对吗?你能帮我澄清一下吗?

我做了一些研究并注意到以下几点: 当使用 static_cast<>() 而不是 reinterpret_cast<>() 时,我得到了相同的输出。 我知道使用不同的转换方法会有所不同,但我很困惑哪一种方法适合我的任务。

关于 c 包装器的所有 posts api 提出了上面链接答案中的方法,并使用 reinterpret_cast 或 static_cast。

甚至可以将通用句柄转换为派生 class 的基 class。这会节省我很多工作,因为接口用于不同的设备 classes,所以我不需要为每个设备重写 c 包装函数。

感谢任何帮助。 谢谢!

我认为,static_cast<>()reinterpret_cast<>() 根本无法进行您请求的这种转换,因为您有多重继承。如果有多个基数 class,则将 class 投射到其基数 classes 涉及一些位移魔法。

我的建议是使用 static_cast<>() 转换为常见的 class T_Device,然后使用 dynamic_cast<>() 完成真正的工作:

T_Device *const device = static_cast<T_Device *>(handle);
T_IDeviceInfo *const info = dynamic_cast<T_IDeviceInfo *>(device);
T_IDeviceControl *const control = dynamic_cast<T_IDeviceControl *>(device);

但据我了解,您有一些 Device-Classes,例如

class T_Keyboard : public T_IDeviceInfo, publibc T_IDeviceControl { /* ... */ };
class T_Mouse : public T_IDeviceInfo, publibc T_IDeviceControl { /* ... */ };

还有一个工厂函数,例如

void *get_device_handle(const char *name) {
    if (strcmp(name, "keyboard") == 0) {
        return new T_Keyboard(/* ... */);
    } else if (strcmp(name, "mouse") == 0) {
        return new T_Mouse(/* ... */);
    } else {
        return NULL;
    }
}

现在的问题是,你怎么能写出像

这样的函数
void open_device(void *handle) {
    T_IDeviceControl *const control = /* some casting from handle */
    control->open();
}

对吧?一个可能的解决方案是,为所有接口定义一个公共虚拟基础 class:

class T_Handle {
public:
    // By the way: You forgot this important one! In polymorphic classes
    // the destructor must be virtual.
    virtual ~T_Handle() { }
};

class T_IDeviceInfo : public virtual T_Handle { /* ... */ };
class T_IDeviceControl : public virtual T_Handle { /* ... */ };

基础classT_Handle必须是虚拟的,否则T_Mouse和[=25=里面有两个T_Handle基础class ].由于 classes T_IDeviceInfoT_IDeviceControl 都包含一个独立的基础 class T_Handle 和 up-casting 从 T_MouseT_Handle 将是模棱两可的。

get_device_handle() 函数必须如下所示:

void *get_device_handle(const char *name) {
    if (strcmp(name, "keyboard") == 0) {
        return dynamic_cast<T_Handle *>(new T_Keyboard(/* ... */));
    } else if (strcmp(name, "mouse") == 0) {
        return dynamic_cast<T_Handle *>(new T_Mouse(/* ... */));
    } else {
        return NULL;
    }
}

open_device() 函数如下所示:

void open_device(void *handle) {
    T_Handle *const base = static_cast<T_Handle *>(handle);
    T_IDeviceControl *const control = dynamic_cast<T_IDeviceControl *>(base);
    control->open();
}