Как реализовать структуру адаптера на C ++, которая работает как в Linux, так и в Windows

Вот что я пытаюсь сделать:

Я разрабатываю кроссплатформенную IDE (Linux и Windows), которая поддерживает плагины . Мне нужно поддерживать расширяемость с помощью инфраструктуры адаптера, аналогичной той, что предоставляет Eclipse. См. здесь для получения более подробной информации, но в основном мне нужно следующее:

Пусть Адаптированный и Адаптированный будут совершенно несвязанными классами, которые уже существуют, и которыми мы не являемся. разрешено изменять любым способом.Я хочу создать класс AdapterManager , который имеет метод

template  Adapted* adapt( Adaptee* object);

, который создаст экземпляр Adapted с учетом экземпляра Adaptee . Как именно создается экземпляр, зависит от функции адаптера, которую необходимо зарегистрировать в AdapterManager . Каждый новый плагин должен иметь возможность добавлять функции адаптера для произвольных типов.

Вот мои мысли о возможном решении и почему оно не работает:

  • Функции RTTI C ++ 11 и класс type_info предоставляют метод hash_code () , который возвращает уникальное целое число для каждого типа в программе. См. здесь . Таким образом, AdapterManager может просто содержать карту, которая с заданными хэш-кодами для классов Adaptee и Adapter возвращает указатель функции на функцию адаптера. Это делает реализацию вышеупомянутой функции adjust () тривиальной:

     template  Adapted * AdapterManager :: adap (Adaptee * object)
    {
    AdapterMapKey mk (typeid (адаптировано) .hash_code (), typeid (Adaptee) .hash_code ());
    Функция адаптера af = adapterMap.get (mk);
    если (! af) вернуть nullptr;
    return (адаптировано *) af (объект);
    }
    

    Любой плагин может легко расширить структуру, просто вставив дополнительную функцию в карту. Также обратите внимание, что любой подключаемый модуль может попытаться адаптировать любой класс к любому другому классу и добиться успеха, если существует соответствующая функция адаптера, зарегистрированная в AdapterManager , независимо от того, кто ее зарегистрировал.

  • Проблема заключается в сочетании шаблонов и плагинов (общих объектов / библиотек DLL). Поскольку два подключаемых модуля могут создавать экземпляр класса шаблона с одинаковыми параметрами, это потенциально может привести к двум отдельным экземплярам соответствующих структур type_info и потенциально различным результатам hash_code () , что приведет к сломать механизм выше. Функции адаптера, зарегистрированные из одного плагина, могут не всегда работать в другом плагине.
  • В Linux динамический компоновщик, кажется, может иметь дело с несколькими объявлениями типов в разных разделяемых библиотеках при определенных условиях согласно this (пункт 4.2). Однако настоящая проблема заключается в Windows, где кажется, что каждая DLL получит свою собственную версию экземпляра шаблона, независимо от того, определена ли она также в других загруженных DLL или в основном исполняемом файле. Динамический компоновщик кажется довольно негибким по сравнению с тем, который используется в Linux.
  • Я рассмотрел возможность использования явных экземпляров шаблонов, которые, кажется, уменьшают проблему, но все же не решают ее, поскольку два разных плагина могут по-прежнему создавать экземпляры одного и того же шаблона одинаковым образом.

Вопросы:

  1. Кто-нибудь знает, как добиться этого в Windows? Если бы вам было разрешено изменять существующие классы, помогло бы это?
  2. Знаете ли вы другой подход для достижения этой функциональности в C ++, при этом сохраняя все желаемые свойства: без изменений существующих классов, работает с шаблонами, поддерживает подключаемые модули. ins и кроссплатформенность?

Обновление 1:
Этот проект использует структуру Qt для многих вещей, включая инфраструктуру подключаемых модулей. Qt действительно помогает в кроссплатформенной разработке. Если вам известно конкретное решение проблемы Qt, это тоже приветствуется.

Обновление 2:
н.м.Этот комментарий заставил меня понять, что я знаю о проблеме только теоретически и на самом деле не проверял ее. Поэтому я провел некоторое тестирование как в Windows, так и в Linux, используя следующее определение:

template 
class TypeIdTest {
    public:
        virtual ~TypeIdTest() {};
        static int data;
};
template  int TypeIdTest::data;

Этот класс создается в двух разных разделяемых библиотеках / DLL с T = int. Обе библиотеки явно загружаются во время выполнения. Вот что я обнаружил:

В Linux все просто работает:

  • Два экземпляра использовали одну и ту же vtable .
  • Объект, возвращенный typeid , находился по тому же адресу.
  • Даже статический элемент данных был таким же.
  • Таким образом, тот факт, что шаблон был создан в нескольких динамически загружаемых разделяемых библиотеках, не имел абсолютно никакого значения. Компоновщик, кажется, просто использует первый загруженный экземпляр и игнорирует остальные.

В Windows два экземпляра «несколько» различны:

  • typeid для разных экземпляров возвращает type_info объектов по разным адресам. Однако эти объекты равны при тестировании с == . Соответствующие хэш-коды также равны. Похоже, что в Windows равенство между типами устанавливается с помощью имени типа - что имеет смысл. Все идет нормально.
  • Однако vtables для двух экземпляров были разными. Я не уверен, насколько это большая проблема. В своих тестах я смог использовать dynamic_cast для преобразования экземпляра TypeIdTest в производный тип через границы разделяемой библиотеки.
  • Еще одна проблема заключается в том, что каждый экземпляр использовал свою собственную копию данных статического поля . Это может вызвать множество проблем и в основном запрещает использование статических полей в классах шаблонов.

В целом, кажется, что даже в Windows все не так плохо, как я думал, но я все еще не хочу использовать этот подход, учитывая, что при создании экземпляров шаблонов все еще используются отдельные vtables и статическое хранилище. Кто-нибудь знает, как избежать этой проблемы? Я не нашел решения.

9
задан Dimitar Asenov 15 February 2012 в 04:34
поделиться