我对类型转换的安全性有一些担忧我设计了一个抽象接口,女巫将由插件导出面向对象的C ABI ,即指向对象的指针和形式为func(void *this, ...)
的C风格函数,而不是C++样式的成员函数,然后这些函数将被打包到表示对象实现的结构中。但是,我的一些底层框架使用多个虚拟继承。通过C ABI通过void指针传递C++对象(有可能的多重虚拟继承)
简单的例子
class A
{
public:
virtual void doA()
}
class B
{
public:
virtual void doB()
}
class C : public A, public B
{
public:
virtual void doA()
virtual void doB()
}
struct impA
{
(*doA)(void *self);
}
struct impB
{
(*doB)(void *self);
}
struct impC
{
(*doA)(void *self);
(*doB)(void *self);
}
void * AfromC(void *v) {
C*c = reinterpret_cast<C*>(v); // Known to be C* type
return static_cast<void*>(static_cast<A*>(c)); // method 1
return reinterpret_cast<void*>(static_cast<A*>(c)); // method 2
//method 3 & 4
C** c = static_cast<C**>(v); // Known to be C* type
return static_cast<void*>(&static_cast<A*>(*c)); // method 3
return static_cast<void*>(static_cast<A**>(c)); // method 4
}
/////////// main code
class A
{
public:
void doA() { imp.doA(self); }
private:
impA imp;
void *self;
}
class B
{
public:
void doB() { imp.doB(self); }
private:
impB imp;
void *self;
}
考虑AfromC,我得到一个指针,我可以放心地通过C ABI的4个可能的方法,我想知道这些不同的方法的考虑,我更倾向于将方法1.
我不确定所有这些方法是否合法或安全。
注:对象永远是由它们创造了二元函数访问/摧毁他们返回/接受他们处理自己或C型数据类型(最多POD的结构)
其他对象虽然我发现在网上提到这些东西,但它们都是关于由于转换为无效而导致出现问题的人,即A* a= static_cast<A*>(static_cast<void*>(c)) // c -> C*
,这是可以预料的,因为这不会纠正vtable,解决方案是使用摘要基本类型(这对我来说工作,因为我需要通过C ABI),但是我也听说虚拟指针比正常指针大,因此我考虑方法3和4的原因,因为这将是指向较大指针的正常指针,因此即使对于类型也是安全的与更大的指针。
所以,我的主要问题是方法1将出问题吗?我可以安全地定义一个模板函数沿着template <typename T, typename U> void * void_cast(U v) { static_cast<void *>(static_cast<T>(v)); }
的行来简化插件代码。最后如果方法1是正确的,为什么?并且可以使用任何方法?
你能否澄清一下,“但是va == vc不能保证”,因为我把va转换成a * = static_cast (va),然后我可以将它转换成C类型的对象,即static_cast(a)或reinterpret_cast (a),因为我知道va已被转换为y我们从C类型的对象中得到答案,或者在通过void类型投射后,这会不再安全吗? –
glenflet
“dereferencing a2是未定义的行为”谨慎解释你为什么这么想?从我所知道的情况来看,在通用视图中,“a2”和“c2”与“a”和“c”是相同的,所以它们应该同样是UB--无论是yes还是no,不是中途。 – dascandy
@glenflet:不能保证可以在标准中找到。在使用虚拟功能的vtables的实现中,通常对象的地址是虚拟表的地址。在这种情况下,static_cast进行偏移校正,但是'va'和'vc'的实际值在这种情况下是不同的。只要尝试使用调试器和具有虚函数的基类和子类 –