Что рекомендуется для подключения систем в приложениях?

Каковы "нормальные" способы создания плагинов в скомпилированных языках (C#/C/C++/D)? Меня особенно интересуют подходы, не зависящие от языка, но язык не является неприемлемым.

В настоящее время подходы "время компиляции" (просто включите код или нет, и все работает) действительны, но предпочтительны вещи, которые могут перейти на более динамичный подход.

Что касается типа среды выполнения, меня больше интересует механика загрузки плагина и все такое, чем разработка интерфейса плагина / приложения.

РЕДАКТИРОВАТЬ: Кстати плагин будет раб, а не хозяин. Основное действие плагина заключается в том, что в данной ситуации его будут призывать "делать свое дело" и получать объект среды, который он должен использовать для получения того, что ему нужно для работы.

8 ответов

Решение

Для скомпилированных языков (где скомпилированный означает, что программа запускается как собственный исполняемый файл, без какой-либо виртуальной машины), вам в значительной степени необходимо использовать какой-то подход к общей библиотеке для конкретной платформы. В Windows это означает использование DLL.

Вы определяете свой интерфейс плагина в терминах набора функций (с конкретными именами, аргументами и соглашениями о вызовах). Затем вы загружаете адреса функций в общей библиотеке и отправляетесь в город. В Windows это означает использование GetProcAddress (), а затем приведение возвращаемого значения к указателю на функцию соответствующего типа в C или любого другого эквивалента на языке, который вы используете.

Другой вариант, который может быть или не быть более желательным, состоит в том, чтобы запустить виртуальную машину для другого языка из вашего собственного приложения, и чтобы плагины были кодом для этого языка. Например, вы можете запустить виртуальную машину Python с использованием CPython и динамически загружать модули Python.

Mono.Addins кажется хорошим решением для.NET. Я полагаю, что он включает API, позволяющий вам загружать плагины (или надстройки) из репозитория и динамически загружать его в работающую сборку.

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

Статические конструкторы чаще всего "умны" в плохом смысле. Поскольку вам придется загружать (C/C++: dlopen и friends под Linux) плагины по одному в любом случае (в динамическом случае), вы также можете явно инициализировать их, как и вы. Среди прочего, это может дать вам возможность отклонить плагины без ожидаемых API.

Обратите внимание, вам не нужно использовать библиотеки динамической загрузки для плагинов. Другие механизмы также могут быть использованы: общая память, сокеты и т. Д....

Для плагина подчиненного типа, где каждый плагин инкапсулирует различную реализацию общего набора функций, я бы просто поместил библиотеки DLL в папку плагинов, известную хост-приложению (например, "c:\program files\myapp\plugins"), и затем вызовите библиотеки DLL с хоста через Reflection.

Вы можете сделать какой-то сложный процесс регистрации, когда установлена ​​каждая DLL, но у меня никогда не возникало проблем с простым подходом плагинов в одной папке.

Редактировать: Чтобы сделать это в C#, вы просто добавляете публичный класс в DLL (с ​​именем "Плагин" или что-то в этом роде) и реализуете там необходимые функции. Затем на своем хосте вы создаете объект типа Plugin и вызываете его методы (все с использованием Reflection).

Это действительно зависит от того, что вы хотите сделать. Обычный шаблон Unix, который можно увидеть в Emacs и Gimp, - это написание программы, состоящей из небольшого скомпилированного компонента, который предоставляет основные функциональные возможности, которые интерпретируемый компонент использует для всего. Плагины, которые предоставляют новые функциональные возможности, которые могут быть встроены в приложение, просты, но вы должны быть очень гибкими в тех примитивах, которые вы предоставляете, чтобы это было возможно. С другой стороны, представьте себе редактор фотографий, который можно сохранять в нескольких форматах. Вы хотите позволить людям писать свои собственные обработчики формата файла. Это требует, чтобы ваш код использовал простой набор примитивов, а затем выбирал реализацию во время выполнения. В прямой (Unix) C используйте dlopen, в C++ используйте extern C, который ограничивает то, что вы можете сделать, и dlopen. В Objective-C у вас есть класс, чтобы сделать это для вас. В первом случае вы делаете или повторно используете переводчика, так что вы можете свободно делать это так, как хотите.

Интерфейсы с пустым регистром (EventSource), кажется, работают хорошо - см. IHttpModule.Init (HttpApplication) в ASP.NET для примера.

Это позволяет автору приложения (который управляет EventSource) добавлять события по мере необходимости, без необходимости расширять интерфейс IPlugin (неизбежно приводя к IPluginEx, IPlugin2, IPlugin2Ex и т. Д.)

Подход, который я использовал (в.NET), заключается в том, чтобы хост сделал первоначальный вызов плагина (через Reflection), запустив плагин и передав ссылку на хост, который плагин сохраняет. Затем плагин вызывает методы на хосте (также через отражение) по мере необходимости.

Я думаю, что с большинством плагинов вызовы обычно делаются в другом направлении (то есть хост будет вызывать плагин по мере необходимости). В моем случае, сами плагины имели элементы пользовательского интерфейса, необходимые для использования функциональности хоста.

Помимо проблем реализации низкоуровневого модуля (например, DLL-библиотеки Windows и проблем с реализацией), игровой движок, который я использую, просто имеет глобальную функцию регистрации в DLL-библиотеках и пытается найти и вызвать ее на каждой dll в каталоге подключаемых модулей. функция регистрации выполняет любую бухгалтерию, необходимую для раскрытия функциональности.

Другие вопросы по тегам