10

今日、私は解決できないような問題に遭遇しました。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_ */

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() {
}

また、ライブラリを使用する単純なコードは次のようになります。

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*ますか?

4

2 に答える 2

4

ここに驚きはありません。テンプレート化されていない通常のクラスであっても、RTTI が共有ライブラリの境界を越えて機能するとは決して考えるべきではありません。一部のコンパイラでは、一部の OS では、一部のコンパイラまたはリンカー オプションを使用して動作する可能性がありますが、一般的には動作せず、必須でもありません (標準では明示的に指定されていません)。そして、それを機能させたとしても、長期的には持続可能ではありません。

私の経験では、RTTI が共有ライブラリの境界を越えることができない場合は、できる場合よりもはるかに重要です。

解決策は次のいずれかです。

  • これらの派生型からのオブジェクトのすべての構築を、dynamic_cast が使用される共有ライブラリ コード内に制限します (この解決策は管理が非常に困難です)。

  • dynamic_cast をまったく使用しないでください (この解決策は理想的であり、ほとんど適用されません)。

  • 共有ライブラリを使用しないでください (共有ライブラリが本当に必要なものであるかどうかを評価するか、派生するポリモーフィック型を公開しない共有ライブラリから高レベルのインターフェイスを公開する可能性があります (これは、アプリケーションでは「オープンアーキテクチャ」の方が適切です))。

  • 独自の RTTI システムとキャスト オペレーターを定義します (スキルによっては難しいかもしれませんが、多くのコードにはなりません。また、多くの主流プロジェクトがこのソリューションを使用しており、これを行う方法に関する多くの例を見つけることができます。 )。

于 2011-05-24T16:53:10.050 に答える
4

Linux/GCC を使用していると仮定します。Windows では「正常に動作する」はずだからです。

GCC での RTTI サポートのパフォーマンスはポインター比較に依存しているため、GCC では「うまく機能する」わけではありません。解決方法を含め、すべてこの GCC FAQで説明されています。dlopen()編集: ただし、この FAQ は、共有ライブラリとの明示的なリンクは機能するはずですが、機能しないと述べています。そのため、以下で説明するバグなど、何か他のものがある可能性があります。

私が見つけた他のいくつかのリンクが役に立ちます
:

于 2011-05-24T15:06:00.760 に答える