З C ++ існує багато проблем з портативністю, що відбувається лише через відсутність її стандартизації на бінарному рівні.
Я не думаю, що це зовсім так просто. Надані відповіді вже дають чудове обґрунтування щодо недостатньої уваги до стандартизації, але C ++ може бути занадто багатим мовою, щоб бути добре підходящим для того, щоб по-справжньому конкурувати з C як стандартом ABI.
Ми можемо перейти до керування іменами, що виникає внаслідок перевантаження функцій, несумісних змінних змін, несумісностей із винятками, які переходять через межі модулів і т. Д. Все це є справжнім болем, і я б хотів, щоб вони могли принаймні стандартизувати вінтажні макети.
Але стандарт ABI стосується не лише створення C ++ дилібів, вироблених в одному компіляторі, який може використовуватися іншим бінарним файлом, побудованим іншим компілятором. ABI використовується крос-мовами . Було б добре, якби вони могли принаймні покрити першу частину, але немає ніякого способу, як я бачу, як C ++ колись справді конкурував із C на роді універсального рівня ABI, настільки важливого для створення найбільш широко сумісних дилібів.
Уявіть просту пару функцій, експортованих так:
void f(Foo foo);
void f(Bar bar, int val);
... і уявіть собі, Foo
чи Bar
були класи з параметризованими конструкторами, конструкторами копій, конструкторами переміщення та нетривіальними деструкторами.
Потім візьміть сценарій Python / Lua / C # / Java / Haskell / тощо. розробник намагається імпортувати цей модуль і використовувати його мовою.
Спочатку нам знадобиться стандарт керування іменами, як експортувати символи, використовуючи перевантаження функцій. Це легша частина. І все-таки це насправді не повинно називатись "mangling". Оскільки користувачі dylib повинні шукати символи за назвою, перевантаження тут повинні призводити до імен, які не схожі на повний безлад. Можливо, назви символів можуть бути подібними "f_Foo"
"f_Bar_int"
або щось подібне. Ми повинні бути впевнені, що вони не можуть зіткнутися з іменем, яке фактично визначено розробником, можливо, резервуючи деякі символи / символи / умови для використання ABI.
Але тепер більш жорсткий сценарій. Як, наприклад, розробник Python викликає конструктори переміщення, конструктори копій та деструктори? Можливо, ми могли б експортувати їх як частину дилібу. Але що , якщо Foo
і Bar
експортуються в різних модулях? Чи слід дублювати символи та реалізації, пов’язані з цим дилібом, чи ні? Я б запропонував нам це зробити, оскільки це може стати дуже прикро дуже швидко, інакше для початку потрібно заплутуватися в декількох інтерфейсах дилібів, щоб створити тут об'єкт, передати його сюди, скопіювати там, знищити його тут. Незважаючи на те, що те саме базове занепокоєння може дещо застосувати в C (лише вручну / явно), C прагне уникати цього лише за характером того, як люди програмують його.
Це лише невеликий зразок незграбності. Що відбувається, коли одна з f
функцій, що перераховані вище, кидає BazException
(JavaScript) класу C ++ з конструкторами та деструкторами та походить std :: виняток у JavaScript?
У кращому випадку я думаю, що ми можемо лише сподіватися стандартизувати ABI, який працює з одного двійкового, що виробляється одним компілятором C ++, до іншого бінарного файлу, виробленого іншим. Це було б чудово, звичайно, але я просто хотів це зазначити. Як правило, супроводжує таке питання щодо розповсюдження узагальненої бібліотеки, яка працює між компіляторами, також часто є бажання зробити її справді узагальненою та сумісною міжмовними мовами.
Пропоноване рішення
Моє запропоноване рішення після спроб знайти шляхи використання інтерфейсів C ++ для API / ABI протягом багатьох років з інтерфейсами стилю COM - це просто стати розробником "C / C ++" (каламбур).
Використовуйте C для створення цих універсальних ІСВ, з C ++ для реалізації. Ми все ще можемо робити такі речі, як функції експорту, які повертають покажчики на непрозорі класи C ++ з явними функціями для створення та знищення таких об’єктів у купі. Спробуйте закохатись у цю естетику C з точки зору ABI, навіть якщо ми повністю використовуємо C ++ для реалізації. Абстрактні інтерфейси можна моделювати за допомогою таблиць функціональних покажчиків. Досить нудно перетворювати ці речі в API API, але переваги та сумісність розповсюдженого дистрибутива, як правило, роблять це дуже вартим.
Тоді, якщо нам не подобається використовувати цей інтерфейс так сильно безпосередньо (ми, мабуть, не повинні принаймні з причин RAII), ми можемо зафіксувати все, що нам потрібно, у статично пов'язану бібліотеку C ++, яку ми постачаємо разом із SDK. Клієнти C ++ можуть використовувати це.
Клієнти Python не захочуть використовувати або інтерфейс C, або C ++ безпосередньо, оскільки немає способів зробити ці пітоніки. Вони захочуть зафіксувати це у власних інтерфейсах pythonique, тому насправді добре, що ми просто експортуємо мінімум API API / ABI, щоб зробити це якомога простіше.
Я думаю, що багато індустрії С ++ виграють від цього більше, ніж намагаються вперто передавати інтерфейси в стилі COM тощо. Це також полегшило б усе наше життя, оскільки користувачі цих дилібів не повинні метушитися з незграбними АБІ. C робить його простим, а простота його з точки зору ABI дозволяє нам створювати API / ABI, які працюють природно і з мінімалізмом для всіх видів ПІІ.