Явная реализация шаблонного класса и dynamic_cast в общей библиотеке - PullRequest
10 голосов
/ 24 мая 2011

Сегодня я столкнулся с проблемой, которую не могу решить.Я собираю разделяемую библиотеку, которая включает шаблонный класс (Derived<T>, основание которого Base) и некоторые явные экземпляры этого класса.Я хотел бы, чтобы пользователь библиотеки вышел из этого шаблонного класса.Проблема возникает, когда я пытаюсь dynamic_cast экземпляр пользователя от Base* до Derived<T>*.

. Я сузил проблему до этого MWE:

Общая библиотека содержит следующеефайлы:

Base.h

#ifndef BASE_H_
#define BASE_H_

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

#endif /* BASE_H_ */

Производные.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() {
}

ипростой код, который использует библиотеку, может быть:

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;
}

Проблема заключается в том, что когда я создаю общую библиотеку и связываю test.cpp с нейdynamic_cast терпит неудачу.Вот пример выходных данных:

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
===

Однако, если я скомпилирую всю библиотеку и пример вместе, приведение выполнится успешно:

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

Мой вопрос: почему dynamic_cast не удалось?

И, исходя из того, что я хотел бы сохранить структуру классов, как в примере, и продолжать использовать общую библиотеку: как я могу успешно получить приведение Derived<some type>* из Base*?

Ответы [ 2 ]

4 голосов
/ 24 мая 2011

Здесь нет ничего удивительного.Даже для обычных классов без шаблонов вы никогда не должны ожидать, что RTTI будет работать через границы разделяемой библиотеки.Для некоторых компиляторов, в некоторых ОС, с некоторыми опциями компилятора или компоновщика, это может работать, но в целом это не сработает и не требуется (явно оставлено не указано в стандарте).И даже если вы заставите это работать, это будет неустойчиво в долгосрочной перспективе.

По моему опыту, случаи, когда RTTI не может пересекать границы разделяемой библиотеки, намного перевешивают случаи, когда это возможно.

Решение состоит в следующем:

  • Ограничить все конструкции объектов из этих производных типов в коде общей библиотеки, где используется dynamic_cast (это решениедовольно сложно управлять).

  • Не использовать dynamic_cast вообще (это решение идеально, редко применимо).

  • Не использовать общий-libraries (оцените, действительно ли разделяемые библиотеки - то, что вам нужно, или, может быть, предоставьте высокоуровневый интерфейс из вашей разделяемой библиотеки, который не предоставляет полиморфные типы для извлечения (что, по-видимому, предполагает "открытую архитектуру""более уместно в вашем приложении)).

  • Определите свою собственную систему RTTI и оператора приведения (это может быть сложно, в зависимости от ваших навыков, ноt это не составляет большого количества кода, и многие проекты основного потока используют это решение, и вы можете найти множество примеров того, как это сделать).

3 голосов
/ 24 мая 2011

Я предполагаю, что вы работаете в Linux / GCC, потому что в Windows он должен "просто работать".

Он не "просто работает" с GCC, потому что для обеспечения производительности поддержка RTTI в GCC основана на сравнении указателей.Все это объяснено в этом GCC FAQ , в том числе о том, как его можно решить.РЕДАКТИРОВАТЬ: хотя, этот FAQ говорит, что он не работает с dlopen(), в то время как явное связывание с общей библиотекой должно работать;так что, возможно, есть что-то еще, такое как ошибка, упомянутая ниже.

Некоторые другие ссылки, которые я нашел, могут помочь:
dynamic_cast интерфейс из общей библиотеки, которая была загружена lt_dlopen (libtool) не работает
динамическое приведение с интерфейсами
C ++ dynamic_cast ошибка в Mac OS 10.6 Snow Leopard

...