Як розробити програму C ++, щоб дозволити імпорт функцій під час виконання?


10

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

Звичайно, я використовував пошук, але не знайшов жодної прямо пов'язаної відповіді.

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

Тепер ідея полягає у наданні користувачеві рамки, яка дозволяє йому складати будь-які функції відповідно до його потреб, тобто відображати будь-яку фізичну поведінку. Рамка повинна забезпечувати функціональні можливості підключення виходів і входів різних функцій. Тому рамка забезпечує клас контейнерів. Я називаю це КОМПОНЕНТ, який здатний вмістити один або багато модельних об'єктів (ФУНКЦІЯ). Ці контейнери можуть також містити інші компоненти (пор. Складений зразок), а також з'єднання (З'єднувач) між параметрами функції. Крім того, клас компонентів забезпечує деякі загальні числові функції, такі як математичний розв'язувач тощо.

Склад функцій повинен виконуватися під час виконання. У першу чергу, користувач повинен мати можливість створити композицію шляхом імпорту 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 ++ має покажчики функцій та об'єкти функцій. Чи всі функції зібрані у виконуваний файл чи вони перебувають у динамічних бібліотеках (на якій платформі)?
Калет

1
Питання занадто широке в тому сенсі, що воно зазвичай вимагає університетського ступеня або з електротехніки / [електронної автоматизації проектування (EDA)] ( en.wikipedia.org/wiki/Electronic_design_automation ) або з машинобудування / комп'ютерного проектування (CAD) . Порівняно кажучи, викликати динамічну бібліотеку C / C ++ дуже просто, див. Умови виклику C для x86 . Може знадобитися маніпулювання стеком (через покажчик стека CPU) та значення регістру процесора.
rwong

1
Функції динамічного завантаження не підтримуються мовою C ++. Вам доведеться переглянути щось, що стосується платформи. Наприклад, компілятор C ++ у Windows повинен підтримувати DLL-файли Windows, які підтримують форму відображення.
Саймон Б

У C ++ дійсно важко викликати функцію, підпис якої (типи аргументів та повернення) невідома під час компіляції. Для цього потрібно знати, як функціонують виклики функцій на рівні складання обраної вами платформи.
Барт ван Інген Шенау

2
Я вирішую це - компілювати c ++ код, який створює інтерпретатор для будь-якої мови, що підтримує команду eval. Проблема вибуху вирішена за допомогою c ++. : P Подумайте, чому це недостатньо добре, і оновіть питання. Це допомагає, коли реальні вимоги зрозумілі.
candied_orange

Відповіді:


13

У чистому стандарті C ++ ви не можете "дозволити імпорт виконання функцій"; відповідно до стандарту, набір функцій C ++ статично відомий під час збирання (на практиці, час зв’язку), оскільки фіксується з об'єднання всіх одиниць перекладу, що складають вашу програму.

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

Деякі сучасні операційні системи дозволяють динамічне завантаження з плагінів . POSIX, зокрема, вказує dlopen& dlsym. У Windows є щось інше LoadLibrary(і нижча модель посилання; вам потрібно чітко анотувати відповідні функції, надані або використовувані плагінами). BTW в Linux ви можете практично dlopenбагато плагінів (дивіться мою manydl.cпрограму , при достатньому терпінні вона може генерувати, а потім завантажити майже мільйон плагінів). Таким чином, ваш XML-річ може призвести до завантаження плагінів. Опис вашого багатокомпонентного / багатокомпонентного опису нагадує мені про сигнали та слоти Qt (для чого потрібен mocпрепроцесор ; вам може знадобитися щось подібне).

Більшість C ++ реалізацій використовують керування іменами . Через це вам краще оголосити extern "C"функції, пов’язані з плагінами (і визначені в них, і доступ до яких здійснюється dlsymз основної програми). Прочитайте C ++ dlopen mini HowTo (принаймні для Linux).

BTW, Qt та POCO є структурами C ++, що забезпечують деякий переносний та вищий рівень підходів до плагінів. І libffi дозволяє викликати функції, підпис яких відомий лише під час виконання.

Інша можливість - вбудувати у свою програму якогось перекладача (наприклад, Lua чи Guile ) (або написати свій власний, як Emacs). Це сильне архітектурне дизайнерське рішення. Ви можете прочитати Lisp In Small Pieces та Pragmatics Мова програмування для отримання додаткової інформації.

Є варіанти або суміші цих підходів. Ви можете використовувати деяку бібліотеку компіляції 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 був API API, щоб ви могли використовувати різні компілятори для різних модулів, якщо користувачі надають свої власні модулі.

DirectShow - це API, який робить все, що я описав, і може бути хорошим прикладом для перегляду.


Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.