Есть много проблем с переносимостью в C ++, что связано только с отсутствием стандартизации на двоичном уровне.
Я не думаю, что это так просто. Приведенные ответы уже дают превосходное обоснование отсутствия акцента на стандартизации, но C ++ может быть слишком богат языком, чтобы быть подходящим для реальной конкуренции с C как стандартом ABI.
Мы можем перейти к искажению имен, вызванному перегрузкой функций, несовместимостью vtable, несовместимостью с исключениями, выходящими за границы модуля, и т. Д. Все это - настоящая боль, и мне бы хотелось, чтобы они хотя бы стандартизировали макеты vtable.
Но стандарт ABI - это не только создание dlib-файлов C ++, созданных в одном компиляторе, которые могут использоваться другим двоичным файлом, созданным другим компилятором. ABI используется кросс-языки . Было бы неплохо, если бы они могли хотя бы охватить первую часть, но я не вижу, чтобы C ++ когда-либо действительно конкурировал с C на уровне универсального ABI, столь важном для создания наиболее широко совместимых dylib.
Представьте себе простую пару функций, экспортируемых так:
void f(Foo foo);
void f(Bar bar, int val);
... и представьте себе , Foo
и Bar
были классами с параметризованными конструкторами, конструкторы копирования, перемещение конструкторов и нетривиальные деструкторами.
Затем возьмите сценарий Python / Lua / C # / Java / Haskell / и т.д. Разработчик пытается импортировать этот модуль и использовать его на своем языке.
Для начала нам понадобится стандарт искажения имен для экспорта символов с использованием перегрузки функций. Это более простая часть. Тем не менее, это не должно быть название «калеча». Поскольку пользователи dylib должны искать символы по имени, перегрузки здесь должны приводить к именам, которые не выглядят как полный беспорядок. Возможно, имена символов могут быть похожими "f_Foo"
"f_Bar_int"
или что-то в этом роде. Мы должны быть уверены, что они не могут конфликтовать с именем, фактически определенным разработчиком, возможно, резервируя некоторые символы / символы / соглашения для использования ABI.
Но сейчас более сложный сценарий. Как разработчик Python, например, вызывает конструкторы перемещения, конструкторы копирования и деструкторы? Может быть, мы могли бы экспортировать их как часть Dylib. Но что, если Foo
и Bar
экспортируются в разных модулях? Должны ли мы дублировать символы и реализации, связанные с этим дилибом или нет? Я бы посоветовал нам это сделать, так как это может очень быстро раздражать, иначе начинать запутываться в нескольких интерфейсах dylib просто для того, чтобы создать объект здесь, передать его сюда, скопировать туда, уничтожить здесь. В то время как та же самая основная проблема могла бы в некоторой степени применяться в C (просто в большей степени вручную / явно), C стремится избежать этого просто по природе того, как люди программируют с ней.
Это всего лишь маленький образец неловкости. Что происходит, когда одна из f
функций, приведенных выше, выбрасывает BazException
(также класс C ++ с конструкторами и деструкторами и выводом std :: exception) в JavaScript?
В лучшем случае я думаю, что мы можем только надеяться стандартизировать ABI, который работает от одного двоичного файла, созданного одним компилятором C ++, до другого двоичного файла, созданного другим. Это было бы здорово, конечно, но я просто хотел указать на это. Обычно при распространении обобщенной библиотеки, работающей с кросс-компиляторами, часто возникает желание сделать ее действительно обобщенной и совместимой с кросс-языками.
Предлагаемое решение
Мое предлагаемое решение после того, как я в течение многих лет пытался найти способы использования интерфейсов C ++ для API / ABI с интерфейсами в стиле COM, - просто стать разработчиком "C / C ++" (каламбур).
Используйте C для создания этих универсальных ABI, а C ++ для реализации. Мы все еще можем делать такие вещи, как функции экспорта, которые возвращают указатели на непрозрачные классы C ++ с явными функциями для создания и уничтожения таких объектов в куче. Попробуйте влюбиться в эту эстетику C с точки зрения ABI, даже если мы полностью используем C ++ для реализации. Абстрактные интерфейсы могут быть смоделированы с использованием таблиц указателей функций. Утомительно включать это в C API, но преимущества и совместимость поставляемого дистрибутива, как правило, делают его очень полезным.
Тогда, если нам не нравится использовать этот интерфейс напрямую (нам, вероятно, не следует по крайней мере по причинам RAII), мы можем обернуть все, что мы хотим, в статически связанную библиотеку C ++, поставляемую с SDK. Клиенты C ++ могут использовать это.
Клиенты Python не захотят использовать интерфейс C или C ++ напрямую, так как нет способов сделать это Pythonique. Они захотят обернуть его в свои собственные интерфейсы pythonique, так что на самом деле хорошо, что мы просто экспортируем минимум API C / ABI, чтобы сделать это как можно проще.
Я думаю, что многие отрасли C ++ выиграют от делать это более, чем пытаться упорно интерфейсов корабля COM-стиля и так далее. Это также облегчит нашу жизнь, поскольку пользователям этих dlib не придется суетиться с неуклюжими ABI. C делает это простым, а простота этого с точки зрения ABI позволяет нам создавать API / ABI, которые работают естественно и с минимализмом для всех видов FFI.