Что безопасно для подключаемой системы С++?

Плагины в С++ сложны, потому что ABI не определен правильно, и каждый компилятор (или его версия) следует своим собственным правилам. Однако COM на Windows показывает, что можно создать минимальную подключаемую систему, которая позволяет программистам с разными компиляторами создавать плагины для хост-приложения с помощью простого интерфейса.

Давайте будем практичны и оставим стандарт С++, что не очень полезно в этом отношении, в сторону на минуту. Если я хочу написать приложение для Windows и Mac (и, возможно, Linux), которое поддерживает плагины С++, и если я хочу дать авторам плагинов достаточно большой выбор компиляторов (скажем, менее двух лет версий Visual С++, GCC или компилятор Intel С++), на какие функции С++ я могу рассчитывать?

Конечно, я предполагаю, что плагины будут написаны для конкретной платформы.

Сверху моей головы, вот некоторые возможности С++, о которых я могу думать, с тем, что, на мой взгляд, является ответом:

  • vtable layout, использовать объекты через абстрактные классы? (Да)
  • встроенные типы, указатели? (Да)
  • структуры, союзы? (Да)
  • исключения? (Нет)
  • extern "C" функции? (Да)
  • stdcall non-extern "C" функции со встроенными параметрами? (Да)
  • non-stdcall non-extern "C" функции с пользовательскими параметрами? (Нет)

Я был бы признателен за любой опыт, который у вас есть в этой области, которую вы могли бы поделиться. Если вы знаете какое-нибудь умеренно успешное приложение, в котором есть подключаемая система С++, это тоже круто.

Карл

Ответ 1

В журнале Dr Dobb Journal есть статья Создание собственной плагиновой платформы: часть 1, которая является довольно хорошим показанием по этому вопросу. Это начало серии статей, которые охватывают архитектуру, разработку и развертывание платформы межплатформенных платформ C/С++.

Ответ 2

Вы также можете рассмотреть возможность замены обычного интерфейса плагина с помощью интерфейса сценариев. Есть несколько очень хороших привязок для нескольких языков сценариев в C/С++, которые уже решили вашу проблему. Наверное, неплохо было бы задуматься над ними. Например, посмотрите Boost.Python.

Ответ 3

Qt имеет очень приятную систему для плагинов, которые я использовал в прошлом. Он использует систему мета-объектов Qt для преодоления многих проблем, обычно возникающих при разработке плагинов С++.

Один пример: Q_DECLARE_INTERFACE работает, чтобы вы не могли использовать несовместимый плагин. Другой - ключ сборки, чтобы убедиться, что вы загрузили правильный плагин для своей архитектуры, ОС, компилятора. Если вы не используете систему плагинов Qt, вам придется беспокоиться и изобретать решения самостоятельно. Это не обязательно ракетостроение, и я не говорю, что вы потерпите неудачу, но ребята из Trolltech довольно умны и долго думали об этом, и я предпочел бы использовать то, что они создали, чем изобретать колесо самостоятельно.

Другой пример: RTTI обычно не работает через границы DLL, но при использовании Qt такие вещи, как qobject_cast, которые полагаются на метаобъектную систему, работают через границы DLL.

Ответ 4

Я думаю, что вы безопасно создаете систему плагинов на основе:

  • Упаковка функций плагина в библиотеку (.dll,.so и т.д.)
  • Требование о том, чтобы плагин показывал экспорт ключей на языке C.
  • Требование, чтобы плагин реализовал (и вернул указатель/ссылку) абстрактный С++-интерфейс.

Вероятно, самая успешная система плагина С++: старый добрый Adobe Photoshop. И если не это, один из виртуальных форматов синтезатора, таких как VSTi и т.д.

Ответ 5

В книге Imperfect С++ от Matthew Wilson есть хорошая информация об этом.

Совет по-видимому: до тех пор, пока вы используете тот же (или эквивалентный) компилятор, вы можете использовать С++, иначе вам лучше использовать C в качестве интерфейса поверх вашего кода на С++.

Ответ 8

У меня есть собственный движок игры, в котором есть плагин С++.

У меня есть код в заголовочных файлах, поэтому он попадает в блок компиляции плагинов.

Более крупные функции, которые живут в главном движке, вызывается через экспортированную функцию C (плагин вызывает MyObject_somefunction (MyObject * obj), который в движке просто вызывает obj- > somefunction()). Если вызов функции C уродлив для вашего вкуса, то с некоторым обманом заголовка, когда заголовок включен в плагин, используйте функцию-член #defined для вызова функции C:

#if defined(IN_THE_PLUGIN)
void MyObject::somefunction() { MyObject_somefunction(this); }
#endif

Виртуальные функции либо должны быть чистыми, либо код живет в файле заголовка. Если я не наследую от класса и просто просто запускаю один, виртуальный функциональный код может жить в движке, но тогда класс должен экспортировать некоторые C-функции для создания и уничтожения объекта, который вызывается из плагина.

В принципе, трюки, которые я использовал, с целью поддержания общей независимости платформы, просто составляют экспорт C и трюки с файлами заголовков.