Forum: PC-Programmierung Externen Dateinhalt als String beim Kompilieren importieren


Announcement: there is an English version of this forum on EmbDev.net. Posts you create there will be displayed on Mikrocontroller.net and EmbDev.net.
von Curby23523 N. (nils_h494)


Bewertung
0 lesenswert
nicht lesenswert
Guten morgen,

ich habe mehrere .json Dateien, welche Json Schemas enthalten. Den 
Inhalt dieser Dateien möchte ich zum Zeitpunkt des Kompilierens in 
meinen Quellcode inkludieren und als einem String zur Verfügung stellen.

Ich möchte die .json Dateien hierfür in ihrer Formatierung nicht ändern.

Die Sprache ist C++ und ich nutze CMake. Früher - bei kleineren Dateien 
- habe ich die einzufügenden Dateien umformatiert und per #include 
eingefügt, das da so aus:
dbData.exec (
#include "sql/SQLCreateData.sql"
);

Somit war die .sql keine gültige SQL Datei mehr, sondern voll mit 
Formatierungszeichen:
"CREATE TABLE \"config\" ("
"  \"id\"  INTEGER NOT NULL DEFAULT 0 PRIMARY KEY AUTOINCREMENT"
");"

Gibt es dazu irgendeine Möglichkeit?

von Yalu X. (yalu) (Moderator)


Bewertung
2 lesenswert
nicht lesenswert
Möglichkeit 1:

Verwendung von "raw string literals"

  https://en.cppreference.com/w/cpp/language/string_literal

Dazu muss die JSON-Datei zwar immer noch modifiziert werden, aber nur
noch am Anfang und am Ende, bspw. mit folgendem Shell-Kommando:

echo "R\"---($(cat test.json))---\"" > test.rstring

Du musst dazu aber einen Delimiter finden, der im JSON-Text garantiert
nicht vorkommt.


Möglichkeit 2:

Verwendung eines Tools (z.B. xxd -i), dass den JSON-Text in eine Folge
von Hexzahlen umwandelt, die als Initialisierer für ein char-Array oder
einen std::string genutzt werden können. Damit gibt es keinerlei
Einschränkungen bzgl. des Dateiinhalts.


In beiden Fällen muss das Ganze noch in CMkeLists.txt integriert werden.


Es gab einmal eine Diskussion zur Einführung von std::embed in c++20,
was wohl auch (zumindest testweise) in GCC implementiert wurde. Damit
kann man beliebige Dateien zur Compilezeit in ein std::span<const char>
einlesen. Irgendwie scheint die Sache aber wieder gestorben zu sein,
zumindest kann ich keine aktuellen Informationen darüber finden.

von I <3 Makefiles (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Da bereits eine Variante von make im Einsatz ist:
weitere Regeln definieren, welche die Originaldatei in ein temporäres 
Zwischenartifakt umarbeiten, ähnlich wie das mit .o Dateien geschieht.

Dazu sich ein weiteres Dateinamenssuffix ausdenken oder (besser: und!) 
die Zwischenergebnisse in einem separaten, temporären Verzeichnis 
halten.

Einfache "umformatierungen" mit tr sed awk & co machen, warum nicht 
mit perl python
Aufwändigere umarbeitungen mit spezifischen eigenen Programme/Scripts.

Passende clean Regeln nicht vergessen, damit die Zwischenartifakte 
auch entsorgt werden.

Hat die Zielsprache kein include Mechanismus: mehrere Teildateien 
(Vorspann, Mitte, Abspann) anlegen und mit cat copy in einer eigenen 
Make-Regel zusammenfügen lassen.

von Tom K. (ez81)


Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Yalu X. schrieb:
> In beiden Fällen muss das Ganze noch in CMkeLists.txt integriert werden.

Z.B. wie im Anhang.
Hier wird aus den json-Files eine Library mit cpp/h erzeugt, die den 
Inhalt der json als string enthalten.

(+) Reines cmake ohne externe Scripte o.ä., allerdings nur mit make/gcc 
getestet.
(+) Die JSON-Files können unverändert bleiben
(+) Die Source-Files werden bei Änderungen der Json-Vorlagen neu 
generiert.
(-) Cmake muss bei hinzukommenden/entfallenden JSONs neu ausgeführt 
werden. Das GLOB sollte durch manuelles Auflisten ersetzt werden.
(-) Hässlich. CMake halt.
(-) Die Dateinamen der JSONs dürfen nur für Variablen legale Zeichen 
enthalten.

: Bearbeitet durch User
von guest (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Oder mittels objcopy in .o Dateien verwandeln und einfach dazu linken:
https://www.linuxjournal.com/content/embedding-file-executable-aka-hello-world-version-5967

von Curby23523 N. (nils_h494)


Bewertung
0 lesenswert
nicht lesenswert
Tom K. schrieb:
> Hier wird aus den json-Files eine Library mit cpp/h erzeugt, die den
> Inhalt der json als string enthalten.

Prima danke. Nach etwas reinfummeln funktioniert das zumindest im 
Ansatz.

von Niklas G. (erlkoenig) Benutzerseite


Bewertung
0 lesenswert
nicht lesenswert
guest schrieb:
> Oder mittels objcopy in .o Dateien verwandeln und einfach dazu linken:

Das lässt sich noch etwas verbessern:

1. Man konvertiert die Datei erst mit "ld". Der Witz daran ist, dass 
"ld" automatisch die richtige Architektur auswählt, während man die bei 
"objcopy" noch mit "-B" umständlich angeben muss, was für portable 
Makefiles problematisch ist:
ld -r -b binary -o blob.pre.o blob.txt

2. Man verarbeitet die Datei mit "objcopy", um die Section-Einstellungen 
zu ändern:
objcopy --set-section-alignment" ".data=4" "--set-section-flags" ".data=alloc,load,readonly,data" "--rename-section" ".data=.rodata.myblob" "blob.pre.o" "blob.o"

Die seit Binutils 2.33 verfügbare Option "--set-section-aligment" (von 
mir implementiert :) ) setzt die Ausrichtung des Datenblocks im 
Speicher. Das ist insbesondere wichtig wenn man die Daten auf µC danach 
per DMA o.ä. verarbeiten möchte. Die gezeigte "--set-section-flags" 
Option sorgt dafür dass der Datenblock im schreibgeschützten 
Speicherbereich landet (also typischerweise Flash). Mit 
"--rename-section" bekommt die Section noch einen sprechenderen Namen.

3. Man linkt die erzeugte "blob.o" dazu, und nutzt dann wie im o.g. Link 
gezeigt im C-Code die Symbole "_binary_blob_txt_start" / 
"_binary_blob_txt_end" / "_binary_blob_txt_size". Man kann da noch einen 
Wrapper für bauen:
#include <string_view>

#define OS_BINSECT2(start,end)    (std::string_view { []() -> char* { extern char start; return &start; }(), []() -> std::size_t { extern char start, end; return &end - &start; }()})

#define OS_BINSECT(name)      OS_BINSECT2(_binary_ ## name ## _start, _binary_ ## name ## _end)

Wenn man dann im Code irgendwo "OS_BINSECT(blob_txt)" schreibt wird dies 
automatisch zu einem std::string_view, welcher die gewünschten 
Binärdaten enthält.

Der Vorteil dieses Vorgehens ist dass so beliebig (große) binäre 
Datenblöcke schnell eingebunden werden können, egal was die für 
(Sonder)zeichen enthalten, ohne C(++)-Code generieren zu müssen und die 
Daten escapen zu müssen. Indem man das richtige ld & objcopy benutzt 
(also z.B. arm-none-eabi-ld/objcopy) ist die ELF-Datei automatisch 
richtig für die gewünschte Plattform.

Der Nachteil ist dass man eben aktuelle binutils braucht und das im 
Makefile etwas umständlich ist.

Beim Erzeugen von C(++) Code muss man auch auf Trigraphs acht geben und 
das Übersetzen großer solcher Datenblöcke dauert je nach Darstellung 
recht lange. Daher bevorzuge ich die ld+objcopy Variante.

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.