如何在C++中实现适用于Linux和Windows的适配器框架

Dim*_*nov 9 c++ dll templates cross-platform shared-libraries

这是我想要做的:

我正在开发支持插件的跨平台IDE(Linux和Windows).我需要使用类似于Eclipse提供的适配器框架来支持可扩展性.有关详细信息,请参阅此处,但基本上我需要以下内容:

AdapteeAdapted是已经存在的,而我们是不允许以任何方式改变完全无关的类.我想创建一个AdapterManager有方法的类

template <class Adaptee, class Adapted> Adapted* adapt( Adaptee* object);
Run Code Online (Sandbox Code Playgroud)

这将创建Adapted给定实例的实例Adaptee.如何创建实例取决于必须注册的适配器函数AdapterManager.每个新插件都应该能够为任意类型提供适配器功能.

以下是我对可能解决方案的看法以及为什么它不起作用:

  • C++ 11的RTTI函数和type_info类提供了一种hash_code()方法,它为程序中的每种类型返回一个唯一的整数.看到这里.因此,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);
    }
    
    Run Code Online (Sandbox Code Playgroud)

    任何插件都可以通过简单地在地图中插入附加功能来轻松扩展框架.另请注意,任何插件都可以尝试使任何类适应任何其他类,并且如果存在相应的适配器函数,则AdapterManager无论注册者是谁,都会成功.

  • 这个问题是模板和插件(共享对象/ DLL)的组合.由于两个插件可以使用相同的参数实例化模板类,因此可能会导致相应type_info结构的两个单独实例以及可能不同的hash_code()结果,这将破坏上述机制.从一个插件注册的适配器功能可能并不总是在另一个插件中工作.
  • 在Linux中,动态连接器似乎是能够处理的在根据某些条件下不同的共享库类型的多个声明(点4.2).然而真正的问题是在Windows中,似乎每个DLL都将获得自己的模板实例化版本,无论它是否也在其他加载的DLL或主可执行文件中定义.与Linux中使用的动态链接器相比,动态链接器似乎非常不灵活.
  • 我已经考虑使用显式模板实例化,这似乎可以减少问题,但仍然无法解决它,因为两个不同的插件可能仍然以相同的方式实例化相同的模板.

问题:

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

更新1:
该项目将Qt框架用于许多方面,包括插件基础结构.Qt真的有助于跨平台开发.如果你知道问题的Qt特定解决方案,那也是受欢迎的.

更新2:
nm的评论让我意识到我只是在理论上知道这个问题,并没有真正测试过它.所以我使用以下定义在Windows和Linux中进行了一些测试:

template <class T>
class TypeIdTest {
    public:
        virtual ~TypeIdTest() {};
        static int data;
};
template <class T> int TypeIdTest<T>::data;
Run Code Online (Sandbox Code Playgroud)

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

Linux中一切正常:

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

Windows中,两个实例是"有些"不同的:

  • typeid对不同的情况下返回type_info不同的地址对象.然而,这些对象在测试时是相同的==.相应的哈希码也相等.似乎在Windows上使用类型名称建立类型之间的相等 - 这是有道理的.到现在为止还挺好.
  • 但是这两个实例的vtable是不同的.我不确定这是多少问题.在我的测试中,我能够使用跨共享库边界dynamic_cast向下转换TypeIdTest派生类型的实例.
  • 还有一个问题是每个实例化都使用自己的静态字段副本data.这可能会导致很多问题,并且基本上不允许模板类中的静态字段.

总的来说,似乎即使在Windows中,事情也没有我想象的那么糟糕,但我仍然不愿意使用这种方法,因为模板实例化仍然使用不同的vtable和静态存储.有谁知道如何避免这个问题?我没有找到任何解决方案.

seh*_*ehe 1

我认为 Boost Extension 正是解决这个问题领域:

您尤其会对作者在这篇博文中所写的内容感兴趣:“跨 DLL 边界的资源管理

RTTI 并不总是按预期跨 DLL 边界运行。查看 type_info 类,看看我是如何处理这个问题的。

我不确定他的解决方案是否真的可靠,但他之前确实有过这个想法。事实上,有一些使用 Boost Extensions 的示例,您可以尝试一下,您可能想使用它。