向下转换从动态库返回的类型
Downcasting types returned from dynamic library
我正在编写一个 returns 指向基础 class 的动态库,但我想将它们向下转换为派生 class。示例:
//Library code:
class A
{
public:
A(void) = default;
virtual ~A(void) = default;
virtual void Foo(void)
{
std::cout << "A::Foo\n";
};
};
class B : public A
{
public:
B(void) = default;
void Foo(void) override
{
std::cout << "B::Foo\n";
}
virtual void Bar(void)
{
std::cout << "B::Bar\n";
}
};
class Factory
{
public:
Factory(void) = default;
virtual std::shared_ptr<A> CreateB(void)
{
return std::shared_ptr<A>{new B{}};
}
};
extern "C" std::shared_ptr<Factory> CreateFactory(void)
{
return std::make_shared<Factory>();
}
//Application code:
int main(int argc, char* argv[])
try
{
auto handle = dlopen("./Dynamic.so", RTLD_LAZY);
if (handle == nullptr)
{
throw std::runtime_error{dlerror()};
}
auto factoryaddress =
reinterpret_cast<std::shared_ptr<Factory>(*)(void)>(
dlsym(handle, "CreateFactory"));
if (factoryaddress == nullptr)
{
dlclose(handle);
throw std::runtime_error{dlerror()};
}
auto factory = factoryaddress();
auto a = factory->CreateB();
a->Foo();
auto b = std::dynamic_pointer_cast<B>(a);
b->Bar();
std::cin.get();
}
我明白了
/usr/include/c++/5/bits/shared_ptr.h:458: undefined reference to `typeinfo for B'
/usr/include/c++/5/bits/shared_ptr.h:458: undefined reference to `typeinfo for A'
我打赌你展示的代码与实际尝试编译的代码不同。为了有构建时链接问题,你必须
- 使用 RTTI 支持进行编译,这是因为您使用了
dynamic_cast
和
- 在 class 中有一个非内联虚函数,您使用它的 RTTI 信息。
在你的代码中,一切都是内联的,但是如果你把 ~A()
放到一个单独的源文件中(不在公共头文件中),你会得到这个错误。
解决方案很简单,但有点难看。所有导出的 classes 必须具有所有内联虚函数。您可以拥有一个具有实际实现的私有非内联非虚拟函数,并且只需从另一个调用一个。
所有这一切背后的原因是,如果翻译单元定义了 class 的第一个非内联虚函数,则会发出 RTTI 信息(typeinfo
和一些其他符号)。在您的情况下,当您编译应用程序时,编译器看不到任何虚函数定义,并假定 typeinfo
s 在其他某个目标文件中。这不是真的,因此链接器失败。
当所有虚函数都是内联时,RTTI 内容在每个引用 class 的目标文件中作为弱(a.k.a."comdat" 部分)发出。因此,您可以在库和应用程序之间共享 class,两者将具有相同的弱 RTTI 符号。当应用程序加载库时,第二组重复的 RTTI 符号被丢弃,库代码链接到应用程序代码中已经存在的符号。这是常见的弱符号机制。
我正在编写一个 returns 指向基础 class 的动态库,但我想将它们向下转换为派生 class。示例:
//Library code:
class A
{
public:
A(void) = default;
virtual ~A(void) = default;
virtual void Foo(void)
{
std::cout << "A::Foo\n";
};
};
class B : public A
{
public:
B(void) = default;
void Foo(void) override
{
std::cout << "B::Foo\n";
}
virtual void Bar(void)
{
std::cout << "B::Bar\n";
}
};
class Factory
{
public:
Factory(void) = default;
virtual std::shared_ptr<A> CreateB(void)
{
return std::shared_ptr<A>{new B{}};
}
};
extern "C" std::shared_ptr<Factory> CreateFactory(void)
{
return std::make_shared<Factory>();
}
//Application code:
int main(int argc, char* argv[])
try
{
auto handle = dlopen("./Dynamic.so", RTLD_LAZY);
if (handle == nullptr)
{
throw std::runtime_error{dlerror()};
}
auto factoryaddress =
reinterpret_cast<std::shared_ptr<Factory>(*)(void)>(
dlsym(handle, "CreateFactory"));
if (factoryaddress == nullptr)
{
dlclose(handle);
throw std::runtime_error{dlerror()};
}
auto factory = factoryaddress();
auto a = factory->CreateB();
a->Foo();
auto b = std::dynamic_pointer_cast<B>(a);
b->Bar();
std::cin.get();
}
我明白了
/usr/include/c++/5/bits/shared_ptr.h:458: undefined reference to `typeinfo for B'
/usr/include/c++/5/bits/shared_ptr.h:458: undefined reference to `typeinfo for A'
我打赌你展示的代码与实际尝试编译的代码不同。为了有构建时链接问题,你必须
- 使用 RTTI 支持进行编译,这是因为您使用了
dynamic_cast
和 - 在 class 中有一个非内联虚函数,您使用它的 RTTI 信息。
在你的代码中,一切都是内联的,但是如果你把 ~A()
放到一个单独的源文件中(不在公共头文件中),你会得到这个错误。
解决方案很简单,但有点难看。所有导出的 classes 必须具有所有内联虚函数。您可以拥有一个具有实际实现的私有非内联非虚拟函数,并且只需从另一个调用一个。
所有这一切背后的原因是,如果翻译单元定义了 class 的第一个非内联虚函数,则会发出 RTTI 信息(typeinfo
和一些其他符号)。在您的情况下,当您编译应用程序时,编译器看不到任何虚函数定义,并假定 typeinfo
s 在其他某个目标文件中。这不是真的,因此链接器失败。
当所有虚函数都是内联时,RTTI 内容在每个引用 class 的目标文件中作为弱(a.k.a."comdat" 部分)发出。因此,您可以在库和应用程序之间共享 class,两者将具有相同的弱 RTTI 符号。当应用程序加载库时,第二组重复的 RTTI 符号被丢弃,库代码链接到应用程序代码中已经存在的符号。这是常见的弱符号机制。