Um spezielle Byte-Konstanten (Werte im Bereich von 0-255) wirklich platzsparend innerhalb konstanter (char)Strings zu speichern, suche ich nach einer Möglichkeit, eine konstante Zahl (0-255) als Binärwert direkt in einen string zu schreiben. Grundgedanke: const char * teststring= "mystring \x41" (0x41 wäre die konstante - würde hier als A abgespeichert werden) Die Herausforderung besteht darin, solche Konstanten vorher zu definieren, sodaß ich nicht \x41 angeben muß, sondern reale Zahlenwerte nutzen kann. Hier ein Beispiel: Ich erreiche bereits, daß die Konstante 0x42 als Binärwert in einen Char-String gelegt wird (wird im String als 'B' angezeigt) #define MY_CONST \x42 #define AS_BINSTRING(a) CONST_2_STRING(a) #define CONST_2_STRING(a) #a const char * teststring = "0x42 is stored as " CONST_2_STRING(a) " END"; im Flash (für AVRs müßte man den Bezeichner "PROGMEM" angeben), würde dann stehen "0x42 is stored as B END" ----Problem / Herausforderung---- Wie bekomme ich es hin, mir die Konstante als wirklichen Zahlenwert zu definieren und trotzdem binär abzuspeichern? also MY_CONST mit 0x42 zu initialisieren statt \x42 ? die gefundenen Infos und Beispiele zu GCC string concatenation haben mir hier leider nicht weitergeholfen, da es sich bei \x42 um eine escape-Sequenz handelt. Weiß jemand eine Möglichkeit, wie ich dies trotzdem erreichen kann? Ich möchte auf "Laufzeit-Umwandlung" verzichten, die Umwandlung sollte vom Compiler erledigt werden. ----Hintergrund------ Mein Ziel ist es, eine sehr Platz- und ressourcensparende Alternative zum Parsen mit "sscanf" schreiben, welche auch auf kleineren AVRs mit minimalem RAM und Flash-Bedarf eingesetzt werden kann. ----Danke Vorab---- Es ist Weihnachten, daher möchte ich mich bei ALLEN die willens sind, sich mit dieser geistigen Herausforderung beschäftigen bereits vorab herzlich danken und würde mich für hilfreiche Kommentare auch gern erkenntlich zeigen.
Äh... was ist das Problem? Brauchst du sowas?
1 | #define MY_CONST 0x42
|
2 | #define CONST_2_STRING(a) "a"
|
3 | |
4 | int main () |
5 | {
|
6 | const char * teststring = "0x42 is stored as " CONST_2_STRING(a) " END"; |
7 | printf("%s",teststring); |
8 | |
9 | return 0; |
10 | }
|
> Ich möchte auf "Laufzeit-Umwandlung" verzichten, die Umwandlung > sollte vom Compiler erledigt werden. Irgendwie versteh ich dein Problem nicht. In "mystring \x41" wird keinesfalls der Text \x41 angelegt, sondern im Speicher steht tatsächlich an dieser Stelle schon ein Byte mit dem Wert 0x41. Kann es sein, dass du hier Schreibweise im C Programm (als Anweisung an den Compiler) und das, was dann tatsächlich im Speicher landet, durcheinanderwürfelst?
Karl Heinz Buchegger schrieb: >> Ich möchte auf "Laufzeit-Umwandlung" verzichten, die Umwandlung >> sollte vom Compiler erledigt werden. > > Irgendwie versteh ich dein Problem nicht. Ich verstehe nicht, wozu er das braucht. > In > "mystring \x41" > > wird keinesfalls der Text \x41 angelegt, sondern im Speicher steht > tatsächlich an dieser Stelle schon ein Byte mit dem Wert 0x41. Er möchte 0x41 in seinem Quellcode stehen haben statt "\x41", und er sucht eine Möglichkeit, dieses 0x41 dann in seinen String reinzubekommen.
Genau so ist es- im Flash soll direkt eine "0x41" stehen - also ein einzelnes byte (innerhalb eines strings). Genau dies meinte ich mit "im text wird dann ein "B" angezeigt - d.h. im Flash steht das Zeichen 0x41 = 65 = ASCII B) Dieses "Byte" möchte ich vorher als Zahl definieren können (z.b. mit defines (1<<x> einzelne Flags setzen können usw...) es wäre mit dem beschriebenen Makro kein Problem, die Zahl als "dezimalzahl" abzuspeichern und später auszuwerten, doch mir geht es wiegesagt um eine platzsparende (binäre) Variante. Wozu ich dies brauche: Ich möchte eine sehr platzsparende und ressourcen (RAM) schonende Alternative für eigene Projekte zum Textparsing entwickeln - die sache funktioniert in etwa so, daß es im System eine definierte Liste von Variablen gibt, bereits per "formatstring" der index eines bestimmten Elements angegeben werden kann (inclusive Formatierung), sodaß der Wert dann geparsed werden kann. Vorteil zu sscanf: weniger Overhead beim Aufruf (ich übergebe meinem "parser" nur eine "anweisungsdatei" und er füllt selbstständig eine liste mit variablen, die ich vorher im System angelegt habe. Zudem muß ich beispielsweise keine Gleitkommazahlen implementieren, da ich mit Festkomma arbeite usw.. (-> platzsparende Libary, mehr Flash übrig für Programmcode :-) ) Soweit meine Idee - leider wäre sie ohne die Möglichkeit im Text mit "binären" Daten zu arbeiten ziemlich uneffizient. Ich weiß das Thema ist nicht einfach - arbeite selbst seit ca 8 Jahren im Bereich embedded systems - und habe einige Jahre AVR Erfahrung sammeln dürfen. Daher hoffe ich sehr auf Hilfe irgendeines C-Gurus bzw. einfach eines ehrgeizigen Geistes, der sich dieses Problems mal annimmt. In Assembler würde mir dies weniger Kopfzerbrechen bereiten, doch möchte ich die Strings gern effizient in "C" (C++) integrieren - und da klappen .BYTE Anweisungen halt nicht ;-) Wiegesagt, ich revanchiere mich liebend gern für die aufgewendete Zeit- ob nun mit einem kleinen Prototypen oder einem Stück Sourcecode/Schaltung für ein Problem etc.. - meine Fantasie stelle ich gern zur Verfügung. Kurzum - freue mich sehr über hilfreiche Ideen von eurer Seite. Falls es noch Fragen oder unklarheiten zum oben geschilderten Thema gibt, bitte zögert nicht - ich versuche es so gut wie möglich zu erklären. Vielen Dank im Voraus und euch allen ein paar beschaulich schöne Tage :-) Dominik
Edit: ASCII 65 ist natürlich auch bei meinem Rechner ein "A" - nur um keine Verwirrung aufkommen zu lassen ;)
Dominik schrieb: > Ich möchte eine sehr platzsparende und ressourcen (RAM) schonende > Alternative für eigene Projekte zum Textparsing entwickeln - die sache > funktioniert in etwa so, daß es im System eine definierte Liste von > Variablen gibt, bereits per "formatstring" der index eines bestimmten > Elements angegeben werden kann (inclusive Formatierung), sodaß der Wert > dann geparsed werden kann. > Vorteil zu sscanf: weniger Overhead beim Aufruf (ich übergebe meinem > "parser" nur eine "anweisungsdatei" und er füllt selbstständig eine > liste mit variablen, die ich vorher im System angelegt habe. OK. Ich denke jetzt ist mir ein bischen klarer was du erreichen möchtest. Du willst im Prinzip so was machen (Ich fantasiere mal ein wenig, wie das Endergebnis aussehen könnte ohne mich darum zu scheren, wie man das implementieren könnte)
1 | // es gibt 3 Variablen
|
2 | uint8_t Var1; |
3 | uint16_t Var2; |
4 | uint8_t Var3; |
5 | |
6 | // und eine Durchnummerierung dieser Variablen, inkl. notwendiger
|
7 | // Zusatzinfo
|
8 | struct Variables[] = { |
9 | { &Var1, ... }, // ab sofort ist Var1 die Variable Nr. 1 |
10 | { &Var3, ... }, // Var3 ist auch bekannt als Variable Nr 2 |
11 | { &Var2, ... } // und Var2 nennen wir ab sofort Variable Nr 3 |
12 | };
|
13 | |
14 | ....
|
15 | |
16 | char Input[] = "78, 12, 65"; |
17 | |
18 | parse( "#3 #1 #2", Input ); |
parse soll jetzt basierend auf dem 'Formatstring' die 78 in Variable Nummer 3 ablegen (welche Var2 ist), die 12 kommen nach Variable Nr 1 (also Var1) und die 65 sollen zur Variablen Nr 2 kommen (also nach Var3). Ist das das Prinzip, welches du erreichen möchtest? Hmm. Zur Laufzeit ist das alles kein Thema. Aber zur Compilezeit fällt mir da nichts dazu ein. Weder dazu, wie du die #n Angaben gscheit in den String reinkriegst, noch wie du da den Compiler überreden kannst. Wird wohl auf eine Laufzeitlösung rauslaufen.
Karl Heinz, ich denke mit deiner Fantasie hast Du den Nagel ziemlich auf den Kopf getroffen :) Ich dachte mir, es kann doch nicht so schwer sein, dieses \x41 mit "irgendeinem" Makro, Stringification oder zusammenführung irgendwie dahin zu überführen - in ASM geht es direkt per define/enum/equation Hoffe sehr, daß irgendeiner, der es versteht besser als ich mit Makros zu spielen als ich, hier zufällig mal einen Blick drauf wirft und sich sagt, dies wäre 5 Minuten nachdenken wert... :) Denn mit der Effizienz stirbt oder lebt ein solcher Parser - hier wollte ich die Infos gern Binär verpacken - ginge umständlich mit einem "Skriptumwandler" ( der mir dann einen \x01 \x02 string erzeugt), aber wäre doch um so vieles schöner, wenn es direkt ginge.. Nunja - ich hoffe mal meine Weihnachtswünsche werden noch irgendwie erhört. Trotzdem möchte ich Dir sehr danken - du hast meine langen Ausführung wirklich bildhaft auf den Punkt gebracht. Dir - und euch allen noch einen schönen zweiten Weihnachtsfeiertag Viele Grüße, Dominik
Dominik schrieb: > Ich dachte mir, es kann doch nicht so schwer sein, dieses \x41 mit > "irgendeinem" Makro, Stringification oder zusammenführung irgendwie > dahin zu überführen - in ASM geht es direkt per define/enum/equation Der C Präprozessor hat schlicht und einfach keine Stringverarbeitung integriert. Der scheidet somit aus, wenn es darum geht, dem 0x41 sein 0 wegzuschnibbeln. Was vielleicht funktionieren könnte ist, vor 41 einmal 0x und einmal \x zu hängen. Es gibt natürlich andere Möglichkeiten, beispielsweise mit awk/perl/m4/... den Quelltext umzuwandeln.
Anstatt die Zahl in den String zu bauen, bau doch einfach den String/Char als konstande in den struct, z.B. 'B' anstatt 0x42 im Initializer der Struktur.
An sich wäre dies eine sehr gute Idee - doch leider sollte diese Info ja mit in den string (und dort eben möglichst wenig Platz wegnehmen) Zumindest würde auf diese Weise die Verwendung z.B. eines Enums klappen - doch leider kann ich einen char/byte-string schlecht in eine Struktur kapseln, sodaß an den richtigen Stellen meine "Parse-Infos" stehen.. z.b. "Beispieltext 40 von 0x420 " geparsed mit "Beispieltext \x80\x01 von \x83\x02" 0x80 könnte bedeuten dez. uint8 0x81 = hex uint16 x01/x02 wäre ein Variablenindex in einer liste.. alle zeichen im parsestring die das 0x80-bit gesetzt haben, leiten einen Parsevorgang ein... so einfach könnte der Parsetext aussehen - ein einfacher aufruf würde reichen: Parse(my_const_char_beispieltext_parsestring); ==> sehr geringer overhead => für kleine Applikationen viel geeigneter als das (oft zu) mächtige sscanf den obigen beispieltext würde man idealerweise etwa in dieser form definieren können: static const char * my_const_char_beispieltext_parsestring= "Beispieltext " PARSEVALUE(FLAG_U8 | FLAG_DEZ, ENUM_VARLIST1) "von "PARSEVALUE(FLAG_U16 | FLAG_HEX, ENUM_VARLIST2)" soweit die Idee.. Wer kennt jemanden der mir helfen kann? :-) Mag keinen C-Code-Generator basteln müssen, da soetwas immer recht fehleranfällig und auf jeden fall umständlicher in ein Projekt-Makefile einzubinden ist (gerade in punkto dependencies, aktualisierung) gibt es evtl von anderen ähnliche Bestrebungen in die richtung? Vielleicht könnte man sich ja zusammentun..
Johann L. schrieb: > Anstatt die Zahl in den String zu bauen, bau doch einfach den > String/Char als konstande in den struct, z.B. 'B' anstatt 0x42 im > Initializer der Struktur. Dies geht leider nicht/schlecht, da 0x42 ja Ergebnis einer Berechnung sein soll (z.b. mehrere Flags gesetzt - raus kommt 0x42) Im Code wäre es wesentlich besser lesbar etwas in der Art zu schreiben: const char* tmp="start parsetext " PARSEHEX_u8_var1 " end parsetext"; statt z.B. "start parsetext \x81 end parsetext"; aber vermutlich gibts wirklich keine Variante.. ich habe es nicht hinbekommen, das \x vor eine Zahl "anzuhängen", sodaß sie korrekt ausgewertet wird
Dominik schrieb: > Johann L. schrieb: >> Anstatt die Zahl in den String zu bauen, bau doch einfach den >> String/Char als konstande in den struct, z.B. 'B' anstatt 0x42 im >> Initializer der Struktur. > > Dies geht leider nicht/schlecht, da 0x42 ja Ergebnis einer Berechnung > sein soll (z.b. mehrere Flags gesetzt - raus kommt 0x42) Das ist aber ein wichtiger Hinweis. Es muß also nicht nur ein wörtlich dastehendes 0x42 als Element eines Strings eingefügt werden, sondern das Ergebnis einer Berechnung. Das macht einen Unterschied, denn wenn da jetzt nicht 0x42, sondern 0x40 | 0x2 steht, würde (wenn es überhaupt irgendwie ginge) der Präprozessor ja eher sowas wie "\x40 | 0x2" draus machen statt "\x42". > Im Code wäre es wesentlich besser lesbar etwas in der Art zu schreiben: > const char* tmp="start parsetext " PARSEHEX_u8_var1 " end parsetext"; > > statt z.B. "start parsetext \x81 end parsetext"; > > aber vermutlich gibts wirklich keine Variante.. Wenn du immer genau eine Konstante (bzw. eine fixe Anzahl hast), könnte ich dir höchstens sowas anbieten:
1 | #include <stdio.h> |
2 | |
3 | #define MAKE_PARSE_STRING(name, str1, val, str2) \
|
4 | struct \
|
5 | { \
|
6 | const char a[sizeof(str1)-1]; \
|
7 | const char b; \
|
8 | const char c[sizeof(str2)]; \
|
9 | } name ## _struct = { \
|
10 | str1 , val, str2 \
|
11 | }; \
|
12 | const char* name = (const char*)&name ## _struct
|
13 | |
14 | |
15 | #define PARSEHEX_u8_var1 0x40 | 0x2
|
16 | |
17 | MAKE_PARSE_STRING(tmp, "Hallo ", PARSEHEX_u8_var1, " Welt"); |
18 | |
19 | int main() |
20 | {
|
21 | printf("%s\n", tmp); |
22 | }
|
Ist ein bischen ein Hack, aber geht.
Rolf Magnus schrieb: > Ist ein bischen ein Hack, aber geht. Gehen tut viel, aber warum sollte man sich solche Schwurbelei antun wollen? ;-)
Mark Brandis schrieb: > Gehen tut viel, aber warum sollte man sich solche Schwurbelei antun > wollen? ;-) Wenn du eine schönere Alternative hast, nur her damit.
Rolf Magnus schrieb: > Wenn du eine schönere Alternative hast, nur her damit. Ich halte den Anwendungsfall ehrlich gesagt für etwas exotisch. Er will Stringverarbeitung machen, aber unbedingt mit minimalem Hauptspeicher und sscanf ist ihm zu ressorcenlastig? Wenn man die Arbeitsstunden in Euro bewertet, die man hier für eine Sonderlösung braucht, dann wäre es vielleicht doch einfacher und womöglich kostengünstiger einen Mikrocontroller mit ausreichend Speicher zu nehmen.
@Mark: Strings parsen ist exotisch? :) Sobald man Daten verarbeiten /senden will, kommt man heute kaum drumrum - jedes Modem, die meisten GPS/WLAN/ISM Receiver nutzen textuelle Protokolle.. Einen Mikrocontroller mit genug speicher für ein einzelnes Gerät wäre 100%ig günstiger - jedoch für eine mögliche Serie würde man sich viel LeiterplattenPlatz/Geld verschenken, wenn die dahinterstehende Anwendung äußerst flexibel an verschiedenste Anwendungssfälle anpaßbar ein muß, das "low-end" Gerät jedoch nur wenig Speicher benötigt. Hier wäre eine effiziente Programmierung die beste Lösung. Einen solchen Algorithmus könnte man in zig anderen Anwendungen ebenfalls nutzen (nicht mal nur auf AVR-Harvard-Architekturen beschränkt, sondern durchaus auch von Neumann (ARM etc..) laufen lassen. Der Aufwand wäre dann auch gerechtfertigt zumal ich ein ähnliches Konzept früher bereits realisiert hatte - hier waren die "parse-Befehle" im String allerdings textuell kodiert und daher speicheraufwändiger. SScanf ist ist sehr sehr mächtig - ich benötige einige wenige String-Parse Funktionen, kann auf sehr viele identifier verzichten, benötige aber stattdessen u.U. applikationsspezifische Erweiterungen wie z.B. Bereichsüberprüfungen usw.. - hier wären binär-Flags sehr effizient nutzbar (in 95% der Fälle ist das Flag nicht gesetzt, der string bleibt kurz und bündig). hier mal eine Gegenüberstellung: (AVR) Sscanf - Flash: 1,8KB mindestens !-- String-Overhead von min. 3-4 Bytes pro geparster Variable !-- hoher Stack(Aufruf-)Overhead wenn mehrere Variablen auf einmal geparsed werden müssen (und zwischendurch bedingungen ausgewertet werden müssen) + bereits fertig implementiert -> Zeitersparnis "meine idee" - Flash: <1KB für "einfache Parsefunktionen" !++ geringer Overhead (1-2 Bytes pro geparster variablen) !++ leicht erweiterbar (z.b. "x zeichen beim parsen überspringen / bis char X suchen", dann weiterparsen etc... !++ nahezu kein Stack Overhead, da in eine indexliste geparsed wird !++ bedingungen (wie z.b. bereichsprüfungen) können direkt in den Parsevorgang integriert werden --> eine zeile pro "komplexem string parsen" -- Entwicklungsaufwand => meine variante spart hauptsächlich Flashspeicher in konstanten strings. ==> Ersparnis wird relevant bei textuellen Befehlen, Protokollen mit vielen zu parsenden Variablen Flash ist bei AVRs gerade das Bottleneck, da nicht durch ext.Flash erweiterbar. Falls ich es nicht hinbekomme, würde ich jedoch auf eine textuelle Variante ausweichen und z.B. Parse-Strings und andere Konstanten in einen seriellen Flash speichern.. @Rolf Vielen Dank für diese gute Idee - ich werde diesen Ansatz ein wenig verfeinern, er kommt dem, was ich realisieren möchte am nähesten :)
Dominik schrieb: > @Mark: > Strings parsen ist exotisch? :) Sobald man Daten verarbeiten /senden > will, kommt man heute kaum drumrum - jedes Modem, die meisten > GPS/WLAN/ISM Receiver nutzen textuelle Protokolle.. Dann würde ich versuchen herauszubekommen, wie dieses Problem dort gelöst wird. > hier mal eine Gegenüberstellung: (AVR) Ist es zwingend vorgegeben, dass es ein AVR Mikrocontroller sein muss?
Dort wird es mit größeren Mikrocontrollern gelöst :-) unschuldig guck Zwingend vorgegeben ist der AVR natürlich nicht - nur habe ich ihn als einen der platzsparensten (code-effizientesten) controller kennengelernt. Muß allerdings zugeben, daß ich zu dieser Zeit noch ausschließlich via Assembler unterwegs war - mit C fängt man sich doch einiges an Overhead ein wenn man ein gewisses Maß an Codekompatibilität erreichen möchte. Mal eine Off-Topic Frage: Einen preisgünstigen Mikrocontroller mit internem Flash,RAM EEPROM, (+auslese-schutz), der sich zudem noch relativ einfach via SPI oder notfalls JTAG programmieren und kostenlos debuggen (debugger for free) läßt, ansonsten ähnlich flexibel wie der AVR ist - welcher Name würde Dir da einfallen? Herzlichen Dank im Voraus, Dominik
Dominik schrieb: > Mal eine Off-Topic Frage: > Einen preisgünstigen Mikrocontroller mit internem Flash,RAM EEPROM, > (+auslese-schutz), der sich zudem noch relativ einfach via SPI oder > notfalls JTAG programmieren und kostenlos debuggen (debugger for free) > läßt, ansonsten ähnlich flexibel wie der AVR ist - welcher Name würde > Dir da einfallen? Was verstehst Du unter preisgünstig? :-) Vielleicht käme so etwas wie ein NXP LPC ARM Cortex-M0 in Frage. Die gibt es je nach Ausstattung teilweise auch schon in Einzelstückzahl für unter 2 Euro, da ist ein ATTiny auch nicht mehr viel billiger (wenn meine wenigen Minuten Suche und Preisvergleich auf Mouser aussagekräftig sind). Die Tools sind zumindest bis zu einer gewissen Codegröße - ich glaube 32 KB - kostenlos. Oder mal Sourcery CodeBench Lite Edition ausprobieren, das müsste auch kostenlos sein.
Dank Dir für den Tipp, über Cortex habe ich bereits ausgiebig nachgedacht - einziger Grund sie nicht zu nehmen waren bisher die toch recht teuren tools. Da ich zum Programmieren Eclipse nutze und begonnen habe, meine Projekte aus Kompatibilitätsgründen in C / C++ zu schreiben, war ich von den GNU-Tools bisher recht angetan, da sie sich nicht hinter Profi-Lösungen verstecken müssen und nahezu alles damit programmierbar ist. Für eine C++ Programmierumgebung inc. Debugger legt man jedoch einige tausend Euro auf den Tisch, welche ich zumindest derzeit noch nicht ausgeben möchte - bei AVR ist das einfach alles Gratis (Debugging geht zwar nicht oder nur über Umwege in Eclipse, doch man gewöhnt sich schnell daran, den Sourcecode in 2 Programmen parallel zu verwenden - schreiben in Eclipse, Debuggen im Studio) Falls es eine vergleichbare Cortex-Alternative gäbe, wäre ich mittelfristig dabei, da die Preise tendenziell günstiger sind als bei den AVRs. Gäbe es hier zumindest eine kostengünstige (unbeschränkte) Programmier-Variante? - C/C++ Debugging wäre natürlich sehr hilfreich, geht aber notfalls auch mit "printf"-Style , womit ich gern zurück zum eigentlichen Thema überleiten möchte :)
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.