Xcode 4.3 / 4.4 typeinfo is lost for a class created in a dynamically loaded shared library only if the class overrides the method

I ran into a problem using dynamic_cast for objects created in a loaded shared library, but only if the class contains a method that overrides another method.

I am using Xcode 4.3 with the Apple LLVM 3.1 compiler. I compiled the same code with gcc and clang on linux and have no problem, so I assume this is a compiler error in Xcode, but has anyone seen this before?

Suppose the class definitions in the heading are "test3.h"

#pragma once class c1 { public: virtual ~c1 (); virtual void foo (); }; class c2 : public c1 { public: void foo () override; }; class c3 : public c1 { public: }; 

Suppose the implementation code in a static library in the source file is called test3.cpp

 #include "test3.h" c1::~c1 () { } void c1::foo () { } void c2::foo () { } 

Suppose a simple simple library in the source file test2.cpp

 #include "test3.h" extern "C" c1 * get1 () { return new c2; } extern "C" c1 * get2 () { return new c3; } 

Suppose a simple executable application in the source file test1.cpp

 #include "test3.h" #include <dlfcn.h> #include <iostream> int main () { auto lib (dlopen ("libtest2.dylib", RTLD_NOW | RTLD_GLOBAL)); auto a1 (dlsym (lib, "get1")); auto a2 (dlsym (lib, "get2")); auto f1 ((c1 * (*) ())a1); auto f2 ((c1 * (*) ())a2); auto o1 (f1 ()); auto o2 (f2 ()); auto d1 (dynamic_cast <c2 *> (o1)); auto d2 (dynamic_cast <c3 *> (o2)); auto result1 (d1 != 0); auto result2 (d2 != 0); std::cout << result1 << std::endl; std::cout << result2 << std::endl; } 

When the test program is running, result1 is false, and result2 is true. I expect both results1 and result2 to be true.

Has anyone seen this or thought of a workaround?

+4
source share
2 answers

I think the cause of this problem is twofold. First, dynamic transformation uses equality comparison for RTTI objects. Secondly, there are two of them: one in the main and one in the dynamic library.

The workaround for this problem should be to ensure that the class to be shared is in a separate dynamic link library and linked to both your main line and all other shared libraries.

Indeed, this is a bug in the library / compiler: it should not use pointer equality to compare the dynamic type.


Unfortunately. I tried this and it did not fix my problem (g ++ messed it up too). It is strange that I correctly catch the exception in another library, dragging it, catching it correctly with the base class pointer, but dynamic_cast does not work. Therefore, my recommendation does not work (on OSX). Unfortunately.

0
source

You cannot use a static library for code for implementing C ++ classes, if you are going to transfer them from .dylib to another application - you need to use a common object.

The reason is that during the connection you get personal copies of type information in both libtest2.dylib and test1 ; and they will be incompatible with each other.

If you want it to work, you need to export the classes from test3.cpp/h to .dylib , which is associated with both libtest2.dylib and the test1 application.

Makefile example:

 CXX=clang CXXFLAGS=-std=c++11 all: libshlib.dylib libtest2.dylib test1 clean: rm -f *.o test1 *.dylib test3.o: test3.cpp test3.h $(CXX) $(CXXFLAGS) -c -fPIC test3.cpp -o test3.o libshlib.dylib: test3.o $(CXX) $(CXXFLAGS) -fPIC -shared test3.o -o $@ -lstdc++ test2.o: test2.cpp test3.h $(CXX) $(CXXFLAGS) -c -fPIC test2.cpp -o test2.o libtest2.dylib: libshlib.dylib test2.o $(CXX) $(CXXFLAGS) -o $@ -shared test2.o -lstdc++ -L. -lshlib test1: test1.o test3.h $(CXX) $(CXXFLAGS) -o $@ test1.o -lstdc++ -L. -lshlib test1.o: test1.cpp test3.h $(CXX) $(CXXFLAGS) -c -o $@ test1.cpp 
0
source

All Articles