Creazione di una libreria con ABI compatibile con le versioni precedenti che utilizza Boost

Sto lavorando su una certa libreria C ++ (o più framework). Voglio renderlo retrocompatibile con le versioni precedenti, preservando non solo la compatibilità API ma anche ABI (come l’ottimo lavoro che fa Qt).

Uso molte funzionalità di Boost e mi sembra che ciò renda imansible la compatibilità con le versioni precedenti, a meno che non costringa un utente ad avere esattamente la stessa (a volte vecchia) versione di Boost.

Esiste un modo (senza riscrivere 1/2 di Boost) per creare un “prefisso” attorno al suo spazio dei nomi / rinominarlo per evitare che interferisca con una versione utente di Boost?

Ad esempio la mia libXYZ utilizza Boost 1.33 e ha la class boost::foo . Nella versione 1.35 boost::foo stato aggiornato e il nuovo membro è stato aggiunto, quindi, boost::foo da 1.33 e 1.35 non sono compatibili con ABI. Quindi, un utente di libXYZ deve usare Boost 1.33 o ricompilare libXYZ con Boost 1.35 (che potrebbe già aver rotto alcune API in un modo che XYZ non compilerebbe).

Nota: sto parlando di UNIX / Linux OS con ELF dove il collegamento dinamico è simile al collegamento statico, quindi non è ansible collegarsi con due versioni diverse di librerie perché i simboli interferirebbero.

Una soluzione adatta a cui potrei pensare sta mettendo Boost in qualche altro spazio dei nomi privato. Quindi, libXYZ userebbe ::XYZ::boost::foo invece di ::boost::foo . Ciò eviterebbe la collisione con altre versioni di Boost che l’utente potrebbe utilizzare.

Quindi, la libXYZ continuerà a lavorare con Boost 1.33 collegato staticamente o dynamicmente ad esso con altri namespace, assumendo che:

  • Non esporrà l’API Boost all’esterno.
  • Manterrebbe la versione privata stabile dell’API esposta.

C’è un modo per fare queste cose con Boost?

Modifica: Alla fine ho deciso di creare uno script che rinominasse tutti i simboli boost nella sorgente con un simbolo personalizzato.

Fondamento logico: semplificazione del processo di compilazione, indipendente dal supporto per la visibilità del compilatore, inoltre, la visibilità funziona solo sulle librerie dinamiche, poiché ciò non funziona, quindi ho bisogno di build e dipendenze separate per ogni tipo di librerie.

Lo script è disponibile qui: http://art-blog.no-ip.info/files/rename.py

Modifica 2: l’ ultima versione di Boost BCP supporta la ridenominazione dello spazio dei nomi.

Fondamentalmente, assicurati che l’interfaccia pubblica della tua libreria non esponga Boost. Puoi sempre usarlo quanto vuoi internamente. In genere, avere l’interfaccia di una libreria dipende da un’altra libreria non è buona (a meno che non dipenda da una libreria standard come STL). Il boost si adatta quasi alla categoria di libreria “standard”, ma l’ABI cambia così tanto che la tua interfaccia non dovrebbe usarla.

Per essere sicuro di non esporre i simboli Boost, ci sono alcune cose che potresti fare:

A. compilare con -fvisibility=hidden e contrassegnare tutti i simboli pubblici con __attribute__((visibility("default"))) . potresti usare una macro per renderlo più semplice:

 #define ABI __attribute__((visibility("default"))) 

B. fai qualcosa del genere:

 #pragma GCC visibility push(hidden) #include  #pragma GCC visibility pop 

Dovresti anche racchiudere questo intorno a tutti gli altri simboli interni che non vuoi esportare, o dichiararlo con __attribute__((visibility("hidden"))) . Ancora una volta, puoi usare una macro per renderla più semplice:

 #define INTERNAL __attribute__((visibility("hidden"))) 

Di queste opzioni, mi piace Un migliore, perché ti fa pensare esplicitamente a quali simboli vengono esportati, così non esporti per errore le cose che non vuoi.

A proposito, puoi trovare molte più informazioni su come creare i DSO in Come scrivere librerie condivise di Ulrich Drepper.

In generale non è ansible fare affidamento su alcun tipo di ABI in C ++ oltre i binding C standard. Ma a seconda di quante supposizioni fai, puoi usare sempre più C ++ nell’interfaccia.

Ho trovato questo fantastico articolo sui passaggi per trasformare la tua API in un ABI stabile. Ad esempio, non passare mai i tipi di dati Standard C ++ Library (o Boost) attraverso l’interfaccia; potrebbe rompersi anche con una piccola correzione di bug alla libreria.

Alcuni esempi dei problemi a cui prestare attenzione quando pubblichi un’API compatibile con ABI sono:

  • Windows Debug heap . Devi essere sicuro che tutte le allocazioni e le deallocazioni si trovano sullo stesso lato di un “modulo” (cioè, eseguibile o DLL).
  • Problema di interfaccia binaria fragile . Anche se entrambi i lati del tuo sistema usano costantemente lo stesso compilatore e le stesse librerie, devi fare attenzione in C ++ su ciò che pubblichi nei tuoi file .h e dove si verificano le allocazioni.

Se segui l’articolo collegato, troverai soluzioni per questi e altri problemi.

Modifica :

Ho anche trovato un interessante articolo pubblicato da Microsoft che descrive come funzionano le interfacce COM, prendendo un progetto C ++ e trasformandolo in COM. È mia convinzione che uno dei motivi principali per cui Microsoft ha sviluppato la COM è stato quello di risolvere il problema di Fragile Binary Interface che ha C ++, in modo che possano inviare DLL con le API di pubblicazione orientate agli oggetti.

Prendi in considerazione l’utilizzo di un strumento di controllo della conformità per mantenere un’interfaccia API / ABI stabile.

Dovresti essere in grado di fare qualcosa del genere:

 namespace XYZ { #include  } 

E dovrebbe scaricare le intestazioni di boost nel namespace XYZ. Si noti, tuttavia, che ciò funzionerà solo con le librerie di sola intestazione.