Как реализовать адаптерную платформу в C ++, которая работает как в Linux, так и в Windows - PullRequest
9 голосов
/ 14 февраля 2012

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

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

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

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

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

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

  • CФункции RTTI ++ 11 и класс 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);
    }
    

    Любой плагин может легко расширить каркас, просто вставив дополнительную карту в карту.Также обратите внимание, что любой плагин может попытаться адаптировать любой класс к любому другому классу и добиться успеха, если существует соответствующая функция адаптера, зарегистрированная в 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;

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

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

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

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

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

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

1 Ответ

1 голос
/ 15 февраля 2012

Я думаю, что Boost Extension имеет дело именно с этой проблемной областью:

В частности, вам будет интереснов том, что автор написал в этом сообщении в блоге: «Управление ресурсами через границы DLL :

RTTI не всегда функционирует должным образом через границы DLL. Проверьте классы type_info, чтобы увидетькак я с этим справляюсь.

Я не уверен, действительно ли его решение действительно надежно, но он наверняка задумывался об этом раньше. На самом деле, есть некоторые примеры, использующие Boost Extensions, которые вы можете датьидти, вы можете использовать его.

...