Esiste un linguaggio di definizione dell'interfaccia per le librerie software?

1

Supponiamo che io stia scrivendo una libreria C ++ che intendo distribuire in forma binaria, con interfacce da altri linguaggi (ad esempio Python). L'approccio 'facile' di appena compilazione della biblioteca e distribuire il DLL o quadro non funziona bene .

Per farlo funzionare è necessario compilare la libreria con ogni compilatore supportato e ogni opzione del compilatore supportato e cose brutte possono accadere se non lo fai.

Il problema è che l'ABI di C ++ in generale non è stabile e l'ABI di STL è decisamente non stabile. Una sorta di soluzione è di attenersi al "semplice" C ++ nella tua API pubblica - classi semplici con tipi di base. Il problema è che non usi i buoni tipi di STL come std::string e 'std :: vector e finisci per reimplementarli.

Quindi mi chiedo se c'è una soluzione migliore usando una libreria Interface Definition Language (IDL). Ce ne sono molti per i protocolli di rete, come Thrift, Protobuf, gRPC, CapnProto, ecc. Ce n'è uno per le librerie?

La soluzione ideale prenderebbe quindi questo file IDL, genererà un wrapper C- > C ++ attorno alla libreria C ++, così che il suo ABI sia ora C ABI. Potrebbe quindi generare anche wrapper open source attorno alla libreria C per qualsiasi lingua tu voglia (incluso C ++).

So che è una specie di folle per avvolgere C ++ con un'API C e quindi avvolgerlo con un'API C ++. Ma non riesco a vedere un modo migliore.

Esiste? È pazzo? C'è un modo migliore?

    
posta Timmmm 09.08.2017 - 18:04
fonte

2 risposte

5

Sì, ci sono cose simili per le biblioteche che non sono (necessariamente) accessibili tramite una rete. La COM di Microsoft sarebbe un ovvio esempio. L'OS / 2 quasi obsoleto di IBM ha fatto lo stesso (usando un'implementazione di CORBA, se la memoria serve).

Sebbene sia in gran parte caduto dal favore, CORBA ha isolato la maggior parte dei componenti dal livello di trasporto, quindi poteva agire come un semplice wrapper attorno a una libreria sullo stesso sistema, o un livello simile a Thrift per parlare con un server su qualche altro sistema .

La cosa più vicina (attualmente popolare) che riesco a pensare su Linux è D-bus, che ha una sorta di scopo simile, ma si basa principalmente sulla messaggistica piuttosto che sulle chiamate di funzione. Questi possono servire più o meno gli stessi scopi, ma dal tuo punto di vista (wrapper around a library) sono ancora un po 'diversi.

Da quando hai tirato su Thrift, ti faccio notare che Thrift usa un modello abbastanza semplice per i suoi livelli di trasporto, quindi è abbastanza fattibile usarlo come un wrapper attorno alle librerie su una singola macchina. La via più ovvia su sistemi di tipo Unix sarebbe probabilmente quella di usare socket di dominio Unix per la comunicazione. Ciò fornisce un'interfaccia socket, ma la comunicazione è (almeno normalmente) tramite buffer di memoria condivisa gestiti dal kernel, quindi nonostante l'interfaccia di rete, è piuttosto veloce.

    
risposta data 10.08.2017 - 03:25
fonte
3

Per costruire il commento di @ amon, avvolgere il codice C ++ con un file di intestazione C è ciò che vuoi fare, ed è ampiamente usato e ha molti vantaggi.

Perché dovresti ridurre l'interfaccia esterna delle librerie C ++ a un file di intestazione C?

  • Molti linguaggi popolari hanno in particolare lo scopo di chiamare in modo nativo le funzioni C (ad esempio, C ++, Objective C, Fortran, Cython, Golang). Le lingue che non consentono le chiamate native alle funzioni C hanno quasi tutti un modo standard di avvolgere il codice C per chiamarlo (Java, Python, Matlab).
  • Lo standard C cambia molto poco rispetto a C ++, il che riduce notevolmente il rischio di parti del tuo file di intestazione utilizzando funzionalità deprecate. Molte librerie sono ancora distribuite con i file di intestazione ANSI C.
  • L'esposizione di oggetti C ++ nei file di intestazione interrompe generalmente l'incapsulamento, il che significa che le modifiche alla libreria sono in grado di suddividere il codice downstream (puoi inoltrare le classi declare ma questo porta ad altre sfide).
  • Limita notevolmente la superficie della libreria (minimizza l'accoppiamento del codice downstream).
  • Nessun nome di mangling .

Puoi facilmente passare STL's std :: vector e std :: string in funzioni C abbastanza facilmente usando le funzioni .data() e .size() . Questo molte volte significa che hai bisogno di due chiamate di funzioni: 1) una dove la libreria risponde di quanta memoria ha bisogno, e 2) dove la memoria allocata viene passata nella libreria e riempita / modificata. Che arriva a un punto importante, quando scrivi una libreria, non mischiare l'allocazione della memoria e la de-allocazione tra i confini della biblioteca (se la libreria la alloca, la libreria dovrebbe liberarla e se il chiamante lo assegna, il chiamante dovrebbe liberarlo). Passare attorno ad oggetti come std::vector() violerebbe questo, poiché internamente alloca, rialloca e rilascia la memoria.

    
risposta data 11.08.2017 - 21:36
fonte

Leggi altre domande sui tag