2013-10-28 12 views
14

Vorrei scrivere qualcosa in un file (chiamiamolo foo.cpp) e comprendono come una stringa nel mio programma al momento della compilazione, simile al modo #include lo faLeggere un file in una stringa al momento della compilazione

In questo momento sto usando questo C preprocessore #define:

#define toString(src) #src 

per convertire un mucchio di codice in una stringa, usato come in questo esempio:

const char* str = toString(
    int x; 
    void main(){} 
); 

Si può leggere su macro stringificazione there se vuoi.

Mi piacerebbe spostare quel codice su un file esterno, che sarebbe "collegato" in fase di compilazione. Non voglio che il file debba essere distribuito con il programma, il che sarebbe il caso se dovessi leggerlo in fase di esecuzione.

Ho cercato di usare una direttiva #include come mostrato di seguito, ma il compilatore rifiutato esso:

const char* str = toString(
#include "foo.cpp" 
); 

g++ sembra essere completamente confuso, ma clang++ mi ha dato questo errore:

error: embedding a #include directive within macro arguments is not supported 

Qualcuno sa se/come questo può essere fatto?

Nota: lo sto utilizzando per scrivere i miei shader GLSL, anche se dubito che questa informazione sia di qualche utilità.

PS: Prima mi dici che questo è un duplicato di this question, mettendo il mio codice in una stringa gigante in un file o utilizzando uno strumento esterno (es. xxd) per scaricare il suo rappresentazione esadecimale non sono "soluzioni" per me, poiché non sono migliori (cioè più facili/più puliti) del mio attuale metodo.


Update, qualche anno più tardi:
Ho appena realizzato che non ho mai avuto modo di rispondere a questa domanda perché era chiuso come duplicato. Ho trovato la risposta che stavo cercando quando ho visto this commit, a sua volta basato su a comment on this article, e lo sto usando da allora.

In poche parole, un file di assieme piccola include i file che vuoi e li espone in un dato NAME, con le tre variabili NAME_begin, NAME_end e NAME_len che consente di accedere ai propri contenuti dal codice C.

In questo modo, si dispone di un file normale che non contiene nient'altro che il codice che si desidera e viene letto automaticamente in fase di compilazione, anziché doverlo leggere in fase di esecuzione o saltare attraverso i cerchi xxd.

+4

È un duplicato della domanda collegata. Semplicemente non ti piacciono le risposte fornite. Ma la risposta è che non puoi farlo nel modo in cui vuoi farlo, quindi avrai solo un mucchio di risposte che rispecchiano le risposte della domanda collegata. – rici

+0

Che piattaforma stai tentando di compilare per ... o stai tentando di renderlo multipiattaforma? –

+0

Sto cercando di capire come i termini "migliore", "più facile" e "più pulito" si applicano al * questo * metodo, molto meno a qualsiasi altro. Solo per la comprensione, si desidera eseguire il preprocesso di un set di stringhe a livello di origine delle stringhe che vengono poi scritte su un file utilizzando un codice non ancora visualizzato, quindi compilare successivamente detto file dall'origine e il problema che si sta verificando è incorporare le istruzioni del preprocessore nella "fonte" del passaggio 1? – WhozCraig

risposta

10

io non sono abbastanza sicuro di quello che si sta cercando di realizzare, ma ma Linux utility a riga di comando xxd potrebbe essere quello che stai cercando:

xxd -i [filename] 

genererà un file di intestazione stile C che contiene una matrice con il file contenuto nella codifica binaria completa e una variabile con la sua lunghezza.

Esempio:

xxd -i /proc/cpuinfo 

fa un file con

unsigned char _proc_cpuinfo[] = { 
    0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x6f, 0x72, 0x09, 0x3a, 0x20, 
    0x30, 0x0a, 0x76, 0x65, 0x6e, 0x64, 0x6f, 0x72, 0x5f, 0x69, 0x64, 0x09, 
... 
}; 
unsigned int _proc_cpuinfo_len = 654390; 

È possibile includere l'intestazione risultante nel codice e accedere alla matrice e file di lunghezza tramite queste variabili.

+3

Come ho detto nella domanda, questa soluzione non è né più semplice né più pulita * (che è soggettiva) * del mio metodo attuale, ma grazie per la copia/incolla :) – 1ace

7

Dici che non ti piace xxd perché lascia il file illeggibile. Giusto. Sarebbe semplice scrivere la propria utility che codifica i dati in un formato diverso, dato che in particolare si vogliono stringhe come input e output. È possibile sfruttare la concatenazione letterale stringa per renderlo facilmente leggibile.

const char* str = 
#include "foo.h" 
    ; 

foo.h:

" int x;" "\n" 
" void main(){}" "\n" 

Ho provato brevemente utilizzando C++ 11 di raw string literals, che avrebbe consentito utilizzando il file senza riformattazione, ma non ha funzionato. Lo #include è considerato parte della stringa anziché includere il file come desiderato.

1

La cosa più semplice in questo tipo di caso è scrivere un piccolo preprocessore , che legge il file e lo stampa avvolgendo ogni riga tra virgolette. Probabilmente lo farei in Python, ma è piuttosto semplice in C++. Supponendo che hai ottenuto inputFile, outputFile e variableName da qualche parte (probabilmente argv, ma si potrebbe desiderare di ricavare il ultimi due dal nome del file di input:

void 
wrapFile(std::istream& inputFile, 
      std::ostream& outputFile, 
      std::string const& variableName) 
{ 
    outputFile << "extern char const " << variableName << "[] =\n"; 
    std::string line; 
    while (std::getline(inputFile, line)) { 
     outputFile << " \"" << line << "\\n\"\n"; 
    } 
    std::outputFile << ";" << std::endl; 
} 

A seconda di ciò che è nel file che si' Re compreso, si potrebbe avere manipolare i line prima di output che, per sfuggire le cose come " o \.

Se si voleva ottenere l'immaginazione, si potrebbe aggiungere alcuni test per inserire il punto e virgola sull'ultima riga incartata, piuttosto che una riga di proprio, ma non è proprio necessario.

Ciò comporterà una stringa terminata '\0'. Data la probabile lunghezza delle stringhe, potrebbe essere preferibile aggiungere una seconda variabile con la lunghezza:

std::outputFile << "extern int const " 
       << variableName 
       << "_len = sizeof(" << variableName << ") - 1;\n"; 

(Non dimenticare il -1, dal momento che non si vuole contare il di terminazione '\0' che il compilatore aggiungerà alla stringa letterale.) Se siete tra cui il file generato dove sarà essere utilizzato, non è necessariamente bisogno di questo, dal momento che std::begin e std::end fornirà le informazioni necessarie (ma ancora una volta, non dimenticare di usare std::end(variableName) - 1, di ignorare il '\n').

Se stai usando make, è abbastanza facile per rendere il vostro generato del file dipendono dal file che viene avvolto, e l'eseguibile che fa l'involucro (che a sua volta dipende dalla sorgente sopra, etc.). Con Visual Studios, devi creare un progetto separato per il codice di wrapping, se lo scrivi in ​​C++ (uno dei motivi per cui dovrei usare Python per questo), e probabilmente avrai problemi con lo gestione delle dipendenze; Visual Studios non è realmente progettato per il lavoro professionale (dove i grandi blocchi di codice vengono generati regolarmente utilizzando tali tecniche).

+0

Quando si utilizza VS, è sufficiente inserire il file di testo come una risorsa e accedervi utilizzando le funzioni LoadResource; anche se questo è specifico per Windows. –

+0

@ZacHowland Sì. So che Windows ha un paio di opzioni qui. Purtroppo, non molto portatile. –

+0

Purtroppo. Il mio commento è stato più diretto alla nozione che Visual Studio non è realmente progettato per il lavoro professionale. È ... ma è incentrato su un lavoro professionale specifico di Windows (forse questo potrebbe cambiare con il loro nuovo ritrovato nell'arena open-source, però). –

Problemi correlati