Warum bietet C Sprachbindungen an, bei denen C ++ zu kurz kommt?

60

Ich habe mich kürzlich gefragt, wann ich C über C ++ verwenden soll und umgekehrt? Zum Glück hat mich schon jemand geschlagen und obwohl es eine Weile gedauert hat, konnte ich alle Antworten und Kommentare zu dieser Frage verdauen.

Ein Punkt in diesem Beitrag wird jedoch immer wieder angesprochen, ohne Beispiel, Bestätigung oder Erklärung:

"C-Code ist gut, wenn Sie mehrere Sprachbindungen für Ihre Bibliothek haben möchten."

Das ist eine Umschreibung. Ich sollte beachten, dass mehrere Leute darauf hinweisen, dass mehrere Sprachbindungen in C ++ möglich sind (über einige externFunktionen), aber dennoch, wenn Sie diesen Beitrag in seiner Gesamtheit lesen, ist es ziemlich offensichtlich, dass C ideal für Portabilität / Sprachbindung ist. Meine Frage ist: warum?

Kann jemand bitte konkrete Gründe angeben, warum das Schreiben von Bibliotheken in C eine einfachere Bindung und / oder Portabilität in anderen Sprachen ermöglicht?

smeeb
quelle
6
Meist aus historischen und sozialen Gründen. Die meisten Sprachimplementierungen und Laufzeitsysteme sind höher als C. Zum Beispiel sind die Ocaml-, SBCL- und Haskell-Laufzeiten in C codiert (und werden durch die Neucodierung in C ++ nicht wesentlich
verbessert
8
In der Praxis ist es schmerzhaft, echte C ++ - Bibliotheken (denken Sie an Qt) in diese Laufzeiten zu kleben.
Basile Starynkevitch
3
@Basile: Qt ist keine echte C ++ - Bibliothek. Auch für schmerzhaftere Bibliotheken ist es nur schmerzhaft, weil sie tatsächlich nützliche Semantik ausdrücken.
DeadMG
2
Lesen Sie Essential COM von Don Box. Es wird der Prozess zum Erstellen eines ABI in C ++ erläutert. Mit COM hat Microsoft ein ABI erstellt. dh COM C ++ - Bibliotheken können aus C oder VB oder anderen Sprachen verwendet werden.
User93353

Antworten:

69

C verfügt über eine sehr viel einfachere Schnittstelle, und die Regeln für die Konvertierung einer Quellcode-Schnittstelle in eine Binärschnittstelle sind so einfach, dass die Generierung externer Schnittstellen, an die eine Bindung hergestellt werden soll, gut etabliert ist. C ++ hat andererseits eine unglaublich komplizierte Oberfläche, und die Regeln für die ABI-Bindung sind weder formal noch in der Praxis standardisiert. Das bedeutet, dass so gut wie jeder Compiler für jede Sprache für jede Plattform an eine externe C-Schnittstelle gebunden werden kann und genau weiß, was zu erwarten ist. Bei einer C ++ - Schnittstelle ist dies jedoch im Wesentlichen unmöglich, da sich die Regeln in Abhängigkeit davon ändern, welcher Compiler, welche Version und welche Plattform, mit der der C ++ - Code erstellt wurde.

In C gibt es auch keine Standardregeln für die Implementierung von Binärsprachen, aber es ist eine Größenordnung einfacher und in der Praxis verwenden Compiler dieselben Regeln. Ein weiterer Grund, der das Debuggen von C ++ - Code erschwert, ist die oben erwähnte komplizierte Grammatik, da Debugger häufig nicht mit vielen Sprachfunktionen umgehen können (Platzieren von Haltepunkten in Vorlagen, Parsen von Zeiger-Casting-Befehlen in Datenanzeigefenstern usw.).

Das Fehlen einer Standard-ABI (Application Binary Interface) hat eine weitere Konsequenz: Der Versand von C ++ - Schnittstellen an andere Teams / Kunden ist unpraktisch, da der Benutzercode nur funktioniert, wenn er mit denselben Tools und Build-Optionen kompiliert wurde. Wir haben bereits eine andere Ursache für dieses Problem gesehen - die Instabilität von Binärschnittstellen aufgrund der fehlenden Kapselung der Kompilierungszeit.

- Defektes C ++

Mason Wheeler
quelle
4
Sie sollten beachten, dass es zwar möglich ist, eine C-ähnliche API zu definieren, die in C ++ implementiert ist ( extern "C"dies ist jedoch noch ein weiterer Arbeitsschritt, der gegen die von C ++ bereitgestellten Funktionen abgewogen werden sollte)
jhominal
5
Das C ++ ABI ist im Kontext der Frage, mit welchen C ++ - Bibliotheken leicht exportiert werden kann, irrelevant extern "C".
DeadMG
12
@jhominal: Es ist viel besser, als es in C zu schreiben, wo Sie eine C-Schnittstelle definieren und dann auch in C implementieren müssen, während Sie in C ++ eine C-Schnittstelle definieren können und dann nicht implementieren müssen Unabhängig davon, in welcher Sprache Sie implementieren, müssen Sie immer noch eine C-Schnittstelle definieren. Dies gilt natürlich für C ++ ebenso wie für C oder jede andere Sprache, die C-Bindungen verfügbar machen kann.
DeadMG
9
Wäre es möglich, dem Link zu diesem FQA-Fehler einen Haftungsausschluss hinzuzufügen?
Martin Ba
2
@ DocBrown: Sicher, für mich ist die Frage nach C-Sprachbindungen im Vergleich zu C ++ - Sprachbindungen.
Mason Wheeler
32

Wenn Sie versuchen, mit einem Sprecher einer anderen Sprache zu kommunizieren, ist Pidgin einfacher als Shakespeare-Englisch.

Die Konzepte von C - Funktionsaufrufe, Zeiger und mit NULL abgeschlossene Zeichenfolgen - sind sehr einfach, sodass andere Sprachen sie problemlos gut genug implementieren können, um C-Funktionen aufzurufen. Aus historischen Gründen sind viele andere Sprachen in C implementiert, was den Aufruf von C-Funktionen noch einfacher macht.

C ++ fügt einiges hinzu - Klassen mit Vererbung und vtables und Zugriffsmodifikatoren; Ausnahmen mit Stapelabwicklung und Änderung des Kontrollflusses; Vorlagen. All dies erschwert es anderen Sprachen, C ++ - Bindungen zu verwenden: Im besten Fall muss mehr "Klebe" - oder Interoperabilitätscode implementiert werden, und im schlimmsten Fall werden die Konzepte nicht direkt übersetzt (aufgrund von Unterschieden in Klassenmodellen, Ausnahmebehandlung, usw.). Insbesondere für Vorlagen, die einfach verwendet (instanziiert) werden, ist normalerweise ein Kompilierungsschritt mit einem C ++ - Compiler erforderlich, was die Verwendung in anderen Umgebungen erheblich erschwert.

Trotzdem ist es möglich, die Schwierigkeit, Bindungen aus einer C ++ - Bibliothek für eine andere Sprache bereitzustellen, zu überschätzen:

  • C ++ - Bindungen können genauso kompatibel sein wie C, wenn Sie bereit sind, daran zu arbeiten. Wie @DeadMG hervorhebt, unterstützt C ++ extern "C", sodass Sie Sprachbindungen im C-Stil (mit der Einfachheit und Kompatibilität von C-Bindungen) aus einer C ++ - Bibliothek exportieren können (mit der Einschränkung, dass Sie keine C ++ - spezifischen Funktionen verfügbar machen können). .
  • Ein weiterer häufiger Einwand gegen C ++ - Sprachbindungen ist die mangelnde ABI-Stabilität für C ++, die jedoch ebenfalls überbewertet wird. C ++ ABIs sind weniger standardisiert als C ABIs, es gibt jedoch Standards und De-facto-Standards (Itanium C ++ ABI, das auch unter OS X verwendet wird ; der De-facto-Standard von GCC für Linux). Windows ist schlechter, aber selbst unter Windows sollte es problemlos funktionieren , in einer Visual C ++ - Version zu bleiben .
Josh Kelley
quelle
1
Das andere Problem beim Bereitstellen einer Bindung von einer C ++ - Bibliothek an eine andere Sprache besteht darin, dass für die andere Sprache möglicherweise Bindungen in C implementiert werden müssen bietet möglicherweise keine Tools für die Verwendung von C ++ ABI an.
Random832
6
@ Random832: Das ist völlig irrelevant, wenn die C ++ - Seite einfach die C-Schnittstelle anbieten kann. Sie müssen die Bindung nicht in C implementieren, um eine C-Bindung anzubieten.
DeadMG
21

C ist eine der ältesten Sprachen, die es noch gibt. Das ABI ist einfach, und praktisch jedes Betriebssystem, das heute noch verwendet wird, wurde darauf geschrieben . Während einige dieser Betriebssysteme Dinge hinzugefügt haben, z. B. in C # /. NET oder was auch immer oben drauf ist, sind sie unten in C. sehr durchdrungen.

Das bedeutet, dass praktisch jede Programmiersprache eine Schnittstelle zu C-Bibliotheken benötigt , um die vom Betriebssystem bereitgestellten Funktionen nutzen zu können . Perl, Java, C ++, sie alle nativ Möglichkeiten bieten , um „C zu sprechen“, weil sie mussten , wenn sie jedes einzelne Rad neu erfinden , gibt es nicht wollte.

Dies macht C zum Latein der Programmiersprachen. (Wie viele Jahre Internet vor dieser Metapher muss "das Englisch der Programmiersprachen" sein?)


Wenn Sie Ihre Bibliothek in C schreiben, erhalten Sie (offensichtlich) kostenlos eine C-kompatible Schnittstelle. Wenn Sie Ihre Bibliothek in C ++ schreiben, können Sie C-Bindungen über die extern "C"von Ihnen erwähnten Deklarationen abrufen.

Allerdings können Sie diese Bindungen erhalten nur für die Funktionalität , die in C ausgedrückt werden kann .

Ihre Bibliotheks-API kann also nicht ...

  • Vorlagen,
  • Klassen,
  • ausnahmen,
  • Alle Funktionen , die Objekte entgegennehmen oder zurückgeben .

Ein einfaches Beispiel: Sie müssten dafür sorgen, dass Ihre exportierten Funktionen anstelle von (oder in diesem Fall) arrays ( ) annehmen und zurückgeben .[]std::vectorstd::string

Damit können Sie nicht nur den Kunden Ihrer Bibliothek nicht die Vorteile von C ++ bieten, sondern müssen auch zusätzliche Anstrengungen unternehmen, um Ihre Bibliotheks-API von C ++ in "C-kompatibel" ( extern "C") zu "übersetzen" .

Aus diesem Grund könnte darauf hingewiesen werden, dass C die bessere Wahl für die Implementierung einer Bibliothek ist. Ich persönlich denke, die Vorteile von C ++ überwiegen immer noch den für eine extern "C"API erforderlichen Aufwand , aber das bin nur ich.

DevSolar
quelle
Windows scheint zu versuchen, sich auf .NET zu stützen, Android basiert auf Java (auch C als Implementierungsdetail für einige APIs) und iOS / OSX basiert auf Objective-C.
user253751
1
Englisch ist bereits die dominierende Sprache in der Programmierwelt. Dominanter als in anderen Berufen.
Siyuan Ren
3
@immibis: Windows, Linux / Android und BSD / OSX sind allesamt in C geschriebene Kernel mit in (und für) C geschriebenen Schnittstellen. NET benötigt C-Aufrufe, Python benötigt C-Aufrufe, Objective-C benötigt C-Aufrufe. Keiner von diesen benötigt C ++ - Aufrufe, was der Punkt ist, den ich versuchte zu machen.
DevSolar
@DevSolar Viele Android-Produkte sind ursprünglich in Java geschrieben und verwenden kein JNI (Sie können es "rückwärts" verwenden, um Java-Code von C aus aufzurufen, aber das bestätigt nur, dass es ursprünglich Java ist). Keine Erfahrung mit iOS / OSX, aber ich höre, dass diese mit Objective-C identisch sind.
user253751
3
@immibis: Aber Sie tun wissen , den Unterschied zwischen einem OS - Kernel und deren User - Space, nicht wahr? Ich bezweifle ernsthaft, dass ein Großteil der Java-Benutzer Android weniger zu einem Linux-Kernel mit C-ähnlichen Systemaufrufen macht als der Linux-Kernel, der auf meinem Desktop ausgeführt wird. Ich bezweifle auch, dass sie Java im Kernel oder in der Middleware verwenden. Eigentlich weiß ich, dass sie dort C verwenden. Es ist das Henne-Ei-Problem, genau umgekehrt. Es wurde vor in C durchgeführt, und es ist so viel einfacher zu noch tut es in C.
DevSolar
6

Lassen Sie die Details weg, die andere Antworten bereits liefern:

Der Grund, warum so viele Sprachen eine C-Bindung bereitstellen, ist, dass alle * nix- und Windows-Betriebssysteme den größten Teil ihrer Betriebssystem-API über eine C-Schnittstelle verfügbar machen. Daher muss die Sprachimplementierung bereits eine Schnittstelle zu C aufweisen, um auf den Haupt-Oses ausgeführt werden zu können. Daher ist es unkompliziert, auch die direkte Kommunikation mit einer beliebigen C-Schnittstelle aus der Sprache heraus anzubieten.

Martin Ba
quelle
5

Es gibt keinen Grund. Wenn die Semantik, die Sie ausdrücken möchten, grundsätzlich C-kompatibel ist und nicht so etwas wie Vorlagen, gibt es keinen Grund, warum Sie sie einfacher binden können, wenn die Implementierung in C geschrieben ist. Tatsächlich ist es per Definition so ziemlich so, dass eine C-Schnittstelle sein kann Wird von jeder Implementierung ausgefüllt, die den Binärvertrag erfüllen kann, einschließlich einer Implementierung in einer anderen Sprache. Es gibt andere Sprachen als C ++, die C-Binärverträge implementieren können, die auf diese Weise funktionieren.

Worauf es wirklich ankommt, sind Leute, die keine neuen Sprachen oder Ideen lernen wollen, die tatsächlich nützliche Semantiken oder Funktionen haben, und verzweifelt versuchen, einen Grund zu finden, um in der Dinosaurier-Ära zu bleiben.

DeadMG
quelle
4
Ich denke, Sie haben Recht und die Abwähler haben die Frage falsch verstanden, aber Ihre Antwort verpasst es tatsächlich, dieses potenzielle Missverständnis hervorzuheben: Es geht nicht um "eine Bibliothek mit einer C-Schnittstelle" oder "eine Bibliothek mit einer C ++ - Schnittstelle" msgstr "eine vollständig in C geschriebene Bibliothek vs." eine Bibliothek mit einer in C ++ geschriebenen C - Schnittstelle ".
Doc Brown
@Snowman: Problematische C ++ - Bindungen haben absolut nichts mit dem Problem zu tun, C-Bindungen verfügbar zu machen.
DeadMG
2
Suchen Sie sich also eine Sprache mit nützlicher Semantik und nützlichen Funktionen aus und zwingen Sie sich, diese in eine C-Benutzeroberfläche zu übersetzen? Während Klassen und Vorlagen möglicherweise vermieden werden können (stellen Sie jedoch in Frage, warum Sie C ++ überhaupt verwenden), muss jede Funktion mit C-Bindung Ausnahmen abfangen, anstatt sie in den C-Code fließen zu lassen. Ganz zu schweigen davon, dass jeder, der Ihre C-kompatible Bibliothek verwendet, sich jetzt zusätzlich zu den ABI-Konflikten und Bibliotheksinkongruenzen des Substrats C mit C ++ und seinen ABI-Konflikten und Bibliotheksinkongruenzen auseinandersetzen muss.
Prosfilaes
2
@prosfilaes: Es ist trivial, Ausnahmen in Rückkehrcodes umzuwandeln. Sie müssen nicht auf die Verwendung von Klassen verzichten, da diese problemlos in eine C-API integriert werden können und Sie die Verwendung von Vorlagen in Ihrer Implementierung nicht vermeiden müssen. Und Ihre Benutzer müssen sich nicht mit C ++ ABI-Konflikten auseinandersetzen, es sei denn, sie bauen aus dem Quellcode auf. In diesem Fall müssen Sie sich mit der Quellsprache befassen, egal was das ist.
DeadMG
4
Ich habe nicht abgelehnt, weil man in C ++ nicht unbedingt eine Bibliothek mit einer C-API schreiben sollte, sondern weil es gute Gründe gibt, C zu verwenden, außer ein Dinosaurier zu sein. Wenn Sie für eine API in der Sprache X schreiben, sei es C, FORTRAN, COBOL, BLISS oder Java, wird es immer einfacher, einfacher und korrekter sein, in dieser Sprache zu schreiben, als in einem Fancier , mehr Spaß Sprache und Schnittstelle der beiden.
Prosfilaes
2

Bei der Verbindung mit einer anderen Sprache gibt es zwei Hauptachsen:

  • Die Konzepte, die die Schnittstelle übertragen kann: Nur Werte? Verweise? Generika?
  • wie die Schnittstelle in "Binaries" implementiert ist (ABI genannt)

C hat in diesen beiden Punkten einen Vorteil gegenüber C ++:

  • C hat nur meist einfache Konzepte, die in fast jeder anderen Sprache vorkommen 1
  • Die ABI von C-Binärdateien wird von OS 2 festgelegt

Nun, warum haben die meisten Sprachen einen ähnlichen Satz von Konzepten als C, kann daran liegen, dass es entweder "einfach" oder "bereits vorhanden" ist; es spielt jedoch keine Rolle, der Punkt ist, dass sie es tun.

Im Gegenteil, C ++ hat komplexe Konzepte, und das ABI wird von jedem Compiler festgelegt (obwohl viele das Itanimum ABI einhalten, außer unter Windows ...). Es gab tatsächlich einen Vorschlag von Herb Sutter, dass Betriebssysteme ein C ++ - ABI (pro Betriebssystem) reparieren, um dieses Problem teilweise zu beheben. Man sollte auch beachten, dass ein C ++ FFI möglich ist, D versucht es 3 .

1 Mit Ausnahme von variadics ( ...) sind diese nicht einfach

2 Hat C einen Standard-ABI?

3 Schnittstelle von D zu Legacy-C ++ - Code

Matthieu M.
quelle
0

Grundsätzlich kommt es auf die ABI-Standardisierung an. Während weder C noch C ++ ein standardisiertes ABI haben, mit dem andere Sprachen die Schnittstelle zwischen geschriebenen Binärdateien herstellen können, ist C zu einem de-facto-Standard geworden, jeder kennt es und jeder andere kann dieselben einfachen Regeln verwenden, die die Sprache in Bezug auf hat Typen und Funktionsaufrufe.

C ++ könnte ein Standard-ABI haben, aber Stroustrup hat gesagt, dass er die Notwendigkeit eines solchen nicht sieht. Er sagt auch, dass es schwierig sein würde, einen Konsens der Compiler-Autoren zu erzielen (obwohl ich bezweifle, dass - das C ++ - Standardkomitee eine ABI herausgeben würde, die den bestehenden ähnelt, und Compiler-Autoren einfach die nächste Version ihrer Compiler ändern würden, die gelegentlich nicht mit Binaries kompatibel sind Ich erinnere mich, dass ich einige Bibliotheken mit einem neuen Sun-Compiler neu kompiliert habe und festgestellt habe, dass sie nicht mit den alten funktionieren.

Sie werden bemerken, dass einige Unternehmen bereits in den 90er Jahren ein Standard-ABI verwendet haben. Microsoft hat diesen Prozess mit COM begonnen und heute das WinRT-ABI verfeinert (nicht zu verwechseln mit dem anderen WinRT, auf das verwiesen wird) auf einen Tabellentyp (OS), mit dem in C # geschriebene Programme mit in C oder C ++ geschriebenen Bibliotheken kommunizieren können (dh die Microsoft-eigene OS-Schicht wird in C ++ geschrieben, mit WinRT verfügbar gemacht und von C # -Anwendungen verwendet, wenn sie eine beliebige OS-Laufzeitroutine aufrufen)

Es gibt nicht viel, was jemand tun kann, es sei denn, ein Normungsgremium greift auf diese Situation zu und behebt sie. Microsoft sieht den Wert offensichtlich darin und hat Schritte unternommen, um ihn für ihre Plattform zu beheben.

Die Antwort ist also, dass C keine Sprachbindungen bereitstellt. Es kommt vor, dass niemand zugehört hat und sie trotzdem konsumiert.

gbjbaanb
quelle
-2

Alle Antworten bleiben hinter dem eigentlichen Problem zurück: Durch das Kompilieren von C ++ wird "Name Mangling" eingeführt, sodass Binärdateien nicht mit "einfachen" Funktionsaufrufen kompatibel sind.

Alles ABI-Zeug ist nur ein Versuch, es zu standardisieren.

Im Allgemeinen kann nicht garantiert werden, dass Sie Funktionen, die mit verschiedenen Compilern kompiliert wurden, vernetzen können, auch wenn Sie sich an C ++ halten. Früher war es sicher, dass sie nicht kompatibel sind, aber heute schleicht sich die Standardisierung ein;)

OTOH C wurde genau als "High Level Assembly" konzipiert und ermöglicht alle Arten von einfachen Schnittstellen. Es sollte nicht überraschen, dass es besser für sprachübergreifende Vorlieben geeignet ist.

Randnotiz: Der ursprüngliche C ++ - Compiler (cfront) produzierte tatsächlich eine C-Quelle, die kompiliert werden musste, genau wie gcc, das Assembly "under the hood" produziert.

ZioByte
quelle