17

Ho creato un sistema che registra automaticamente gli oggetti funzione (funtori) in una mappa basata sul costruttore di un'istanza globale.Variabile globale C++ non inizializzata quando collegata tramite librerie statiche, ma OK quando compilata con l'origine

In ogni file cpp che definisce il functor, esiste un'istanza globale dell'istanza della classe registrar per registrare il functor su un oggetto singleton std::map<int, std::function<...> >.

Questa è la definizione di classe di registrar:

template 
< 
    typename map_type, 
    typename handler_type 
> 
struct registrar 
{ 
    registrar 
     (
      map_type& map_object, 
      boost::uint16_t cmd_code, 
      const handler_type& handler 
     ) 
     { 
      map_object.insert(std::pair<boost::uint16_t, handler_type>(cmd_code, handler)); 
     } 
}; 

in ogni file cpp. L'istanza globale è definita in questo modo:

namespace one_way 
{ 
    static registrar <in_out_map_type, handler> 
     post_receiver(in_out_map_type::instance(), command, handlers()); 
} 

Tutto funziona correttamente se si compila tutto il cpp con il main.cpp insieme. Ma se compilo il file cpp in una libreria statica e lo collego a main.cpp, la registrazione non funziona.

Ho eseguito il test con VC10 e GCC4.61 su Windows & e Ubuntu 11.10. Entrambi falliscono.

Ho trovato a thread with the same problem ma OP non ha detto se l'ha risolto o no.

Mi manca qualcosa?


Modifica


Grazie per tutte le risposte tra i commenti.

Ogni risposta mi ha davvero aiutato a pensare di più e ad approfondire questo metodo. Dopo tutto lo studio e prove, alla fine ho rinunciato all'idea di fare affidamento su variabile globale/static per l'auto-registrazione attraverso i confini binari, perché non c'è portatile modo per garantire che funzionerà.

Il mio ultimo modo è di mantenere la registrazione all'interno di un file binario.

+0

Non viene mostrato come si sta chiamando il registrar per le funzioni registrate. Inoltre, c'è un errore di battitura nell''istanza globale '('handlers()' invece di 'handler()')? A meno che manchi qualcosa, ciò definisce una funzione, non una variabile globale. Quindi, ci sono, mi sembra, un numero di punti di connessione mancanti nell'immagine ... –

+4

Affidarsi all'inizializzazione statica per registrare le funzioni è probabilmente un errore un po 'incline a lungo termine. Hai preso in considerazione solo la registrazione esplicita? –

+0

Per GCC, date un'occhiata a -Wl, - aiuta l'intero archivio. –

risposta

-1

Credo che il tuo file oggetto dalla libreria non sia collegato. Guarda come Microsoft gestisce il simbolo acrused (la distinzione tra maiuscole e minuscole, non ricordo il caso e non c'è MSVC su questa macchina).

Una volta che sai come gestiscono acrtused, fai la stessa cosa con la tua variabile globale per forzare il collegamento.

Si aggiornerà se trovo la risposta.

Ecco un paio di possibilità per forzare le cose a collegare e inizializzare in un ordine un po 'forzato.

Look here per una risposta GCC.

Look here per MSVC10.

+0

Grazie per i collegamenti. per favore vedi la mia modifica nell'OP. –

0

Non sono sicuro se questo sarà di aiuto o no, ma ho fatto una domanda che suona molto simile:

C++ runtime knowledge of classes

Forse vale la pena dare un'occhiata a?

+0

Non ho alcun problema con ottenere il codice di registrazione eseguito se include il codice sorgente in un progetto, solo che è rimosso dal linker se incluso come libreria (statico o dinamico). Inserire il codice di registrazione nel file di intestazione potrebbe funzionare e passare l'ottimizzazione del linker. Ma nel mio caso, invalida qual è lo scopo dell'utilizzo di una libreria: ridurre i tempi di compilazione spostando la parte lenta (codice di registrazione fortemente modellato) in una libreria. Grazie per il tuo link. –

+0

La registrazione viene effettivamente effettuata in un file .cc ..., a patto che l'app utilizzi la fabbrica per la creazione dei materiali registrati, le registrazioni non verranno rimosse automaticamente. Io stesso ho usato la stessa cosa in un file .a e compilato e collegato a esso senza problemi. –

+0

Nel mio caso, non faccio riferimento alla fabbrica nel codice cliente, piuttosto, io uso solo la mappa pre-registrata, quindi non esporto alcun codice relativo alla registrazione, a sua volta il linker non collega quella parte, che finisce in una mappa vuota. Un ulteriore problema è che MSVC e GCC differiscono nel modo di forzare l'esportazione del simbolo. Anche io finalmente felice, il codice sarebbe complicato. Quindi ho rinunciato. –

7

Risposta breve per Android NDK funziona, qualsiasi libreria statica interessata da questo problema deve essere aggiunta alla variabile LOCAL_WHOLE_STATIC_LIBRARIES - verranno quindi referenziati utilizzando il flag -Wl,--whole-archive e non saranno soggetti a stripping.

Più rispondere per MSVC:

variabili statiche in un'unità di traduzione vengono inizializzati prima di qualsiasi codice di regolare l'unità di traduzione esegue. In pratica l'inizializzazione avviene quando viene caricato il file eseguibile contenente o la libreria dinamica. Quando si chiama \ c main() o la chiamata a LoadLibrary()/dlopen() completa, tutte le variabili statiche saranno state inizializzate.

Il problema, come descritto da MSDN:

Costruttori e assegnazione per funzione globale o metodi statici nella dichiarazione do non creare un riferimento e non impedirà/OPT: eliminazione REF. Gli effetti collaterali di tale codice non dovrebbero essere dipendenti quando non esistono altri riferimenti ai dati.

Può essere conveniente inserire il codice oggetto da più unità di traduzione in un unico file , una libreria statica convenzionalmente denominati con un c Lib \ o \ c .a suffisso. Il linker MSVC effettua l'analisi di dipendenza su librerie statiche e non include il codice non referenziato dall'entità inclusa.

Lo schema comune di utilizzare una variabile statica per dichiarare e causare la registrazione di un oggetto fabbrica può fallire in questa circostanza - linker MSVC ritenga la statica come essere irraggiungibile e strisce dal risultato.

Solutions

Una ricerca su Google: utile http://www.google.com/search?q=msvc+factory+static+library

Una soluzione è quella di impostare il flag /OPT:NOREF linker sull'entità compreso. Tuttavia, questa è un'impostazione tutto o niente e richiede che tutte le librerie incluse siano completamente collegabili.

Se qualcosa nel file che contiene la statica viene referenziato (direttamente o indirettamente) da all'entità inclusa, quindi dalle regole del linguaggio deve essere conservata la statica stessa.

L'approccio più semplice consiste nel mettere una funzione fittizia nel file e fare riferimento a quella da da qualche parte nota per essere considerata raggiungibile.

Un altro approccio consiste nell'utilizzare il flag del linker /INCLUDE per fare riferimento a un'entità nel file del problema. Ipotizzando un'entità denominata DummyForLinkProblem, questo può essere fatto in origine dell'entità tra cui:

#pragma comment(linker, "/include:DummyForLinkProblem") 

ZooLib's Solution

ZooLib entità attualmente interessate da questo problema sono quelli in ZFile_Win.cpp, ZGRgnRep_HRGN. cpp, ZNet_Internet_WinSock.cpp, ZStreamRWCon_SSL_Win.cpp, ZTextCoder_Win.cpp e ZUnicode_Normalize_Win.cpp.

Abbiamo #include ZCompat_MSVCStaticLib.h nei file di intestazione corrispondenti, e mettere in ciascuno un ZMACRO_MSVCStaticLib_Reference(ModifiedFileName). Nei file cpp abbiamo inserito uno ZMACRO_MSVCStaticLib_cpp(ModifiedFileName). Il ModifiedFileName è generalmente il nome file con la Z iniziale e l'estensione del file rimosse, lo stesso stile di utilizzato nei macro ZCONFIG_API_XXX.

Per garantire che il file eseguibile o la libreria non rimuova queste entità, #include semplicemente il file di intestazione appropriato dal codice di riferimento noto nell'entità inclusa. Questo sarà perché si verifichi un riferimento non in esecuzione e le cose funzioneranno come previsto.

Problemi correlati