L'indirizzo C ++ 11 riguardava il passaggio di oggetti std lib tra i confini della libreria dinamica / condivisa? (vale a dire dll e così)?


34

Una delle mie principali lamentele riguardo al C ++ è quanto sia difficile in pratica passare oggetti libreria std al di fuori dei limiti della libreria dinamica (cioè dll / so).

La libreria std è spesso solo intestazione. Il che è fantastico per fare alcune fantastiche ottimizzazioni. Tuttavia, per le DLL, sono spesso costruite con diverse impostazioni del compilatore che possono influire sulla struttura / codice interno dei contenitori di una libreria std. Ad esempio, in MSVC una DLL può compilare con il debug iteratore attivo mentre un'altra crea con esso disattivato. Queste due DLL potrebbero incorrere in problemi durante il passaggio dei contenitori std. Se espongo std::stringnella mia interfaccia, non posso garantire che il codice utilizzato dal client std::stringcorrisponda esattamente a quello della mia libreria std::string.

Questo porta a problemi di debug difficili, mal di testa, ecc. O controlli rigidamente le impostazioni del compilatore nella tua organizzazione per prevenire questi problemi o usi un'interfaccia C più semplice che non avrà questi problemi. Oppure specifica ai tuoi clienti le impostazioni del compilatore previste che dovrebbero usare (il che fa schifo se un'altra libreria specifica altre impostazioni del compilatore).

La mia domanda è se C ++ 11 ha provato a fare qualcosa per risolvere questi problemi?


3
Non conosco la risposta alla tua domanda, ma posso dire che le tue preoccupazioni sono condivise; sono la chiave del motivo per cui non userò il C ++ nei miei progetti, poiché apprezziamo la stabilità ABI rispetto alla compressione di ogni ultimo ciclo di potenziale efficienza.
Donal Fellows il

2
Si prega di distinguere. È difficile tra DLLs. Tra una SOs ha sempre funzionato bene.
Jan Hudec,

1
A rigor di termini, questo non è un problema solo C ++. È possibile avere questo problema con altre lingue.
MrFox,

2
@JanHudec Posso garantire che tra SO non funziona quasi così magicamente come sembra indicare. Data la visibilità dei simboli e il modo in cui spesso funziona la manipolazione dei nomi, potresti essere più isolato da un problema, ma compilare uno .so con diversi flag / ecc. E supponendo che tu possa collegarlo in un programma con altri flag è un destinatario del disastro.
sabato

3
@sdg: con flag predefiniti e visibilità predefinita funziona. Se li cambi e ti trovi nei guai, è un tuo problema e nessun altro.
Jan Hudec,

Risposte:


20

Hai ragione a dire che qualsiasi cosa STL - in realtà, qualsiasi cosa provenga da una libreria di terze parti che è stata modellata - è meglio evitare in qualsiasi API C ++ pubblica. Volete anche seguire il lungo elenco di regole su http://www.ros.org/reps/rep-0009.html#definition per inibire la rottura ABI che rende la programmazione delle API pubbliche C ++ un compito ingrato.

E la risposta su C ++ 11 è no, questo standard non lo tocca. Più interessante è perché no? La risposta è perché C ++ 17 è molto toccante e per l'implementazione dei moduli C ++ abbiamo bisogno di modelli esportati per funzionare, e per questo abbiamo bisogno di un compilatore di tipo LLVM come clang che può scaricare l'intero AST su disco e quindi eseguire ricerche dipendenti dal chiamante per gestire i numerosi casi di violazione ODR in qualsiasi grande progetto C ++ - che, tra l'altro, include un sacco di codice GCC ed ELF.

Infine, vedo molti commenti di odio e pro-GCC su MSVC. Questi sono molto male informati: GCC su ELF è fondamentalmente e irrimediabilmente incapace di produrre codice C ++ valido e corretto. Le ragioni sono molte e molte, ma citerò rapidamente un esempio di caso: GCC su ELF non può produrre in sicurezza estensioni Python scritte usando Boost.Python dove più di un'estensione basata su Boost.Python viene caricata in Python. Questo perché ELF con la sua tabella dei simboli C globale è semplicemente incapace di progettare la prevenzione delle violazioni ODR che causano segfault, mentre PE e MachO e le specifiche proposte dei moduli C ++ utilizzano tutte le tabelle dei simboli per modulo, il che per inciso significa anche tempi di avvio del processo molto più rapidi. E ci sono molti altri problemi: vedi StackOverflow a cui ho risposto di recente ahttps://stackoverflow.com/questions/14268736/symbol-visibility-exceptions-runtime-error/14364055#14364055 per esempio dove i lanci di eccezioni C ++ sono irrimediabilmente rotti sull'ELF.

Ultimo punto: per quanto riguarda l'interoperabilità tra diversi STL, questo è un grande problema per molti grandi utenti aziendali che cercano di mescolare librerie di terze parti strettamente integrate con alcune implementazioni di STL. L'unica soluzione è un nuovo meccanismo per C ++ per gestire l'interoperabilità STL e, mentre ci sono, potresti anche riparare l'interoperabilità del compilatore in modo da poter (ad esempio) mescolare file di oggetti compilati MSVC, GCC e clang e tutto funziona . Guarderei lo sforzo del C ++ 17 e vedrei cosa succederà lì nei prossimi anni - sarei sorpreso se nulla lo facesse.


Ottima risposta! Spero solo che Clang migliori la compatibilità di Windows e possa impostare un buon compilatore standard predefinito. Il sistema testuale di inclusione / intestazione di C ++ è orribile, non vedo l'ora che i moduli semplificino l'organizzazione del codice C ++, acceleri i tempi di compilazione e migliorino l'interoperabilità del compilatore con catture che violano ODR.
Alessandro Stamatto,

3
Personalmente, mi aspetto un aumento sostanziale dei tempi di compilazione. Attraversare rapidamente un AST intra-modulo è molto difficile e probabilmente avremo bisogno di una cache di memoria condivisa in memoria. Tuttavia, quasi tutto il resto va meglio. A proposito, i file di intestazione sono sicuramente in circolazione, gli attuali moduli C ++ hanno file di interfaccia mappati da 1 a 1 ai file di intestazione. Inoltre, i file di interfaccia generati automaticamente saranno C ++ legali, quindi un'intestazione legacy semplicemente fa filtrare le macro C e sputarle come file di interfaccia. Bello eh?
Niall Douglas,

Freddo! Ho tanti dubbi sui moduli. Il sistema del modulo prenderà in considerazione l'inclusione testuale e l'inclusione simbolica? Con la presente direttiva include il compilatore deve ricompilare decine di migliaia di righe di codice ripetutamente per ogni file di origine. Il sistema dei moduli consentirà un giorno il codice senza dichiarazioni a termine? Migliorerà / faciliterà gli strumenti di costruzione?
Alessandro Stamatto,

2
-1 per aver suggerito che tutti i modelli di terze parti sono sospetti. La modifica della configurazione è indipendente dal fatto che l'oggetto da configurare sia un modello.
DeadMG

1
@Alessandro: i moduli C ++ proposti disabilitano esplicitamente le macro C. Puoi usare i template o nowt. Le interfacce proposte sono C ++ legali, semplicemente autogenerate, e possono essere opzionalmente precompilate per la velocità di replica, ovvero non aspettarsi alcun aumento di velocità rispetto alle intestazioni precompilate esistenti. Le ultime due domande, in realtà non lo so: dipende :)
Niall Douglas,

8

Le specifiche non hanno mai avuto questo problema. Questo perché ha un concetto chiamato "una regola di definizione", che impone che ogni simbolo abbia esattamente una definizione nel processo in esecuzione.

Le DLL di Windows violano questo requisito. Ecco perché ci sono tutti questi problemi. Quindi dipende da Microsoft ripararlo, non dal comitato di standardizzazione C ++. Unix non ha mai avuto questo problema, perché le librerie condivise funzionano in modo diverso lì e per impostazione predefinita si conformano a una regola di definizione (puoi esplicitamente romperlo, ma ovviamente lo fai solo se sai che puoi permetterlo e devi schiacciare i pochi cicli extra).

Le DLL di Windows violano una regola di definizione perché:

  • Esse codificano da quale libreria dinamica verrà utilizzato un simbolo durante il tempo di collegamento statico e risolvono i simboli staticamente all'interno della libreria che li definisce. Quindi, se lo stesso simbolo debole viene generato in più librerie condivise e quelle librerie che vengono utilizzate in un singolo processo, il linker dinamico non ha alcuna possibilità di unire quei simboli. Di solito tali simboli sono membri statici o impedimenti di classe delle istanze del modello e ciò causa problemi durante il passaggio di istanze tra codice in diverse DLL.
  • Indicano chiaramente se il simbolo verrà importato dalla libreria dinamica già durante la compilazione. Pertanto, il codice collegato staticamente ad alcune librerie è incompatibile con il codice collegato dinamicamente alla stessa libreria.

Unix che utilizza le esportazioni in formato ELF importa implicitamente tutti i simboli esportati per evitare il primo problema e non distingue tra simboli risolti staticamente e dinamicamente fino al tempo di collegamento statico per evitare il secondo.


L'altro problema riguarda i flag del compilatore. Tale problema esiste per qualsiasi programma composto da più unità di compilazione, le librerie dinamiche non devono essere coinvolte. Tuttavia è molto peggio su Windows. Su Unix non importa se si collega staticamente o dinamicamente, nessuno collega comunque il runtime standard staticamente (in Linux potrebbe persino essere illegale) e non esiste un runtime di debug speciale, quindi un build è abbastanza buono. Ma il modo in cui Microsoft ha implementato collegamenti statici e dinamici, runtime di debug e release e alcune altre opzioni significa che hanno causato l'esplosione combinatoria delle varianti di libreria necessarie. Ancora una volta problema di piattaforma piuttosto che problema di linguaggio C ++.


2
@DougT .: GCC non ha nulla a che fare con questo. La piattaforma ABI ha. In ELF, il formato oggetto utilizzato dalla maggior parte di Unices, le librerie condivise esportano tutti i simboli visibili e importano tutti i simboli che esportano. Quindi, se qualcosa viene generato in più librerie, il linker dinamico utilizzerà la prima definizione per tutti. Semplice, elegante e funzionante.
Jan Hudec,

1
@MartinBa: non c'è nulla da unire, ma non importa fintanto che è lo stesso e fintanto che non dovrebbe essere unito in primo luogo. Sì, se si utilizzano impostazioni del compilatore incompatibili su una piattaforma ELF, si ottiene lo stesso disordine di ovunque e ovunque. Anche se non si usano librerie condivise, è un po 'fuori tema qui.
Jan Hudec,

1
@Jan - è rilevante per la tua risposta. Scrivi: "... una regola di definizione ... Le DLL di Windows violano questo requisito ... le librerie condivise funzionano in modo diverso [su UNix] ..." ma la domanda posta riguarda problemi con roba std-lib (definita nelle intestazioni) e la ragione per cui non c'è nessun problema su Unix non ha nulla a che fare con SO vs. DLL ma con il fatto che su Unix (apparentemente) esiste solo una versione compatibile della libreria standard mentre su Windows MS ha scelto di avere versioni incompatibili (debug) (con controllo esteso ecc.).
Martin Ba,

1
@MartinBa: No, il motivo principale per cui esiste un problema su Windows è che il meccanismo di esportazione / importazione utilizzato su Windows non può unire correttamente i membri statici e gli impedimenti di classe delle classi di modelli in tutti i casi e non può unire simboli collegati staticamente e dinamicamente. È peggiorato molto dalle molteplici varianti della libreria, ma il problema principale è che C ++ ha bisogno di flessibilità dal linker che il linker dinamico di Windows non ha.
Jan Hudec,

4
Penso che questa implicazione che la specifica DLL sia rotta e la corrispondente richiesta di Msft di 'ripararlo' siano fuori luogo. Il fatto che le DLL non supportino determinate funzionalità di C ++ non è un difetto della specifica DLL. Le DLL sono un meccanismo di impacchettamento indipendente dalla lingua, indipendente dal fornitore e ABI per esporre i punti di ingresso al codice macchina ("chiamate di funzione") e ai BLOB di dati. Non sono mai stati pensati per supportare in modo nativo le funzionalità avanzate di una determinata lingua. Non è colpa di Msft o delle specifiche DLL che alcune persone vogliono che siano qualcos'altro.
Euro Micelli,

6

No.

C'è un sacco di lavoro in corso per sostituire il sistema di intestazione, caratteristica che si chiama Moduli e che potrebbe avere un impatto su questo, ma certamente non grande.


2
Non credo che il sistema di intestazione avrebbe alcun impatto su questo. I problemi sono che le DLL di Windows violano una regola di definizione (il che significa che non seguono le specifiche C ++, quindi il comitato C ++ non può farci nulla) e che ci sono così tante varianti di runtime standard in Windows, che il comitato C ++ può " non fare nulla per nessuno dei due.
Jan Hudec,

1
No, non lo fanno. Come potrebbero, le specifiche non menzionano nemmeno qualcosa del genere. Oltre a ciò, quando un programma (Windows) è collegato alle dll di Windows, l'ODR è soddisfatto: tutti i simboli visibili (esportati) devono obbedire all'ODR.
Paul Michalik,

@PaulMichalik C ++ copre il collegamento (fase 9) e mi sembra che almeno il collegamento in fase di caricamento di DLL / SO rientri nella fase 9. Ciò significa che i simboli con collegamento esterno (esportato o meno) dovrebbero essere collegati e conformi a l'ODR. Il collegamento dinamico con LoadLibrary / dlopen ovviamente non rientra in tali requisiti.
bames53

@ bames53: IMHO, le specifiche sono troppo deboli per consentire dichiarazioni di quel tipo. Un .dll / .so potrebbe essere visto come un "programma" da solo. Quindi, le regole erano soddisfatte. Qualcosa come caricare altri "programmi" in fase di esecuzione è così sottostimato dallo standard che qualsiasi affermazione al riguardo è piuttosto arbitraria.
Paul Michalik,

@PaulMichalik Se un eseguibile richiede il collegamento del tempo di caricamento, prima del collegamento del tempo di caricamento ci sono entità esterne lasciate irrisolte e mancano le informazioni necessarie per l'esecuzione. LoadLibrary e dlopen non rientrano nelle specifiche, ma il collegamento del tempo di caricamento piuttosto chiaramente deve far parte della fase 9.
bames53
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.