Ci sono molti problemi di portabilità con C ++, che è solo a causa della mancanza della sua standardizzazione a livello binario.
Non penso sia così semplice. Le risposte fornite forniscono già un'ottima motivazione sulla mancanza di attenzione alla standardizzazione, ma il C ++ potrebbe essere troppo ricco di un linguaggio per essere adatto per competere sinceramente con il C come standard ABI.
Siamo in grado di ingannare il nome derivante da sovraccarico di funzioni, incompatibilità vtable, incompatibilità con eccezioni oltre i limiti del modulo, ecc. Tutti questi sono un vero dolore, e vorrei che potessero almeno standardizzare i layout di vtable.
Ma uno standard ABI non riguarda solo la produzione di dylibs C ++ prodotti in un compilatore in grado di essere utilizzati da un altro binario creato da un altro compilatore. L'ABI è utilizzato in più lingue . Sarebbe bello se almeno potessero coprire la prima parte, ma non vedo in alcun modo C ++ competere veramente con C nel tipo di ABI universale così cruciale per realizzare i dylibs più ampiamente compatibili.
Immagina una semplice coppia di funzioni esportate in questo modo:
void f(Foo foo);
void f(Bar bar, int val);
... e immaginare Foo
e Bar
sono classi con costruttori con parametri, copiare costruttori, costruttori spostare, e distruttori non banali.
Quindi prendi lo scenario di un Python / Lua / C # / Java / Haskell / etc. lo sviluppatore sta cercando di importare questo modulo e utilizzarlo nella loro lingua.
Per prima cosa avremmo bisogno di uno standard di modifica del nome su come esportare i simboli utilizzando il sovraccarico delle funzioni. Questa è una parte più semplice Eppure non dovrebbe davvero essere chiamato "mangling". Poiché gli utenti del dylib devono cercare i simboli per nome, i sovraccarichi qui dovrebbero portare a nomi che non sembrano un disastro completo. Forse i nomi dei simboli potrebbero essere simili "f_Foo"
"f_Bar_int"
o qualcosa del genere. Dovremmo essere sicuri che non possano scontrarsi con un nome effettivamente definito dallo sviluppatore, magari riservando alcuni simboli / caratteri / convenzioni per l'utilizzo dell'ABI.
Ma ora uno scenario più difficile. In che modo lo sviluppatore Python, ad esempio, invoca costruttori di spostamento, copia costruttori e distruttori? Forse potremmo esportarli come parte del dylib. Ma cosa succede se Foo
e Bar
vengono esportati in diversi moduli? Dobbiamo duplicare i simboli e le implementazioni associati a questo dylib o no? Suggerirei di farlo, dal momento che potrebbe diventare davvero fastidioso molto velocemente, altrimenti iniziare a doversi impigliare in più interfacce dylib solo per creare un oggetto qui, passarlo qui, copiarne uno lì, distruggerlo qui. Mentre la stessa preoccupazione di base potrebbe applicarsi in qualche modo in C (solo più manualmente / esplicitamente), C tende ad evitarlo solo per natura del modo in cui le persone programmano con esso.
Questo è solo un piccolo esempio dell'imbarazzo. Cosa succede quando una delle f
funzioni sopra lancia una BazException
(anche una classe C ++ con costruttori e distruttori e deriva std :: exception) in JavaScript?
Nella migliore delle ipotesi, possiamo solo sperare di standardizzare un ABI che funziona da un binario prodotto da un compilatore C ++ a un altro binario prodotto da un altro. Sarebbe fantastico, ovviamente, ma volevo solo sottolineare questo. Accompagnare in genere tali preoccupazioni per la distribuzione di una libreria generalizzata che funziona tra compilatori è spesso anche il desiderio di renderla veramente generalizzata e compatibile con più lingue.
Soluzione suggerita
La mia soluzione suggerita dopo aver faticato a trovare modi per utilizzare le interfacce C ++ per API / ABI per anni con interfacce in stile COM è quella di diventare uno sviluppatore "C / C ++" (gioco di parole).
Usa C per creare quegli ABI universali, con C ++ per l'implementazione. Possiamo ancora fare cose come le funzioni di esportazione che restituiscono i puntatori a classi C ++ opache con funzioni esplicite per creare e distruggere tali oggetti nell'heap. Cerca di innamorarti dell'estetica C dal punto di vista ABI anche se utilizziamo totalmente C ++ per l'implementazione. Le interfacce astratte possono essere modellate utilizzando tabelle di puntatori a funzioni. È noioso racchiudere questa roba in un'API C, ma i vantaggi e la compatibilità della distribuzione che ne deriva tenderanno a renderlo molto utile.
Quindi, se non ci piace usare questa interfaccia così direttamente (probabilmente non dovremmo almeno per ragioni RAII), possiamo racchiuderlo in una libreria C ++ collegata staticamente fornita con l'SDK. I client C ++ possono usarlo.
I client Python non vorranno usare direttamente un'interfaccia C o C ++ in quanto non c'è modo di renderli pythonique. Avranno voglia di avvolgerlo nelle proprie interfacce Pythonique, quindi in realtà è una buona cosa che stiamo solo esportando un minimo C API / ABI per renderlo il più semplice possibile.
Penso che gran parte del settore C ++ trarrebbe beneficio dal fare questo piuttosto che provare a spedire ostinatamente interfacce in stile COM e così via. Inoltre, renderebbe più facile la vita di tutti gli utenti di questi dylibs per non doversi preoccupare di ABI scomodi. C lo rende semplice e la sua semplicità dal punto di vista ABI ci consente di creare API / ABI che funzionano in modo naturale e con minimalismo per tutti i tipi di SFI.