Perché C fornisce "associazioni" di linguaggio in cui C ++ non è all'altezza?


60

Di recente mi chiedevo quando usare C su C ++ e viceversa? Fortunatamente qualcuno mi ha già battuto e anche se ci è voluto un po ', sono stato in grado di digerire tutte le risposte e i commenti a quella domanda.

Tuttavia, un elemento in quel post continua a essere affrontato più e più volte, senza alcun tipo di esempio, verifica o spiegazione:

"Il codice C è adatto per quando si desidera avere associazioni in più lingue per la propria libreria"

È una parafrasi. Dovrei notare che diverse persone sottolineano che in C ++ sono possibili collegamenti a più lingue (tramite alcuni externfunzionamenti), ma tuttavia, se leggi quel post nella sua interezza, è abbastanza ovvio che C è l'ideale per la portabilità / il legame linguistico. La mia domanda è: perché?

Qualcuno può fornire motivi concreti per cui la scrittura di librerie in C consente collegamenti e / o portabilità più facili in altre lingue?


6
Principalmente per motivi storici e sociali. La maggior parte delle implementazioni linguistiche e dei sistemi di runtime sono costruiti sopra C. Ad esempio, i tempi di esecuzione di Ocaml, SBCL, Haskell sono codificati in C (e non guadagneranno molto con la ricodifica in C ++)
Basile Starynkevitch

8
In pratica, incollare librerie C ++ autentiche (pensa a Qt) in questi runtime è doloroso.
Basile Starynkevitch

3
@Basile: Qt non è una vera libreria C ++. Inoltre, anche per le biblioteche più dolorose, è doloroso solo perché esprimono una semantica davvero utile.
DeadMG


2
Leggi Essential COM di Don Box. Spiega il processo di creazione di un ABI in C ++. COM era il modo di Microsoft di creare un ABI. cioè le librerie COM C ++ potrebbero essere usate da C o VB o altre lingue.
user93353

Risposte:


69

C ha un'interfaccia molto, molto più semplice, e le sue regole per convertire un'interfaccia di codice sorgente in un'interfaccia binaria sono abbastanza semplici che la generazione di interfacce esterne a cui legarsi è fatta in modo ben definito. Il C ++, d'altra parte, ha un'interfaccia incredibilmente complicata e le regole per l'associazione ABI non sono affatto standardizzate, né formalmente né in pratica. Ciò significa che praticamente qualsiasi compilatore per qualsiasi linguaggio per qualsiasi piattaforma può legarsi a un'interfaccia C esterna e sapere esattamente cosa aspettarsi, ma per un'interfaccia C ++, è essenzialmente impossibile perché le regole cambiano a seconda di quale compilatore, quale versione e quale piattaforma con cui è stato creato il codice C ++.

In C, non ci sono nemmeno regole standard per l'implementazione del linguaggio binario, ma è un ordine di grandezza più semplice e in pratica i compilatori usano le stesse regole. Un altro motivo che rende difficile il debug del codice C ++ è la grammatica complicata sopra menzionata, poiché i debugger spesso non sono in grado di gestire molte funzionalità del linguaggio (posizionare punti di interruzione nei modelli, analizzare i comandi di cast del puntatore nelle finestre di visualizzazione dei dati, ecc.).

La mancanza di un ABI standard (interfaccia binaria dell'applicazione) ha un'altra conseguenza: rende impraticabile la spedizione delle interfacce C ++ ad altri team / clienti poiché il codice utente non funzionerà a meno che non sia compilato con gli stessi strumenti e le stesse opzioni di costruzione. Abbiamo già visto un'altra fonte di questo problema: l'instabilità delle interfacce binarie a causa della mancanza di incapsulamento del tempo di compilazione.

- C ++ difettoso


4
Dovresti notare che, sebbene sia possibile definire un'API di tipo C implementata in C ++ ( extern "C"è ancora più lavoro aggiuntivo farlo, che dovrebbe essere bilanciato con le funzionalità fornite da C ++)
jhominal

5
L'ABI C ++ è irrilevante nel contesto della domanda, in cui è possibile esportare facilmente le librerie C ++ extern "C".
DeadMG

12
@jhominal: è molto meglio che scriverlo in C, dove devi definire un'interfaccia C e poi devi anche implementarla in C, mentre in C ++ puoi definire un'interfaccia C e quindi non devi implementare in C. Indipendentemente dal linguaggio in cui stai implementando, devi comunque definire un'interfaccia C: questo è ovviamente vero in C ++ come in C o in qualsiasi altro linguaggio in grado di esporre i binding C.
DeadMG

9
Sarebbe possibile aggiungere una dichiarazione di non responsabilità al collegamento a quella trasmissione FQA?
Martin Ba,

2
@DocBrown: certo mi sembra che la domanda riguardi i binding del linguaggio C rispetto ai binding del linguaggio C ++.
Mason Wheeler,

32

Se stai cercando di comunicare con un parlante di un'altra lingua, pidgin è più facile dell'inglese shakespeariano.

I concetti di C - chiamate di funzione, puntatori, stringhe con terminazione NULL - sono molto semplici, quindi altri linguaggi possono facilmente implementarli abbastanza bene da chiamare funzioni C. Per ragioni storiche, molte altre lingue sono implementate in C, il che rende ancora più facile chiamare le funzioni C.

Il C ++ aggiunge parecchie cose - classi, con ereditarietà e vtables e modificatori di accesso; eccezioni, con lo svolgimento della pila e la modifica del flusso di controllo; modelli. Tutto ciò rende più difficile per gli altri linguaggi usare i collegamenti C ++: nella migliore delle ipotesi, c'è più "colla" o codice di interoperabilità da implementare e, nel peggiore dei casi, i concetti non si traducono direttamente (a causa delle differenze nei modelli di classe, gestione delle eccezioni, eccetera.). Per i template in particolare, semplicemente usarli (istanziarli) in genere richiede una fase di compilazione con un compilatore C ++, il che complica enormemente il loro utilizzo da altri ambienti.

Detto questo, è possibile sopravvalutare la difficoltà di fornire collegamenti da una libreria C ++ a un'altra lingua:

  • I binding C ++ possono essere compatibili tanto quanto C, se sei disposto a lavorarci su. Come sottolinea @DeadMG, C ++ supporta extern "C", in modo da poter esportare le associazioni di linguaggio in stile C (con tutta la semplicità e la compatibilità delle associazioni di C) da una libreria C ++ (con la limitazione che non è possibile esporre alcuna funzionalità specifica di C ++) .
  • Un'altra obiezione comune alle associazioni del linguaggio C ++ è la mancanza di stabilità ABI per C ++, ma anche questa è sopravvalutata; Gli ABI C ++ sono meno standardizzati degli ABI C, ma esistono standard e standard de facto (l'ABI Itanium C ++, che viene utilizzato anche su OS X ; lo standard de facto di GCC per Linux). Windows è peggio, ma anche su Windows, rimanere all'interno di una versione di Visual C ++ dovrebbe funzionare bene.

1
L'altro problema con la fornitura di un'associazione da una libreria C ++ a un'altra lingua è che l'altra lingua potrebbe richiedere l'implementazione di associazioni in C. Oppure, per linguaggi che hanno qualcosa come (.NET) P / Invoke o (python) ctypes, esso potrebbe non fornire strumenti per l' utilizzo dell'ABI C ++.
Casuale 832,

6
@ Random832: il che è completamente irrilevante quando il lato C ++ può semplicemente offrire l'interfaccia C. Non è necessario implementare l'associazione in C per offrire un'associazione C.
DeadMG

21

C è una delle lingue più antiche ancora in circolazione. La sua ABI è semplice e praticamente ogni sistema operativo ancora in uso oggi è stato scritto al suo interno . Mentre alcuni di questi sistemi operativi potrebbero aver aggiunto elementi, ad esempio in C # /. NET o qualunque cosa in alto, in basso sono molto pieni di C.

Ciò significa che, al fine di utilizzare le funzionalità fornite dal sistema operativo, praticamente ogni linguaggio di programmazione là fuori bisogno di un modo di interfacciarsi con librerie C in ogni caso . Perl, Java, C ++, tutti forniscono nativamente modi per "parlare in C", perché dovevano farlo se non volevano reinventare ogni singola ruota che esiste.

Questo rende C il latino dei linguaggi di programmazione. (Quanti anni di Internet prima di quella metafora deve essere "l'inglese delle lingue di programmazione"?)


Quando scrivi la tua libreria in C, ottieni un'interfaccia compatibile con C gratuitamente (ovviamente). Se stai scrivendo la tua libreria in C ++, puoi ottenere i collegamenti C, attraverso le extern "C"dichiarazioni come hai detto.

Tuttavia , è possibile ottenere questi attacchi solo per la funzionalità che può essere espressa in C .

Quindi l'API della libreria non può utilizzare ...

  • modelli,
  • classi,
  • eccezioni,
  • qualsiasi funzione che prende o restituisce oggetti.

Un semplice esempio, dovresti fare in modo che le tue funzioni esportate prendano e restituiscano array ( []) invece di std::vector(o del std::stringresto).

Quindi, non solo non saresti in grado di fornire nessuna delle cose buone che C ++ ha da offrire ai clienti della tua biblioteca, ma dovresti anche impegnarti ulteriormente per "tradurre" l'API della tua biblioteca da C ++ a "C compatibile" ( extern "C").

Ecco perché si potrebbe sottolineare che C è la scelta migliore per l'implementazione di una libreria. Personalmente, penso che i vantaggi del C ++ siano ancora superiori allo sforzo necessario per extern "C"un'API, ma sono solo io.


Windows sembra tentare di basarsi su .NET, Android si basa su Java (anche C come dettaglio di implementazione per alcune API) e iOS / OSX si basano su Objective-C.
user253751

1
L'inglese è già la lingua dominante nel mondo della programmazione. Più dominante rispetto ad altre professioni.
Siyuan Ren,

3
@immibis: Windows, Linux / Android e BSD / OSX sono tutti kernel scritti in C, con interfacce scritte in (e per) C. Indipendentemente da ciò che è costruito sopra, Java ha bisogno di JNI, Perl ha bisogno di chiamate C,. NET ha bisogno di chiamate C, Python ha bisogno di chiamate C, Objective-C ha bisogno di chiamate C. Nessuno di questi ha bisogno di chiamate C ++, che è il punto che stavo cercando di fare.
DevSolar

@DevSolar Molte cose di Android sono scritte in modo nativo in Java e non usano JNI (puoi usarlo "indietro" per chiamare il codice Java da C, ma questo conferma ulteriormente che è nativamente Java). Nessuna esperienza con iOS / OSX ma ho sentito che sono gli stessi con Objective-C.
user253751

3
@immibis: Ma voi fate conoscere la differenza tra un kernel del sistema operativo e il suo spazio utente, non è vero? Dubito seriamente che uno spazio utente prevalentemente Java renda Android un kernel Linux con chiamate di sistema in stile C rispetto al kernel Linux in esecuzione sul mio desktop. Dubito anche che stiano usando Java nel kernel o nel middleware. In realtà, so che stanno usando C lì. È il problema delle galline e delle uova, proprio il contrario. È già stato fatto in C prima, e quindi è molto più facile farlo ancora in C.
DevSolar

6

Tralasciando i dettagli, altre risposte forniscono già:

Il motivo per cui molte lingue forniscono un'associazione C è che tutti i sistemi operativi * nix e Windows espongono la maggior parte delle loro API del sistema operativo tramite un'interfaccia C. Quindi l'implementazione del linguaggio deve già interfacciarsi con C per poter funzionare sui principali assi. Pertanto, è semplice offrire anche la comunicazione diretta con qualsiasi interfaccia C dal linguaggio stesso.


5

Non c'è ragione. Se la semantica che stai cercando di esprimere sono fondamentalmente compatibili con C e non qualcosa come modelli, non c'è motivo per cui puoi legarti più facilmente se l'implementazione è scritta in C. In effetti, è praticamente per definizione che un'interfaccia C può essere compilato da qualsiasi implementazione in grado di soddisfare il contratto binario, inclusa un'implementazione in un'altra lingua. Esistono linguaggi diversi dal C ++ che possono implementare contratti binari C che possono funzionare in questo modo.

In sostanza si tratta di persone che non vogliono imparare nuove lingue o idee che hanno una semantica o funzioni realmente utili che cercano disperatamente di scegliere qualsiasi motivo per rimanere nell'era dei dinosauri.


4
Penso che tu abbia ragione e che i downvoter abbiano frainteso la domanda, ma in realtà la tua risposta manca per evidenziare quel potenziale fraintendimento: la domanda non riguarda "una biblioteca con un'interfaccia C" rispetto a "una biblioteca con un'interfaccia C ++", si tratta di "una libreria scritta interamente in C" vs. "una libreria con un'interfaccia C scritta in C ++".
Doc Brown,

@Snowman: Avere problemi di associazione C ++ non ha assolutamente nulla a che fare con qualsiasi problema con l'esposizione dei collegamenti C.
DeadMG

2
Quindi sceglierai una lingua che ha utili semantiche e funzionalità e poi ti costringerai a tradurle in un'interfaccia C? Sebbene le classi e i modelli possano essere evitabili (ma metti in discussione il motivo per cui stai usando C ++ in primo luogo), ogni funzione con associazione C deve catturare le eccezioni invece di lasciarle scappare nel codice C. Per non parlare del fatto che chiunque utilizzi la tua libreria C-compatibile ora ha a che fare con C ++ e i suoi scontri ABI e disallineamenti della libreria, oltre agli scontri ABI del substrato C e ai disallineamenti della libreria.
prosfilaes,

2
@prosfilaes: è banale convertire le eccezioni in codici di ritorno. Non è necessario evitare di utilizzare le classi poiché possono essere facilmente ridotte in un'API C e non è necessario evitare l'uso di modelli nella propria implementazione. E i tuoi utenti non hanno bisogno di fregarsene degli scontri ABI C ++ a meno che non stiano costruendo dal sorgente, nel qual caso, devi affrontare la lingua di origine, non importa quale sia.
DeadMG

4
Ho effettuato il downgrade non perché non si debba necessariamente scrivere una libreria con un'API C in C ++, ma perché ci sono buone ragioni per usare C oltre ad essere un dinosauro. Se stai scrivendo per un'API in linguaggio X, che sia C, FORTRAN, COBOL, BLISS o Java, c'è sempre un momento in cui sarà più semplice, più facile e più corretto scrivere in quella lingua, poi scrivere in un fantasista , linguaggio più divertente e interfaccia i due.
prosfilaes

2

Esistono due assi principali quando si interfaccia con un'altra lingua:

  • i concetti che l'interfaccia può riportare: solo valori? Riferimenti? farmaci generici?
  • come l'interfaccia è implementata in "binari" (chiamato ABI)

C ha un vantaggio rispetto a C ++ su quei due fronti:

  • C ha solo concetti per lo più semplici, che appaiono in quasi tutte le altre lingue 1
  • L'ABI dei binari C è deciso dal sistema operativo 2

Ora, perché la maggior parte delle lingue ha un insieme di concetti simile a quello di C potrebbe essere dovuto al fatto che è "semplice" o "preesistente"; non importa però, il punto è che lo fanno.

Al contrario, C ++ ha concetti complessi e l'ABI è deciso da ciascun compilatore (sebbene molti aderiscano all'ABI Itanimum, tranne che su Windows ...). In realtà c'era una proposta di Herb Sutter per fare in modo che i sistemi operativi riparassero un ABI C ++ (su una base per sistema operativo) per affrontare parzialmente questo problema. Inoltre, si dovrebbe notare che un FFI C ++ è possibile, D lo sta tentando 3 .

1 Tranne variadics ( ...), quelli non sono semplici

2 C ha un ABI standard?

3 Interfaccia da D a Legacy codice C ++


0

Fondamentalmente si riduce alla standardizzazione ABI. Mentre né C né C ++ hanno un ABI standardizzato che altre lingue possono usare per interfacciarsi tra i binari scritti, C è diventato uno standard di fatto, tutti lo sanno e tutti gli altri possono usare le stesse, semplici regole che il linguaggio ha rispetto tipi e chiamate di funzione.

Il C ++ potrebbe avere un ABI standard, ma Stroustrup ha affermato di non vederne la necessità. Dice anche che sarebbe difficile ottenere il consenso degli autori del compilatore (anche se dubito che - il comitato standard C ++ emetterebbe un ABI simile a quelli esistenti e gli autori del compilatore semplicemente altererebbero la prossima versione dei loro compilatori, che a volte sono incompatibili con i binari costruito con le vecchie versioni dei loro compilatori - Ricordo di aver ricompilato un paio di librerie con un nuovo compilatore Sun e di aver scoperto che non funzionavano con quelle vecchie)

Noterai che alcune aziende hanno iniziato a utilizzare un ABI standard, Microsoft ha iniziato questo processo con COM negli anni '90 e oggi lo hanno perfezionato nell'ABI WinRT (da non confondere con l'altro WinRT che fa riferimento a un tipo di sistema operativo di tabella) che consente ai programmi scritti in C # di comunicare con le librerie scritte in C o C ++ (ovvero il livello del sistema operativo di Microsoft è scritto in C ++, esposto con WinRT e utilizzato dalle applicazioni C # quando chiamano qualsiasi routine di runtime del sistema operativo)

Non c'è molto che nessuno può fare a meno che un ente normativo non acceleri e risolva questa situazione. Microsoft ovviamente vede il valore in esso e ha preso provvedimenti per risolverlo per la loro piattaforma.

Quindi la risposta è davvero C non fornisce legami linguistici. Succede che nessuno li abbia ascoltati e li abbia consumati a prescindere.


-2

Tutte le risposte non sono all'altezza del vero problema: la compilazione C ++ introduce "manipolazione dei nomi", quindi i binari sono incompatibili con le chiamate di funzioni "semplici".

Tutta la roba ABI è poco più che un tentativo di standardizzarla.

In generale non è garantito che sia possibile eseguire il cross-link di funzioni compilate con diversi compilatori, anche se si utilizza il semplice C ++. In precedenza era sicuro che fossero incompatibili, ma al giorno d'oggi si sta insinuando la standardizzazione;)

OTOH C è stato progettato proprio per essere un "assemblaggio di alto livello" e consentire qualsiasi tipo di interfaccia facile. Non dovrebbe sorprendere che sia più adatto al gradimento linguistico.

Nota a margine: il compilatore C ++ originale (cfront) ha effettivamente prodotto una sorgente C che doveva essere compilata, esattamente come gcc che produce Assembly "under the hood".

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.