Почему C предоставляет языковые «привязки», где C ++ терпит неудачу?

60

Недавно я задавался вопросом, когда использовать C над C ++, и наоборот? К счастью, кто-то уже опередил меня, и хотя мне потребовалось некоторое время, я смог переварить все ответы и комментарии на этот вопрос.

Однако, один пункт в этом посте продолжает рассматриваться снова и снова, без какого-либо примера, проверки или объяснения:

«C-код подходит для случаев, когда вы хотите иметь несколько языковых привязок для вашей библиотеки»

Это парафраз. Я должен отметить, что некоторые люди отмечают, что в C ++ возможны множественные языковые привязки (через некоторое externфункционирование), но, тем не менее, если вы прочитаете этот пост целиком, вполне очевидно, что C идеален для переносимости / связывания языка. Мой вопрос: почему?

Может ли кто-нибудь предоставить конкретные причины, почему написание библиотек на C позволяет упростить привязки и / или переносимость на другие языки?

smeeb
источник
6
В основном по историческим и социальным причинам. Большинство языковых реализаций и систем времени исполнения построены выше C. Например, среды выполнения Ocaml, SBCL, Haskell написаны на C (и не сильно выиграют, если перекодировать на C ++)
Basile Starynkevitch
8
На практике склеивание подлинных библиотек C ++ (представьте себе Qt) в эти среды выполнения является болезненным.
Василий Старынкевич
3
@Basile: Qt не является подлинной библиотекой C ++. Кроме того, даже для более болезненных библиотек это только болезненно, потому что они выражают действительно полезную семантику.
DeadMG
2
Полу-связанные: programmers.stackexchange.com/a/185159/20756
Blrfl
2
Прочитайте Essential COM от Don Box. Это объясняет процесс создания ABI в C ++. COM был способом Microsoft создать ABI. то есть библиотеки COM C ++ могут быть использованы из C или VB или других языков.
user93353

Ответы:

69

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

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

Отсутствие стандартного ABI (бинарный интерфейс приложения) имеет другое следствие - это делает доставку интерфейсов C ++ другим группам / клиентам нецелесообразным, так как пользовательский код не будет работать, если он не скомпилирован с теми же инструментами и опциями сборки. Мы уже видели другой источник этой проблемы - нестабильность бинарных интерфейсов из-за отсутствия инкапсуляции времени компиляции.

- Дефектный С ++

Мейсон Уилер
источник
4
Вы должны заметить, что, хотя можно определить C-подобный API, реализованный в C ++ ( extern "C"это еще более дополнительная работа, которая должна быть сбалансирована с возможностями, предоставляемыми C ++)
jhominal
5
ABI C ++ не имеет значения в контексте вопроса, где библиотеки C ++ могут быть легко экспортированы extern "C".
DeadMG
12
@jhominal: Это намного лучше, чем писать это на C, где вам нужно определить интерфейс C, а затем вам также нужно реализовать его на C, тогда как в C ++ вы можете определить интерфейс C, а затем вам не нужно реализовывать это на C. Независимо от того, на каком языке вы реализуете, вы все равно должны определить интерфейс C - это, конечно, верно в C ++, как и в C или любом другом языке, который может отображать привязки C.
DeadMG
9
Можно ли добавить отказ от ответственности в ссылку на эту чушь FQA?
Мартин Ба
2
@DocBrown: Конечно, мне кажется, что вопрос касается привязок к языку C или к языку C ++.
Мейсон Уилер
32

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

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

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

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

  • Привязки C ++ могут быть такими же совместимыми, как C, если вы готовы работать над этим. Как указывает @DeadMG, C ++ поддерживает extern "C", так что вы можете экспортировать привязки языка C-стиля (со всей простотой и совместимостью привязок C) из библиотеки C ++ (с ограничением, что вы не можете предоставлять какую-либо специфическую для C ++ функциональность) ,
  • Другим распространенным возражением против привязок языка C ++ является отсутствие стабильности ABI для C ++, но это также завышено; ABI C ++ менее стандартизированы, чем ABI C, но стандарты и стандарты де-факто существуют (Itanium C ++ ABI, который также используется в OS X ; стандарт GCC де-факто для Linux). Windows хуже, но даже в Windows, пребывание в одной версии Visual C ++ должно работать нормально.
Джош Келли
источник
1
Другая проблема, связанная с предоставлением привязки из библиотеки C ++ к другому языку, заключается в том, что для другого языка может потребоваться реализация привязок в C. Или для языков, имеющих что-то вроде (.NET) P / Invoke или (python) ctypes, он может не предоставлять никаких инструментов для использования C ++ ABI.
Random832
6
@ Random832: Что совершенно не имеет значения, когда сторона C ++ может просто предложить интерфейс C. Вам не нужно реализовывать привязку в C, чтобы предлагать привязку C.
DeadMG
21

Си является одним из старейших языков, которые все еще существуют. Его ABI прост, и практически каждая операционная система, все еще используемая сегодня, была написана на нем . В то время как некоторые из этих ОС могут добавлять что-то, например, в C # / .NET или что-то еще сверху, внизу они очень сильно погружены в C.

Это означает, что для того, чтобы использовать функциональные возможности, предоставляемые ОС, практически каждому языку программирования в любом случае нужен был способ взаимодействия с библиотеками Си . Perl, Java, C ++, все они изначально предоставляют способы «говорить на C», потому что они должны были это делать, если не хотели заново изобретать каждое существующее колесо.

Это делает C латиницей языков программирования. (Сколько лет интернета до этой метафоры должно быть "английским языком программирования"?)


Когда вы пишете свою библиотеку на C, вы получаете C-совместимый интерфейс бесплатно (очевидно). Если вы пишете свою библиотеку на C ++, вы можете получить привязки к C через extern "C"объявления, как вы упомянули.

Тем не менее , вы можете получить эти привязки только для функциональности , которые могут быть выражены в C .

Таким образом, ваш библиотечный API не может использовать ...

  • шаблоны,
  • классы,
  • исключения,
  • любые функции, принимающие или возвращающие объекты.

В одном простом примере вам нужно будет заставить ваши экспортированные функции принимать и возвращать массивы ( []) вместо std::vector(или std::stringв этом отношении).

Таким образом, вы не только не сможете предоставить что-то хорошее, что C ++ может предложить клиентам вашей библиотеки, вам также придется приложить дополнительные усилия для «перевода» библиотечного API из C ++ в «C-совместимый» ( extern "C").

Вот почему можно сказать, что C - лучший выбор для реализации библиотеки. Лично я думаю, что преимущества C ++ все еще перевешивают необходимые усилия для extern "C"API, но это только я.

DevSolar
источник
Похоже, что Windows пытается основываться на .NET, Android основан на Java (также C - деталь реализации некоторых API), а iOS / OSX - на Objective-C.
user253751
1
Английский уже является доминирующим языком в мире программирования. Более доминирующим, чем в других профессиях.
Сиюань Рен
3
@immibis: Windows, Linux / Android и BSD / OSX - это все ядра, написанные на C, с интерфейсами, написанными на (и для) C. Независимо от того, что встроено, Java нуждается в JNI, Perl нужны вызовы C,. NET нуждается в C-вызовах, Python - в C-вызовах, Objective-C - в C-вызовах. Ни одному из них не нужны вызовы C ++, что я и пытался сделать.
DevSolar
@DevSolar Многое из Android написано изначально на Java и не использует JNI (вы можете использовать его «назад» для вызова кода Java из C, но это еще раз подтверждает, что это изначально Java). Нет опыта работы с iOS / OSX, но я слышал, что то же самое с Objective-C.
user253751
3
@immibis: Но вы ведь знаете разницу между ядром ОС и ее пользовательским пространством? Я серьезно сомневаюсь, что в основном пользовательское пространство Java делает Android не менее ядром Linux с системными вызовами в стиле C, чем ядром Linux, запущенным на моем рабочем столе. Я также сомневаюсь, что они используют Java в ядре или в промежуточном программном обеспечении. На самом деле, я знаю, что они используют C там. Это проблема курицы и яйца, наоборот. Это было сделано в C раньше, и , таким образом , это гораздо проще , по- прежнему делать это в С.
DevSolar
6

Оставляя детали, другие ответы уже дают:

Причина, по которой многие языки обеспечивают привязку C, заключается в том, что все операционные системы * nix и Windows предоставляют большую часть своего API-интерфейса ОС через интерфейс C. Таким образом, языковая реализация уже должна взаимодействовать с C, чтобы иметь возможность работать на основных Oses. Следовательно, также просто предложить непосредственную связь с любым C-интерфейсом из самого языка.

Мартин Ба
источник
5

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

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

DeadMG
источник
4
Я думаю, что вы правы, и даунотеры неправильно поняли вопрос, но на самом деле ваш ответ не позволяет выделить это потенциальное недоразумение: вопрос не о «библиотеке с интерфейсом C», а о «библиотеке с интерфейсом C ++», речь идет о «библиотека, написанная полностью на C» против «библиотеки с интерфейсом C, написанной на C ++».
Док Браун
@ Снеговик: Наличие проблемных привязок C ++ не имеет абсолютно никакого отношения к проблеме разоблачения привязок C.
DeadMG
2
Итак, вы собираетесь выбрать язык, который имеет полезную семантику и функции, а затем заставить себя переводить их на интерфейс C? Хотя классов и шаблонов можно избежать (но в первую очередь ставить под сомнение, почему вы используете C ++), каждая функция с привязкой C должна перехватывать исключения, а не позволять им попадать в код C. Не говоря уже о том, что любому, кто использует вашу C-совместимую библиотеку, теперь приходится иметь дело с C ++ и его конфликтами ABI и несоответствиями библиотек, в дополнение к конфликтам ABI субстрата C и несоответствиям библиотек.
Просфилаес
2
@prosfilaes: тривиально преобразовать исключения в коды возврата. Вам не нужно избегать использования классов, так как их можно легко превратить в C API, и вам не нужно избегать использования шаблонов в своей реализации. И вашим пользователям не нужно беспокоиться о конфликтах C ++ ABI, если они не строятся из исходного кода, в этом случае вам придется иметь дело с исходным языком, независимо от того, что это такое.
DeadMG
4
Я высказался против не потому, что не обязательно писать библиотеку с C API на C ++, а потому что есть веские причины использовать C помимо того, что он динозавр. Если вы пишете для API на языке X, будь то C, FORTRAN, COBOL, BLISS или Java, всегда будет время, когда будет проще, проще и правильнее писать на этом языке, чем писать любителем. Веселее языка и интерфейса два.
просфилаес
2

При взаимодействии с другим языком существует две основные оси:

  • концепции, которые может перенести интерфейс: просто значения? Рекомендации? дженерики?
  • как интерфейс реализован в «бинарных файлах» (называемых ABI)

C имеет преимущество перед C ++ по этим двум направлениям:

  • C имеет только в основном простые понятия, которые появляются в большинстве других языков 1
  • ABI C двоичных файлов определяется ОС 2

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

Напротив, C ++ имеет сложные концепции, и ABI определяется каждым компилятором (хотя многие придерживаются Itanimum ABI, за исключением Windows ...). На самом деле было предложение Херба Саттера, чтобы ОС исправляли ABI C ++ (для каждой ОС), чтобы частично решить эту проблему. Кроме того, следует отметить, что FFI C ++ возможен, D пытается это сделать 3 .

1 За исключением variadics ( ...), это не просто

2 Есть ли у C стандартный ABI?

3 Взаимодействие D с устаревшим кодом C ++

Матье М.
источник
0

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

C ++ может иметь стандартный ABI, но Страуструп сказал, что не видит необходимости в этом. Он также говорит, что было бы трудно получить консенсус от авторов компиляторов (хотя я сомневаюсь, что - стандартный комитет C ++ выпустит ABI, аналогичный существующим, а разработчики компиляторов просто изменят следующую версию своих компиляторов, которые иногда несовместимы с двоичными файлами В любом случае, я собрал пару библиотек с новым компилятором Sun и обнаружил, что они не работают со старыми).

Вы заметите, что некоторые компании перешли на использование стандартного ABI, Microsoft начала этот процесс с COM еще в 90-х годах, и сегодня они усовершенствовали его в WinRT ABI (не путать с другим WinRT, который относится к к типу табличной ОС), который позволяет программам, написанным на C #, взаимодействовать с библиотеками, написанными на C или C ++ (т. е. собственный уровень ОС Microsoft написан на C ++, предоставляется с помощью WinRT и используется приложениями C # при вызове любой подпрограммы выполнения ОС)

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

Таким образом, ответ на самом деле C не обеспечивает языковые привязки. Бывает, что никто их не слушал и не потребляет.

gbjbaanb
источник
-2

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

Все материалы ABI - это не более чем попытка стандартизировать их.

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

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

Примечание: оригинальный компилятор C ++ (cfront) на самом деле создал исходный код C, который должен был быть скомпилирован, точно так же, как gcc, который создает Assembly "под капотом".

ZioByte
источник