共享库中模板化类和dynamic_cast的显式实例化

发布于 2024-11-09 21:07:29 字数 3566 浏览 4 评论 0原文

今天我遇到了一个似乎无法解决的问题。我正在编译一个共享库,其中包括一个模板类(Derived,其基为Base)和该类的一些显式实例化。我希望库用户从这个模板类扩展。当我尝试将用户的实例从 Base* 动态转换为 Derived* 时,问题就出现了。

我已将问题范围缩小到此 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* 成功获取 Derived* 转换>基础*?

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* ?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

离线来电— 2024-11-16 21:07:29

我假设你使用的是 Linux/GCC,因为在 Windows 上它应该“正常工作”。

它并不“只适用于”GCC,因为为了提高性能,GCC 中的 RTTI 支持依赖于指针比较。 此 GCC 常见问题解答中对此进行了全部解释,包括如何解决它。编辑:不过,这个常见问题解答说它不适用于 dlopen() ,而与共享库的显式链接应该可以工作;所以也许还有其他的东西,比如下面提到的错误。

我发现的一些其他链接可能有帮助:
dynamic_cast 接口由 lt_dlopen(libtool) 加载的共享库不起作用
使用接口进行动态转换
Mac OS 10.6 Snow Leopard 中的 C++dynamic_cast 错误

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

奢欲 2024-11-16 21:07:29

这并不奇怪。即使对于普通的非模板化类,您也不应该期望 RTTI 能够跨共享库边界工作。对于某些编译器,在某些操作系统上,使用某些编译器或链接器选项,它可能可以工作,但一般来说,它不会,也不需要这样做(在标准中明确保留未指定)。即使你让它发挥作用,从长远来看也是不可持续的。

根据我的经验,RTTI 无法跨越共享库边界的情况远远多于可以跨越的情况。

解决方案是:

  • 将这些派生类型的对象的所有构造限制在使用dynamic_cast的共享库代码中(此解决方案很难管理)。

  • 根本不要使用dynamic_cast(这个解决方案是理想化的,很少适用)。

  • 不要使用共享库(评估共享库是否确实是您所需要的,或者可能从共享库公开一个更高级别的接口,该接口不会公开要派生的多态类型(这似乎建议“开放式架构”更适合您的应用程序))。

  • 定义您自己的 RTTI 系统和转换运算符(这可能很困难,具体取决于您的技能,但它并不需要太多代码,并且许多主流项目都使用此解决方案,您可以找到大量有关如何来做到这一点)。

There is no surprise here. Even for normal non-templated classes, you should never expect the RTTI to work across shared-library boundaries. For some compilers, on some OSes, with some compiler or linker options, it might work, but in general, it will not, and is not required to (explicitly left unspecified in the Standard). And even if you make it work, it will be unsustainable in the long-run.

In my experience, the cases when the RTTI cannot cross-over between shared-library boundaries far outweigh the cases when it can.

The solution is to either:

  • Restrict all the constructions of objects from these derived types to within the shared-library code where the dynamic_cast is used (this solution is pretty hard to manage).

  • Do not use dynamic_cast at all (this solution is idealistic, seldom applicable).

  • Do not use shared-libraries (evaluate if shared-libraries are really what you need, or maybe, expose a higher-level interface from your shared-library, that doesn't expose polymorphic types to be derived (which seem to suggest that an "open-architecture" is more appropriate in your application)).

  • Define your own RTTI system and casting operator (this could be hard, depending on your skill, but it doesn't amount to much code, and many main-stream projects use this solution and you can find plenty of examples on how to do this).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文