我在共享库创建的对象上遇到一些dynamic_cast
时遇到了麻烦:
架构类似于:
class A;
class B : virtual public A; // one of the several interfaces
class C : public B; // defined only in the shared library
C
对象,但它返回一个
dynamic_cast<A*>(pointerToCclass)
因为主要的应用是
不知道C
B
时
从返回的A
指针失败。我怀疑在main和共享代码中创建的vtable有些不同可能是原因。无论如何,最初我不知道这个问题,因为主要的应用程序
调用方法void * A::getInterface( int ifEnum )
,因此通过共享库代码成功执行向下转换,并作为void指针返回。然后主应用程序执行reinterpret_cast
以将void指针绑定到所需的接口。
直到现在,当多继承模式(当C
实现多个接口时)似乎不稳定并导致分段错误时,所有工作都一直有效。
我的嫌犯是真的吗? 有一种更好的方法或众所周知的方法来实现类似的架构吗?
谢谢
我附上了我的真实应用程序的一些简化代码和基本的演员。 首先是常见的定义:
class A
{
public:
typedef A* (*getAobject_fn)(void);
static A * Load( char * filename ) {
void * objhlib = dlopen( filename, RTLD_NOW );
getAobject_fn fp = (getAobject_fn) dlsym( objhlib, "getAobject" );
return fp();
}
virtual A * Create() = 0;
virtual void * getInterface( int ifEnum ) = 0;
};
class B1 : virtual public A {
public:
// some inline or pure virtual functions here
};
class B2 : virtual public A {
public:
// some other inline or pure virtual functions here
};
共享库的标题(实际上C类对主应用程序是不可见的,因为.so在运行时加载而.h不包含在主应用程序中):
class C : public B1, public B2
{
public:
A * Create() { return new C; }
void * getInterface( int ifEnum ) {
if( ifEnum==INTERFACE_ID_B1 )
return dynamic_cast<B1*>(this);
if( ifEnum==INTERFACE_ID_B2 )
return dynamic_cast<B2*>(this);
return 0;
}
};
extern "C" { A * getAobject(); } // probably useless
在共享库的主体中:
C obj;
A * getAobject() { return dynamic_cast<A*>(&obj); } // equivalent to return &obj;
最后,在主要应用程序中:
// In the Init procedure
A * p = A::Load( "foo.so" );
A * pBobj = p->Create(); // pBobj is kept for the entire lifetime
B1 * b1 = reinterpret_cast<B1*>(pBobj->getInterface( INTERFACE_ID_B1 ));
B2 * b2 = reinterpret_cast<B2*>(pBobj->getInterface( INTERFACE_ID_B2 ));
// NOTE:
// b1 = dynamic_cast<B1*>(pBobj) and
// b2 = dynamic_cast<B2*>(pBobj)
// will fail
答案 0 :(得分:4)
如何加载共享对象? g ++使用的地址
解决dynamic_cast
的RTTI信息。传统上,通过
在Unix中默认,要加载的第一个符号将被使用
所有的共享对象,所以没有问题。这个
但是,取决于dlopen
中使用的模式;如果RTLD_LOCAL
指定了该共享对象中的符号(以及共享对象中的符号)
由于加载而隐式加载的对象
共享对象)在共享对象外部不可见。
(我在Java插件中遇到过这个问题.Java会加载
共享对象RTLD_LOCAL
,而dynamic_cast
将不起作用
共享对象隐式加载的共享对象
Java加载。)
关于主可执行文件:大多数Unix链接器都会生成
可用的符号,就像可执行文件已加载一样
使用RTLD_GLOBAL
。大多数,但不是全部; GNU链接器,用于
例如,不执行此操作,以及主可执行文件中的符号
不适用于共享对象。如果你需要它们
可用时,您必须在构建时使用-rdynamic
选项
可执行文件(转换为-export-dynamic
选项
链接器)。或者,如果你需要打破你的
您可以将应用程序分解为单独的共享对象
考虑将几乎所有东西放在共享对象中,以及
使主要可执行文件只是一个简单的库加载器,
在所有共享对象上调用dlopen
,然后dlsym
获取要执行的实际函数的地址,
并通过地址调用它。 (这基本上就是我们的方式
解决了Java插件的问题。所有Java加载了
我们的加载器模块,然后执行dlopen
。通过做它们
显然,我们可以控制dlopen
的选项。)
编辑:
在重读你的问题时,我不确定这是对的
回答。您正在通过void*
,这意味着您
无法访问RTTI(甚至无法访问vtable)。规则
关于void*
(C ++规则,这次,不 g ++)很清楚:
你可以用void*
做的唯一事情是将它转换回来
原始指针类型。特别是序列
Derived*
→void*
→Base*
是未定义的行为。
如果仅涉及单个继承,它通常工作
(但即便如此,它仍然是未定义的行为),但不是
除此以外。因此,如果共享对象将A*
转换为
一个void*
,void*
后来转换为B*
,你
有不确定的行为,不应指望它的工作。
首先将void*
转换为A*
,然后转换它
到B*
,应该工作。然而,更好的是:宣布
函数你打电话来返回A*
,而不是
一个void*
。一般来说,你应该避免void*
可能,特别是在C ++中。