将通用 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_IDeviceInfo
和 T_IDeviceControl
都包含一个独立的基础 class T_Handle
和 up-casting 从 T_Mouse
到 T_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();
}
我正在为 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_IDeviceInfo
和 T_IDeviceControl
都包含一个独立的基础 class T_Handle
和 up-casting 从 T_Mouse
到 T_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();
}