c++ - 如何在可在Linux和Windows上运行的C++中实现适配器框架

标签 c++ templates dll cross-platform shared-libraries

这是我正在尝试做的事情:

我正在开发一个支持插件的跨平台IDE(Linux和Windows)。我需要使用与Eclipse提供的适配器框架相似的适配器框架来支持可扩展性。有关更多详细信息,请参见here,但是基本上我需要以下内容:

AdapteeAdapted是完全不相关的类,它们已经存在并且不允许以任何方式进行更改。我想创建一个具有方法的AdapterManager

template <class Adaptee, class Adapted> Adapted* adapt( Adaptee* object);

给定Adapted实例,它将创建Adaptee实例。如何精确创建实例取决于适配器功能,该功能必须向AdapterManager注册。每个新插件都应能够为任意类型提供适配器功能。

这是我对可能的解决方案以及为何不起作用的想法:
  • C++ 11的RTTI函数和type_info类提供了hash_code()方法,该方法为程序中的每种类型返回唯一的整数。参见here。因此,AdapterManager可以仅包含一个映射,该映射给出了Adaptee和Adapter类的哈希码,从而返回指向适配器函数的函数指针。这使得上面的adapt()函数的实现很简单:
    template <class Adaptee, class Adapted> Adapted* AdapterManager::adapt( Adaptee* object)
    {
      AdapterMapKey mk( typeid(Adapted).hash_code(), typeid(Adaptee).hash_code());
      AdapterFunction af = adapterMap.get(mk);
      if (!af) return nullptr;
      return (Adapted*) af(object);
    }
    

    任何插件都可以通过简单地将附加功能插入 map 来轻松扩展框架。还要注意,如果存在使用AdapterManager注册的相应适配器功能,则无论是谁注册的,任何插件都可以尝试使任何类适应其他任何类并成功。
  • 一个问题是模板和插件(共享对象/DLL)的组合。由于两个插件可以使用相同的参数实例化模板类,因此有可能导致对应的type_info结构的两个单独实例以及潜在的hash_code()结果不同,这将破坏上面的机制。从一个插件注册的适配器功能可能并不总是在另一个插件中起作用。
  • 在Linux中,根据this(第4.2点),动态链接器似乎能够在某些条件下处理不同共享库中类型的多个声明。但是,真正的问题出在Windows中,似乎每个DLL都会获得自己的模板实例化版本,而不管它是否也在其他已加载的DLL或主可执行文件中定义。与Linux中使用的动态链接器相比,动态链接器似乎非常不灵活。
  • 我已经考虑过使用显式模板实例化,这似乎可以减少问题,但是仍然无法解决,因为两个不同的插件可能仍以相同的方式实例化同一模板。

  • 问题:
  • 有谁知道在Windows中实现此目标的方法吗?如果允许您修改现有的类,这会有所帮助吗?
  • 您是否知道在C++中实现此功能的另一种方法,同时仍保留所有所需的属性:无需更改现有类,可以使用模板,支持插件并且是跨平台的?

  • 更新1:
    该项目将Qt框架用于许多事情,包括插件基础结构。 Qt确实有助于跨平台开发。如果您知道针对该问题的Qt特定解决方案,也欢迎您。

    更新2:
    n.m.的评论使我意识到,我只在理论上了解该问题,而没有实际测试过。因此,我使用以下定义在Windows和Linux上进行了一些测试:
    template <class T>
    class TypeIdTest {
        public:
            virtual ~TypeIdTest() {};
            static int data;
    };
    template <class T> int TypeIdTest<T>::data;
    

    此类在两个不同的共享库/DLL中以T = int实例化。这两个库都在运行时显式加载。这是我发现的:

    在Linux中,一切正常:
  • 两个实例使用相同的vtable。
  • typeid返回的对象位于同一地址。
  • 即使静态数据成员也相同。
  • 因此,在多个动态加载的共享库中实例化模板的事实完全没有区别。链接器似乎只是使用第一个加载的实例,而忽略其余实例。

  • 在Windows中,两个实例化在“某种程度上”是不同的:
  • 不同实例的typeid在不同地址返回type_info对象。但是,当使用==测试时,这些对象是相等的。相应的哈希码也相等。在Windows上,似乎类型之间的相等性是使用类型的名称建立的-这很有意义。到现在为止还挺好。
  • 但是,两个实例的vtables不同。我不确定这有多严重。在我的测试中,我能够使用dynamic_castTypeIdTest的实例向下转换为跨共享库边界的派生类型。
  • 还有一个问题是,每个实例都使用其自己的静态字段data的副本。这可能会导致很多问题,并且基本上不允许模板类中的静态字段。

  • 总体而言,似乎即使在Windows中,情况也没有我想像的那么糟糕,但是由于模板实例化仍然使用不同的vtable和静态存储,因此我仍然不愿意使用这种方法。有谁知道如何避免这个问题?我没有找到任何解决方案。

    最佳答案

    我认为Boost Extension完全可以解决此问题域:

  • http://boost-extension.redshoelace.com/docs/boost/extension/index.html

    (in preparation for this library's submission to Boost for review)


  • 特别是您可能会对作者在this blog post: "Resource Management Across DLL Boundaries中写的内容感兴趣:

    RTTI does not always function as expected across DLL boundaries. Check out the type_info classes to see how I deal with that.



    我不确定他的解决方案是否确实可靠,但是他确实曾考虑过这个想法。实际上,您可以尝试使用Boost扩展的一些示例,您可能想要使用它。

    关于c++ - 如何在可在Linux和Windows上运行的C++中实现适配器框架,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9275065/

    相关文章:

    java - 通过一些 JAR 手术修复 Java 类路径问题

    c++ - Debug模式与 Release模式下的 Visual Studio (C++) 调试

    c++ - 无法让我的模板工作。非法使用显式模板参数?

    c++ - Visual Studio C++ - std::map 插入失败

    ruby-on-rails - 如何在 erb 模板中显示应用程序数据?

    c++ - 如果是指针,则启用模板

    visual-studio - UWP平台上的FFmpeg.AutoGen

    c++ - 模板问题 ('typename' 因为不是模板函数参数)

    c++ - 编译C++代码报错 "undefined reference to",原来的makefile代码编译通过

    c++ - 为什么我会收到 "Expression is not assignable"错误?