Liest eine Datei zur Kompilierzeit in einen String ein [duplicate]

Lesezeit: 8 Minuten

Benutzer-Avatar
1Ass

Ich möchte etwas in eine Datei schreiben (nennen wir es foo.cpp) und füge es als String in mein Programm ein zur Kompilierzeitähnlich wie der Weg #include macht es.

Im Moment verwende ich diesen C-Präprozessor #define:

#define toString(src) #src

Um eine Menge Code in eine Zeichenfolge umzuwandeln, wie in diesem Beispiel verwendet:

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

Sie können über Makro-Stringifizierung lesen dort falls Sie es wollen.

Ich möchte diesen Code in eine externe Datei verschieben, die zur Kompilierzeit “verknüpft” wird.
Ich möchte nicht, dass die Datei mit dem Programm verteilt werden muss, was der Fall wäre, wenn ich sie zur Laufzeit lesen würde.

Ich habe versucht, eine zu verwenden #include Direktive wie unten gezeigt, aber der Compiler hat sie abgelehnt:

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

g++ scheint völlig verwirrt zu sein, aber clang++ gab mir diesen Fehler:

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

Weiß jemand ob/wie das geht?

Hinweis: Ich verwende dies, um meine GLSL-Shader zu schreiben, obwohl ich bezweifle, dass diese Informationen von Nutzen sind.

PS: Bevor Sie mir sagen, dass dies ein Duplikat dieser Frage ist, fügen Sie meinen Code in eine riesige Zeichenfolge in einer eigenen Datei ein oder verwenden Sie ein externes Tool (z. xxd), um seine Hex-Darstellung auszugeben, sind für mich keine “Lösungen”, da sie nicht besser (dh einfacher/sauberer) sind als meine aktuelle Methode.


Update einige Jahre später:
Mir wurde gerade klar, dass ich diese Frage nie beantworten musste, da sie als Duplikat geschlossen wurde. Ich fand die Antwort, nach der ich suchte, als ich sah dieses Bekenntnisselbst basierend auf ein Kommentar zu diesem Artikelund benutze es seitdem.

Kurz gesagt, eine kleine Assembly-Datei enthält die gewünschten Dateien und stellt sie unter einer bestimmten Datei bereit NAMEmit den drei Variablen NAME_begin, NAME_end und NAME_len So können Sie von Ihrem C-Code aus auf deren Inhalt zugreifen.

Auf diese Weise haben Sie eine normale Datei, die nur den gewünschten Code enthält, und sie wird zur Kompilierzeit automatisch gelesen, anstatt sie zur Laufzeit lesen oder durchspringen zu müssen xxd Reifen.

  • Es ist ein Duplikat der verknüpften Frage. Sie mögen einfach keine der bereitgestellten Antworten. Aber die Antwort ist, dass Sie es nicht so machen können, wie Sie es wollen, also werden Sie nur eine Reihe von Antworten erhalten, die die Antworten der verknüpften Frage widerspiegeln.

    – Rici

    28. Oktober 2013 um 15:24 Uhr

  • Für welche Plattform versuchen Sie zu kompilieren … oder versuchen Sie, es plattformübergreifend zu machen?

    – Zac Howland

    28. Oktober 2013 um 15:25 Uhr

  • Ich versuche zu ergründen, wie die Begriffe “besser”, “einfacher” und “sauberer” zutreffen Dies Methode, geschweige denn jede andere. Nur zum Verständnis möchten Sie einen Variablensatz von Zeichenfolgen auf Quellebene vorverarbeiten, die dann mit noch nicht sichtbarem Code in eine Datei geschrieben werden, und später diese Datei aus der Quelle kompilieren, und das Problem, das Sie haben, ist Präprozessoranweisungen in die “Quelle” von Schritt 1 einbetten?

    – WhozCraig

    28. Oktober 2013 um 15:28 Uhr


  • @MarkRansom: Eigentlich ist es in C++: raw-string: " d-char-sequenceopt( r-char-sequenceopt) d-char-sequenceopt" (Die lexikalische Grammatik erfordert, dass dieser Sequenz unmittelbar ein großes R vorangestellt wird.)

    – Rici

    28. Oktober 2013 um 15:45 Uhr


  • Ich stimme dafür, dies erneut zu öffnen, da es eine C++-Antwort hat, die nicht für das C-only “Duplikat” geeignet wäre. @rici, warum nicht eine Antwort geben und das OP entscheiden lassen, ob es besser ist, da der Makro-Hack nicht das tut, was er will?

    – Markieren Sie Lösegeld

    28. Oktober 2013 um 16:28 Uhr

Benutzer-Avatar
Sergej L.

Ich bin mir nicht ganz sicher, was Sie erreichen wollen, aber das Linux-Befehlszeilenprogramm xxd vielleicht ist das was du suchst:

xxd -i [filename]

generiert eine Header-Datei im C-Stil, die ein Array mit Ihrem Dateiinhalt in vollständiger Binärcodierung und eine Variable mit ihrer Länge enthält.

Beispiel:

xxd -i /proc/cpuinfo

macht eine Datei mit

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;

Sie können den resultierenden Header in Ihren Code einfügen und über diese Variablen auf das Array und die Dateilänge zugreifen.

  • Wie ich in der Frage sagte, ist diese Lösung weder einfacher noch sauberer (was subjektiv ist) als meine aktuelle Methode, aber danke für das Kopieren/Einfügen 🙂

    – 1Ass

    28. Oktober 2013 um 15:34 Uhr

Benutzer-Avatar
Mark Lösegeld

Du sagst, du magst es nicht xxd weil es die Datei unlesbar macht. Fair genug. Es wäre einfach, ein eigenes Dienstprogramm zu schreiben, das die Daten in einem anderen Format codiert, da Sie speziell Zeichenfolgen als Ein- und Ausgabe benötigen. Sie können die String-Literal-Verkettung nutzen, um dies leicht lesbar zu machen.

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

foo.h:

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

Ich habe kurz versucht, C++ 11 zu verwenden rohe Zeichenfolgenliterale, was die Verwendung der Datei ohne Neuformatierung ermöglicht hätte, aber es hat nicht funktioniert. Das #include wird als Teil der Zeichenfolge betrachtet, anstatt die Datei wie gewünscht einzuschließen.

  • Könnte auch die verwenden toString Makro in foo.h

    – Jimmy T.

    31. Juli um 8:36 Uhr

Benutzer-Avatar
James Kanze

Am einfachsten ist es in solchen Fällen, einen kleinen Präprozessor zu schreiben, der Ihre Datei liest und ausgibt, der jede Zeile in Anführungszeichen setzt. Ich würde das wahrscheinlich in Python machen, aber es ist auch in C++ ziemlich einfach. Angenommen, Sie haben inputFile, outputFile und variableName von irgendwoher (evtl argvaber vielleicht möchten Sie die letzten beiden aus dem Eingabedateinamen ableiten:

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;
}

Je nachdem, was in den Dateien enthalten ist, müssen Sie möglicherweise die line vor der Ausgabe, um Dinge wie zu entkommen " oder \.

Wenn Sie Lust haben, könnten Sie einige Tests hinzufügen, um das Semikolon in die letzte umbrochene Zeile einzufügen, anstatt in eine eigene Zeile, aber das ist nicht wirklich notwendig.

Dies führt zu einem '\0' abgeschlossene Zeichenfolge. Angesichts der wahrscheinlichen Länge der Zeichenfolgen ist es möglicherweise vorzuziehen, eine zweite Variable mit der Länge hinzuzufügen:

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

(Vergessen Sie nicht die -1, da Sie die Terminierung nicht mitzählen möchten '\0' die der Compiler zu Ihrem String-Literal hinzufügt.) Wenn Sie die generierte Datei dort einfügen, wo sie verwendet wird, brauchen Sie dies nicht unbedingt, da std::begin und
std::end liefert die notwendigen Informationen (aber vergessen Sie auch hier nicht, zu verwenden std::end( variableName ) - 1zu ignorieren '\n').

Wenn Sie verwenden makeist es ziemlich einfach, Ihre generierte Datei von der zu umschließenden Datei abhängig zu machen, und die ausführbare Datei, die das Wrapping durchführt (was wiederum von der obigen Quelle usw. abhängt). Mit Visual Studios müssen Sie ein separates Projekt für den Wrapping-Code erstellen, wenn Sie ihn in C++ schreiben (einer der Gründe, warum ich dafür Python verwenden würde), und Sie werden wahrscheinlich einige Probleme mit der Abhängigkeitsverwaltung haben. Visual Studios ist nicht wirklich für professionelle Arbeit ausgelegt (wo regelmäßig große Codeblöcke mit solchen Techniken generiert werden).

  • Wenn Sie VS verwenden, können Sie einfach die Textdatei als Ressource einfügen und mit den LoadResource-Funktionen darauf zugreifen. obwohl das Windows-spezifisch ist.

    – Zac Howland

    28. Oktober 2013 um 15:56 Uhr

  • @ZacHowland Ja. Ich weiß, dass Windows hier ein paar Optionen hat. Leider nicht sehr portabel.

    – James Kanze

    28. Oktober 2013 um 15:57 Uhr

  • Leider. Mein Kommentar zielte eher auf die Vorstellung ab, dass Visual Studio nicht wirklich für professionelles Arbeiten ausgelegt ist. Es ist … aber es konzentriert sich auf Windows-spezifische professionelle Arbeit (vielleicht kann sich das jedoch mit ihrem neu entdeckten Vorstoß in die Open-Source-Arena ändern).

    – Zac Howland

    28. Oktober 2013 um 16:01 Uhr

  • @ZacHowland Dies ist nicht der einzige Fall, in dem Sie maschinell generierten Code wünschen. Jedes professionelle Projekt, an dem ich gearbeitet habe, hat aus verschiedenen Gründen erhebliche Mengen an maschinell generiertem Code verwendet. Und Visual Studios macht es besonders schwierig, wenn die Programme, die den Code generieren, ebenfalls in C oder C++ geschrieben sind.

    – James Kanze

    28. Oktober 2013 um 18:33 Uhr

  • Ich hatte nie ein Problem damit (außer wenn Leute den generierten Code in die Quellcodeverwaltung einchecken, aber das ist weder ein Problem mit VS noch spezifisch dafür), aber andererseits kann ich mich an kein Projekt erinnern, das ich ‘ Wir waren dort, wo wir C/C++ verwendet haben, um die Codegenerierung für C/C++ zu schreiben. Typischerweise haben die Projekte, an denen ich gearbeitet habe, immer eine Skriptsprache für die Codegenerierung verwendet.

    – Zac Howland

    28. Oktober 2013 um 20:53 Uhr


1271380cookie-checkLiest eine Datei zur Kompilierzeit in einen String ein [duplicate]

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy