Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Explicit instantiation of a templated class and dynamic_cast in a shared library

I stumbled into a problem today that I can't seem to solve. I am compiling a shared library that includes a templated class (Derived<T>, whose base is Base) and some explicit instantiations of this class. I would like the library user to extend from this templated class. The problem arises when I try to dynamic_cast the user's instance from Base* to Derived<T>*.

I have narrowed down the problem to this MWE:

The shared library contains the following files:

Base.h

#ifndef BASE_H_
#define BASE_H_

class Base {
public:
    Base();
    virtual ~Base();
};

#endif /* BASE_H_ */

Derived.h

#ifndef DERIVED_H_
#define DERIVED_H_    
#include <Base.h>

template <typename T>
class Derived : public Base {
public:  
    Derived();
    virtual ~Derived();
};

#endif /* DERIVED_H_ */

Derived.cpp

#include <Derived.h>

template <typename T>
Derived<T>::Derived() :
    Base() {
}

template <typename T>
Derived<T>::~Derived() {
}

// explicit instantiations
template class Derived<float>;
template class Derived<double>;
template class Derived<long double>;

Helper.h

#ifndef HELPER_H_
#define HELPER_H_

#include <Base.h>

class Helper {
public:
    Helper(Base* m);
    virtual ~Helper();

};

#endif /* HELPER_H_ */

Helper.cpp

#include <Helper.h>
#include <Base.h>
#include <Derived.h>

#include <iostream>

using namespace std;

Helper::Helper(Base* m) {

    cout << "after received " << m << endl;
    cout << "after fom: " <<  dynamic_cast< Derived<float>* >(m) << endl;
    cout << "after dom: " <<  dynamic_cast< Derived<double>* >(m) << endl;
    cout << "after ldom: " <<  dynamic_cast< Derived<long double>* >(m) << endl;
    cout << "===" << endl;
}

Helper::~Helper() {
}

And a simple code that uses the library could be:

test.cpp

#include <Derived.h>
#include <Helper.h>

#include <iostream>

using namespace std;

class MyModel : public Derived<double> {
public:
    MyModel() : Derived<double>() {
    };

    virtual ~MyModel() {
    };        

};

int main(int argc, char *argv[]) {

    MyModel om1;
    cout << "created mymodel " << &om1 << endl;
    cout << "before fom: " <<  dynamic_cast< Derived<float>* >(&om1) << endl;
    cout << "before dom: " <<  dynamic_cast< Derived<double>* >(&om1) << endl;
    cout << "before ldom: " <<  dynamic_cast< Derived<long double>* >(&om1) << endl;
    cout << "===" << endl;
    Helper root(&om1);

    return 0;
}

The problem is that when I create a shared library and link test.cpp against it, the dynamic_cast fails. Here's an example output:

created mymodel 0x7fff5fbff3e0
before fom: 0
before dom: 0x7fff5fbff3e0
before ldom: 0
===
after received 0x7fff5fbff3e0
after fom: 0
after dom: 0  // <<< Here I expected it to succeed and return a non-null pointer
after ldom: 0
===

However, if I compile the whole library and example together, the cast succeeds:

created mymodel 0x7fff5fbff3e0
before fom: 0
before dom: 0x7fff5fbff3e0
before ldom: 0
===
after received 0x7fff5fbff3e0
after fom: 0
after dom: 0x7fff5fbff3e0
after ldom: 0
===

My question is : Why is the dynamic_cast failing?

And, under the premise that I would like to maintain a class structure like the example, and continue to use a shared library: how can I successfully obtain the Derived<some type>* cast from a Base* ?

like image 890
YuppieNetworking Avatar asked May 24 '11 14:05

YuppieNetworking


1 Answers

I assume you are on Linux/GCC, because on Windows it should "just work".

It does not "just work" with GCC, because for performance RTTI support in GCC relies on pointer comparison. It is all explained in this GCC FAQ, including how it can be resolved. EDIT: though, this FAQ says that it does not work with dlopen() while explicit linking with a shared library should work; so maybe there is something else, such as the bug mentioned below.

Some other links I found that can be of help:
dynamic_cast an interface from a shared library which was loaded by lt_dlopen(libtool) doesn't work
dynamic cast with interfaces
C++ dynamic_cast bug in Mac OS 10.6 Snow Leopard

like image 133
Alexey Kukanov Avatar answered Oct 28 '22 23:10

Alexey Kukanov