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.
> 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
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_tVar1;
3
uint16_tVar2;
4
uint8_tVar3;
5
6
// und eine Durchnummerierung dieser Variablen, inkl. notwendiger
7
// Zusatzinfo
8
structVariables[]={
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
charInput[]="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:
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 :)