2013-03-15 15 views
6

Sto scrivendo una libreria C (condivisa). È iniziato come una singola unità di traduzione, nella quale potevo definire un paio di variabili globali static, da nascondere da moduli esterni.Come nascondere una variabile globale, che è visibile su più file?

Ora che la libreria è cresciuta, voglio suddividere il modulo in un paio di file sorgente più piccoli. Il problema è che ora ho due opzioni per le variabili globali citati:

  1. avere copie private a ogni file sorgente e in qualche modo di sincronizzazione i loro valori attraverso chiamate di funzione - questo otterrà molto brutto molto veloce.

  2. Rimuovere la definizione static, in modo che le variabili siano condivise tra tutte le unità di traduzione utilizzando extern - ma ora il codice dell'applicazione collegato alla libreria può accedere a tali globali, se la dichiarazione richiesta viene effettuata lì.

Quindi, c'è un modo semplice per rendere condivisa la variabile globale privata su più unità di traduzione specifiche?

risposta

6

Si desidera l'estensione visibility attribute di GCC.

In pratica, qualcosa di simile:

#define MODULE_VISIBILITY __attribute__ ((visibility ("hidden"))) 
#define PUBLIC_VISIBILITY __attribute__ ((visibility ("default"))) 

(probabilmente si desidera #ifdef le macro di cui sopra, utilizzando alcuni trucchi di configurazione à la autoconf e altri autotools; su altri sistemi si dovrebbe solo avere definizioni vuote come #define PUBLIC_VISIBILITY /*empty*/ ecc ...)

Poi, dichiarare una variabile:

int module_var MODULE_VISIBILITY; 

o una funzione

void module_function (int) MODULE_VISIBILITY; 

Quindi è possibile utilizzare module_var oppure chiamare module_function all'interno della vostra libreria condivisa, ma non al di fuori.

Vedere anche l'opzione di generazione del codice -fvisibility di GCC.

BTW, è inoltre possibile compilare l'intera libreria con -Dsomeglobal=alongname3419a6 e utilizzare someglobal come al solito; per trovare davvero il tuo utente dovrebbe passare la stessa definizione del preprocessore al compilatore, e puoi rendere il nome alongname3419a6 casuale e abbastanza improbabile da rendere improbabile la collisione.


PS. Questa visibilità è specifica per GCC (e probabilmente per librerie condivise ELF come quelle su Linux). Non funzionerà senza GCC o al di fuori delle librerie condivise .... quindi è abbastanza specifico per Linux (anche se alcuni altri sistemi, forse Solaris con GCC, ce l'hanno). Probabilmente alcuni altri compilatori (clang da LLVM) potrebbero supportare anche quello su Linux per le librerie condivise (non statiche).In realtà, il vero nascondiglio (alle diverse unità di compilazione di una singola libreria condivisa) viene svolto principalmente dal linker (perché le librerie condivise di ELF lo consentono).

+1

Cosa ti fa pensare che l'OP stia utilizzando GCC e che la portabilità del codice non sia desiderata? – Lundin

+5

Ho detto che è un'estensione GCC. Ho aggiunto un PS ripetendolo. –

+0

Sì, l'hai fatto. Ho commentato dal momento che non penso che Stack Overflow dovrebbe essere un forum GCC, tutti sembrano assumere implicitamente GCC al giorno d'oggi. – Lundin

4

La soluzione più semplice ("vecchia scuola") è semplicemente non dichiarare la variabile nell'intestazione pubblica prevista.

Divide l'intestazione delle tue librerie in "header.h" e "header-internal.h", e dichiara le cose interne in quest'ultimo.

Naturalmente, dovresti anche fare attenzione a proteggere il nome della tua biblioteca-variabile globale in modo che non entri in collisione con il codice utente; presumibilmente hai già un prefisso che usi per le funzioni per questo scopo.

È anche possibile includere le variabili in un struct, per renderlo più pulito da allora solo un simbolo effettivo è globalmente visibile.

+2

"Puoi proteggere il codice da Murphy, ma mai da Machiavelli." – aschepler

+0

Grazie. Sicuramente non dovresti metterlo nel file di intestazione pubblico, ma, come ho detto nella mia domanda, puoi ancora accedere alla variabile con la dichiarazione corretta nella tua applicazione. Cerco di mantenere i nomi delle variabili chiari e concisi, quindi la probabilità che l'utente definisca nomi simili, esp. nel contesto del codice che usa quella libreria, è leggermente superiore a zero. Questo è il motivo per cui sto cercando di proteggerlo. Inserirli in una 'struct', tuttavia, è un'idea che potrei usare per ridurre ulteriormente questa probabilità. – ysap

+0

@ysap Se si collega l'intera libreria a "qualcosa", che non è un codice sorgente grezzo, tutti questi problemi dello spazio dei nomi scompariranno e l'utente della libreria dovrà solo preoccuparsi dell'intestazione pubblica. – Lundin

2

È possibile offuscare le cose con strutture mascherate, se si vuole davvero nascondere le informazioni nel miglior modo possibile. per esempio. in un file di intestazione,

struct data_s { 
    void *v; 
}; 

E da qualche parte nella vostra fonte:

struct data_s data; 
struct gbs { 
    // declare all your globals here 
} gbss; 

e poi:

data.v = &gbss;

È quindi possibile accedere a tutte le variabili globali tramite: ((struct gbs *)data.v)->

+0

Grazie. Fondamentalmente ciò che suggeriva @unwind. L'offuscamento è la soluzione semplice, ma rende il mio codice più brutto. – ysap

+0

@ysap È possibile utilizzare le macro per renderlo un po 'più ordinato. Rende le cose più difficili, certo, ma è molto più difficile trovare le variabili globali. – teppic

0

So che questo non sarà quello che intendi è inteso, ma è possibile lasciare le variabili globali statiche e dividerle in più file sorgente.

Copia anche le funzioni che scrivono nella variabile statica corrispondente nello stesso file sorgente dichiarate statiche.

Dichiarare funzioni che leggono la variabile statica in modo che i file di origine esterni dello stesso modulo possano leggere il suo valore.

In un modo che lo rende meno globale. Se possibile, la migliore logica per rompere i file di grandi dimensioni in quelli più piccoli è prendere quella decisione in base ai dati.

Se non è possibile farlo in questo modo, è possibile scaricare tutte le variabili globali in un unico file sorgente come statico e accedervi dagli altri file di origine del modulo per funzioni, rendendolo così ufficiale se qualcuno sta manipolando le tue variabili globali almeno sai come. Ma allora probabilmente è meglio usare il metodo di @ unwind.