Как разработать программу на C ++, позволяющую импортировать функции во время выполнения?

10

Сегодня я хотел бы задать вам вопрос о возможностях C ++ для реализации конкретной архитектуры программного обеспечения.

Конечно, я использовал поиск, но не нашел прямого ответа.

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

Теперь идея состоит в том, чтобы предоставить пользователю структуру, которая позволяет ему составлять любые функции в соответствии с его потребностями, то есть отображать любое физическое поведение. Структура должна обеспечивать функциональные возможности для соединения выходов и входов различных функций. Следовательно, платформа предоставляет контейнерный класс. Я называю это COMPONENT, который может содержать один или несколько объектов модели (FUNCTION). Эти контейнеры также могут содержать другие компоненты (см. Составной шаблон), а также соединения (CONNECTOR) между параметрами функции. Кроме того, класс компонента предоставляет некоторые общие числовые функции, такие как математическое решение и так далее.

Состав функций должен быть сделан во время выполнения. В первом случае пользователь должен иметь возможность настроить композицию путем импорта XML, который определяет структуру композиции. Позже можно подумать о добавлении GUI.

Чтобы дать вам лучшее понимание, вот очень упрощенный пример:

<COMPONENT name="Main">
  <COMPONENT name="A">
    <FUNCTION name="A1" path="lib/functionA1" />
  </COMPONENT>
  <COMPONENT name="B">
    <FUNCTION name="B1" path="lib/functionB1" />
    <FUNCTION name="B2" path="lib/functionB2" />
  </COMPONENT>
  <CONNECTIONS>
    <CONNECTOR source="A1" target="B1" />
    <CONNECTOR source="B1" target="B2" />
  </CONNECTIONS>        
</COMPONENT>

Нет необходимости углубляться в возможности фреймворка, потому что моя проблема гораздо более общая. Когда код / ​​программа платформы компилируется, описание физической проблемы, а также пользовательские функции не известны. Когда пользователь выбирает (через XML или позже через GUI) функцию, платформа должна прочитать информацию о функции, т.е. должна получить информацию о входных и выходных параметрах, чтобы предложить пользователю возможность соединять функции.

Я знаю принципы рефлексии и знаю, что C ++ не предоставляет эту функцию. Тем не менее, я уверен, что понятие «создание объектов во время выполнения» очень часто требуется. Как мне настроить мою программную архитектуру на C ++ для достижения моей цели? Является ли C ++ правильным языком? Что я пропускаю?

Заранее спасибо!

Ура, Оливер

Оливер
источник
C ++ имеет указатели на функции и функциональные объекты. Все ли функции скомпилированы в исполняемый файл или они находятся в динамических библиотеках (на какой платформе)?
Caleth
1
Вопрос является слишком широким в том смысле, что обычно требуется высшее образование в области электротехники / [автоматизация электронного проектирования (EDA)] ( en.wikipedia.org/wiki/Electronic_design_automation ) или машиностроения / автоматизированного проектирования (CAD) . Для сравнения, вызов динамической библиотеки C / C ++ очень прост, см. Соглашения о вызовах C для x86 . Это может потребовать манипулирования стеком (через указатель стека ЦП) и значениями регистра ЦП.
rwong
1
Динамическая загрузка функций не поддерживается языком C ++. Вам придется взглянуть на что-то платформенное. Например, компилятор C ++ в Windows должен поддерживать библиотеки DLL Windows, которые поддерживают форму отражения.
Симон Б
В C ++ действительно сложно вызвать функцию, чья сигнатура (аргумент и возвращаемый тип) неизвестна во время компиляции. Для этого вам нужно знать, как работают вызовы функций на уровне сборки выбранной вами платформы.
Барт ван Инген Шенау
2
Чтобы решить эту проблему, нужно скомпилировать код на С ++, который создает интерпретатор для любого языка, который поддерживает команду eval. Проблема взрыва решена с помощью C ++. : P Пожалуйста, подумайте, почему этого недостаточно, и обновите вопрос. Это помогает, когда реальные требования ясны.
candied_orange

Ответы:

13

В чистом стандарте C ++ нельзя «разрешить импорт функций во время выполнения»; в соответствии со стандартом, набор функций C ++ статически известен во время сборки (на практике, во время компоновки), так как исправлен из объединения всех блоков перевода, составляющих вашу программу.

На практике большую часть времени (за исключением встроенных систем) ваша программа на C ++ работает над какой-либо операционной системой . Прочитайте Операционные системы: Три Легких Части для хорошего обзора.

Некоторые современные операционные системы позволяют динамическую загрузку из плагинов . POSIX особенно указывает dlopen& dlsym. В Windows есть что-то другое LoadLibrary(и более низкая модель компоновки; вам нужно явно аннотировать соответствующие функции, предоставляемые или используемые плагинами). Кстати, на Linux вы можете практически dlopenмного плагинов (см. Мою manydl.cпрограмму , с достаточным терпением она может сгенерировать, а затем загрузить почти миллион плагинов). Таким образом, ваша вещь XML может стимулировать загрузку плагинов. Ваше описание многокомпонентного / многоконтактного соединения напоминает мне о сигналах и слотах Qt (для этого требуется mocпрепроцессор ; вам может понадобиться что-то подобное).

В большинстве реализаций C ++ используется искажение имен . Из-за этого вам лучше объявить как extern "C"функции, связанные с плагинами (и определенные в них и доступные dlsymиз основной программы). Прочитайте мини- руководство по C ++ dlopen (по крайней мере для Linux).

BTW, Qt и POCO - это фреймворки C ++, обеспечивающие переносимый и высокоуровневый подход к плагинам. А libffi позволяет вам вызывать функции, подпись которых известна только во время выполнения.

Другая возможность - встроить некоторый интерпретатор, такой как Lua или Guile , в вашу программу (или написать свой собственный, как это сделал Emacs). Это сильное архитектурно-дизайнерское решение. Вы можете прочитать Lisp In Small Pieces и Прагматику языка программирования для получения дополнительной информации.

Есть варианты или смеси этих подходов. Вы можете использовать некоторую библиотеку компиляции JIT (например, libgccjit или asmjit). Вы можете сгенерировать во время выполнения некоторый код C и C ++ во временном файле, скомпилировать его как временный плагин и динамически загрузить этот плагин (я использовал такой подход в GCC MELT ).

Во всех этих подходах управление памятью является серьезной проблемой (это свойство «всей программы», и то, что на самом деле является «оболочкой» вашей программы, «меняется»). Вам понадобится хоть какая-то культура в отношении сбора мусора . Прочитайте руководство GC для терминологии. Во многих случаях (произвольные циклические ссылки, где слабые указатели не предсказуемы), схемы подсчета ссылок , близкой к интеллектуальным указателям C ++, может быть недостаточно. Смотрите также это .

Читайте также о динамическом обновлении программного обеспечения .

Обратите внимание, что некоторые языки программирования, в частности Common LispSmalltalk ), более дружественны к идее импорта функций во время выполнения. SBCL - это свободная программная реализация Common Lisp, которая компилируется в машинный код при каждом взаимодействии REPL (и даже способна собирать машинный код для сбора мусора и может сохранить весь файл образа ядра, который впоследствии можно будет легко перезапустить).

Василий Старынкевич
источник
3

Очевидно, что вы пытаетесь создать свой собственный стиль программного обеспечения типа Simulink или LabVIEW, но с нечестивым компонентом XML.

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

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

Каждый компонент должен будет реализовать набор функций для выполнения таких вещей, как:

  • Получить название компонента или другие сведения о нем
  • Получите количество входов или выходов, предоставляемых компонентом
  • Опросить компонент о конкретном входе нашего вывода
  • Соедините входы и выходы вместе
  • и другие

И это только для настройки вашего графика. Вам понадобятся дополнительные функции, определенные для организации фактического исполнения вашей модели. Каждая функция будет иметь определенное имя, и все компоненты должны иметь эти функции. Все, что специфично для компонента, должно быть доступно через этот API, идентичным образом от компонента к компоненту.

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

Короче говоря, мало что из этого на самом деле относится к C ++, но вам придется реализовать некоторую информацию о типах времени выполнения, адаптированную к вашей конкретной проблемной области. С каждой функцией, определенной API, вы будете знать, какие имена функций вызывать во время выполнения, и будете знать типы данных каждого из этих вызовов, и вы просто используете обычную загрузку старой динамической библиотеки, чтобы выполнить это. Это придет с достаточным количеством шаблонного, но это только часть жизни.

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

DirectShow - это API, который делает все, что я описал, и может быть хорошим примером для просмотра.

как зовут
источник
-2

Вы можете использовать leadelf library.so, а затем сверять символы с заголовком библиотеки. Вот скрипт php оболочки, который делает это https://github.com/comarius/elf_resolver

user2195463
источник