RTTI + dlopen + GCC = баг
Если грузить библиотеку, юзающую какой-либо класс, через dlopen, то слияния объектов type_info не произойдет, поэтому имеем забавное поведение, а лично я поимел три часа секса от обнаружения сломанного dynamic_cast до написания тесткейса.
classb.h:
struct a { virtual ~a() {} }; struct b : public a { virtual ~b() {} };
test2.cpp:
#include <typeinfo> #include "classb.h" extern "C" { a* getnewb() { return new b; } const std::type_info& gettypeb() { return typeid (b); } }
test.cpp:
#include <dlfcn.h> #include <typeinfo> #include <iostream> #include <cassert> #include "classb.h" typedef a *(*func_t) (void); int main() { func_t func; const std::type_info& (*type) (void); void *handle = dlopen ("./test2.so", RTLD_NOW); func = (func_t) dlsym (handle, "getnewb"); type = (const std::type_info& (*) (void)) dlsym (handle, "gettypeb"); a *a_ptr = (*func) (); const std::type_info& t = (*type)(); assert (dynamic_cast<b*> (a_ptr)); assert (typeid (b) == t); }
Компилять:
g++ -g -fPIC -o test2.so -shared test2.cpp g++ -g -o testcase test.cpp -ldl
UPD: Если чуть подробнее, type_info::operator== на линугзах (ну и везде, где есть поддержка weak symbols) в общем случае сравнивает указатели на строки с типами вместо честного strcmp по самим строкам. В обычных условиях все type_info для физически одинаковых объектов изменяются так, чтобы указатели указывали (разработчики разрабатывают) на одну и ту же строку, это и есть слияние (merging). Если грузить библиотеку через dlopen, как в примере, то этого не происходит, поэтому получаем интересное поведение, как, например, assertion failed в примере.
Однако, мне не очень понятно, почему функция-член объекта, создающая его клон, определенная в чужой библиотеке и вызываемая в моем коде, не избавляет от всех проблем. Возможно, инлайнится, надо еще об этом подумать.