Hallo,
ich möchte gerne ein dynamisches Format auf sprintf anwenden.
Also ich habe eine Funktion der ich das Format (zb: "%u;%f;%f\n") als
String übergebe.
Die dazugehörenden Werte befinden sich in einem Array (konkret im
SDRAM).
Nun möchte ich die Werte aus dem Array (SDRAM) formatiert auslesen.
Leider funktioniert es nicht, nur die Pointer der Funktion sprintf zu
übergeben.
z.B.
1
charbuffer[1024];// buffer in dem der formatierte String hineingeschrieben werden soll
2
sprintf(buffer,format,sdram);//format: Pointer auf den Format String; sdram: Pointer auf den SDRAM bzw irgendeinen buffer wo die Werte drinn stehen
Gibt es da irgendeine Lösung dafür oder muss ich dies über schleifen
zusammenbasteln?
(Würde mir gar nicht gefallen ;) )
Ziel ist es in dem SDRAM beliebige Werte abzuspeichern und je nach
Bedarf diese formatiert auslesen zu können um in eine csv Datei auf der
SD-Karte zu schreiben.
godi
godi S. schrieb:> Leider funktioniert es nicht, nur die Pointer der Funktion sprintf zu> übergeben.
Doch das geht.
Was hast du denn versucht, was hast du erwartet, und was ist stattdessen
passiert?
godi S. schrieb:> sprintf(buffer, format, sdram); //format: Pointer auf den Format> String; sdram: Pointer auf den SDRAM bzw irgendeinen buffer wo die Werte> drinn stehen
Jetzt sehe ich erst, wo der Hund begraben liegt: Du willst die von
sprintf anzuzeigenden Werte nicht als Einzelargumente, sondern als
Byte-Array übegeben. Das geht so nicht, hat aber nichts mit dem
dynamischen Formatstring zu tun.
Du bräuchtest eine Funktion ähnlich vsprintf, die als drittes Argument
keine va_list, sondern ein Byte-Array entgegen nimmt. Die gibt es
(zumindest in C99) nicht. Auch eine Überführung des Byte-Arrays in eine
va_list geht m.W. nur auf "schmutzige" und unportable Art und Weise.
Yalu X. schrieb:> godi S. schrieb:>> Leider funktioniert es nicht, nur die Pointer der Funktion sprintf zu>> übergeben.>> Doch das geht.>> Was hast du denn versucht, was hast du erwartet, und was ist stattdessen> passiert?
Was ich mir erwarte bzw was ich gerne möchte:
Die Übergabe der Variablen Liste (ab Parameter 3) als Array wo die Werte
der Parameter in richtiger Reihenfolge gespeichert sind.
zB:
1
charbuffer[1024];// Der Buffer wo der String hingespeichert werden soll
Yalu X. schrieb:> godi S. schrieb:>> sprintf(buffer, format, sdram); //format: Pointer auf den Format>> String; sdram: Pointer auf den SDRAM bzw irgendeinen buffer wo die Werte>> drinn stehen>> Jetzt sehe ich erst, wo der Hund begraben liegt: Du willst die von> sprintf anzuzeigenden Werte nicht als Einzelargumente, sondern als> Byte-Array übegeben. Das geht so nicht, hat aber nichts mit dem> dynamischen Formatstring zu tun.>> Du bräuchtest eine Funktion ähnlich vsprintf, die als drittes Argument> keine va_list, sondern ein Byte-Array entgegen nimmt. Die gibt es> (zumindest in C99) nicht. Auch eine Überführung des Byte-Arrays in eine> va_list geht m.W. nur auf "schmutzige" und unportable Art und Weise.
Ja genau das will ich. ;)
Wie könnte ich dies am besten lösen?
Den Code von sprintf abwandeln?
godi S. schrieb:> Was ich mir erwarte bzw was ich gerne möchte:> Die Übergabe der Variablen Liste (ab Parameter 3) als Array wo die Werte> der Parameter in richtiger Reihenfolge gespeichert sind.
Schau mal bei vsprintf vorbei.
Die spannende Frage dabei ist allerdings, was die richtige Ordnung der
Liste ist. Denn vsprintf erwartet die in exakt so, wie der Compiler sie
bei varargs-Funktionen erzeugt. Das ist prima, wenn du es genau dafür
verwenden willst, also für eine eigene Funktion wie printf, deren
Paremeter du nach unten durchreichen willst. Weniger prima, wenn du das
Zeug fabrizieren willst.
Wenn du also damit keine va_list verarbeiten willst, dann solltest du
ein eigenes sprintf-Analogon basteln, wenn du dich nicht auf eine
bestimmte Compiler-Release festlegen willt.
Vielen Dank mal für die Info.
Da werde ich mich erst mal einlesen müssen, bis ich weitere ergebnisse
habe. :)
Aber vielleicht hat schon mal jemand so was realisiert und kann mir den
code zukommen lassen. ;)
> Ziel ist es in dem SDRAM beliebige Werte abzuspeichern und je> nach Bedarf diese formatiert auslesen zu können um in eine csv> Datei auf der SD-Karte zu schreiben.
Die Frage ist für mich auch, ob der ganze Ansatz mit sprintf hier
überhaupt zielführend ist.
2 Punkte gibt es zu berücksichtigen
* zum einen ist da die Frage der Anzahl. Denn für jede %-Formatierung im
Format-String, muss es auch einen entsprechenden Wert im 'Array' geben.
Hast du zuviele Werte im Array, dann ist das noch nicht weiter tragisch.
Interessant wird es dann, wenn mehr Formatier-Argumente im Formatstring
vorhanden sind, als es überhaupt Werte gibt.
* zum anderen ist die Reihenfolge der Werte nicht uninteressant. Denn
gerade bei einem recht universellen Mechanismus, sollte es ja eine
Möglichkeit geben, wie ich über den Formatier-String auch noch auswählen
kann, welchen Werte aus dem 'Array' ich eigentlich an dieser Stelle
haben will. An diesem Punkt stehst du aber mit sprintf alleine da, das
kann sprintf nicht.
d.h. ich würde mir hier einen eigenen Mechanismus einfallen lassen, der
die beiden Punkte handhabt. Die Formatierangabe selbst (Feldbreite etc.)
kann ja ähnlich wie im Formatierstring laufen. Die Frage ist ja auch, wo
denn dieser FOrmatierstring zb herkommt und ob das überhaupt ein
Formatierstring sein muss, oder ob man diese Angaben nicht auch anders
an eine spezielle Funktion übergeben darf bzw. kann.
Meine Idee/Umsetzung dahinter ist folgende:
Ich habe Code der aus Simulink generiert wird.
Da dies aber zu Testzwecken verwendet wird möchte ich beliebige Werte in
den SDRAM speichern zum mitloggen. Diese können dann entweder direkt
über TCP/IP an Matlab gesendet werden oder auch als csv Datei auf die
SD-Karte gespeichert werden.
Also im SDRAM befinden sich dann die Werte immer im selben Muster
hintereinandergereiht. Dadurch weiß ich auch genau wieviele Blöcke mit
dem selben Muster im SDRAM gespeichert sind.
Den Format-String will ich auch aus Simulink generieren lassen.
Damit aber der Code für die Speicherverwaltung nicht mit den Code der
eigentlichen Funktion durcheinandergewürfelt wird hätte ich eben gerne
eine Funktion in meiner Speicherverwaltung die mir als Parameter den
Format-String entgegennimmt und wenns noch nötig ist die Größe eines
Blockes.
Somit ist die Speicherverwaltung vollständig alleinstehend, braucht
keine includes des generierten Codes und kann universell einsetzbar
benutzt werden.
Vielen Dank für die Hilfe!
Leider habe ich das Problem, dass in meiner Funktion die definition des
structs unbekannt ist.
Aber dies mit va_list ist eine gute Idee, kann ich sicher noch mal wo
brauchen, und ich habe was dazugelernt. :)
Mein "sprintf" sollte eben nur aufgrund des Formatier-Strings auf die
Elemente im Array zugreifen.
Also wenn der Parser auf zb '%u' trifft dann soll er einfach 4bytes aus
dem Array herausnehmen und diese als uint darstellen. Danach den Pointer
um 4Bytes erhöhen. Wenn er als nächstes '%f' findet dann einfach die
4Bytes auf die gerade der Pointer zeigt hernehmen und diese als float
darstellen. Pointer erhöhen usw...
Ich habe schon einen kurzen Blick in sprintf bzw vfprintf geworfen.
(http://svn.savannah.nongnu.org/viewvc/*checkout*/trunk/avr-libc/libc/stdio/vfprintf.c?revision=2191&root=avr-libc)
Wenn ich es richtig verstanden habe dann müsste es ja funktionieren wenn
ich die va_arg Aufrufe durch ein Makro ersetze, das mir den Wert der
nächsten xBytes zurückliefert und den Pointer eben um xBytes erhöht.
godi S. schrieb:> Mein "sprintf" sollte eben nur aufgrund des Formatier-Strings auf die> Elemente im Array zugreifen.
Das macht doch das Beispiel von mir. vprintf hat sonst keine
Informationen über die struct.
Die struct ist auch nur ein Hilfsmittel, um verschiedene Daten im
Speicher abzulegen.
godi S. schrieb:> Wenn er als nächstes '%f' findet dann einfach die> 4Bytes auf die gerade der Pointer zeigt hernehmen und diese als float> darstellen.
Und genau da musst du aufpassen. printf kennt kein float, sondern nur
double. Schon immer. Es holt also nicht 4 Byte aus dem Speicher, sondern
8.
(Genau genommen kennt printf den Specifier %lf erst seit C11. Er wurde
vorher aber geduldet)(ja, ich weiß dass die 4 bzw. 8 Byte systemabhängig
sind)
Die Werte müssen hintereinander im Speicher stehen, so als wären sie die
Paramter auf dem Stack.
godi S. schrieb:> Mein "sprintf" sollte eben nur aufgrund des Formatier-Strings auf die> Elemente im Array zugreifen.> Also wenn der Parser auf zb '%u' trifft dann soll er einfach 4bytes> aus> dem Array herausnehmen und diese als uint darstellen. Danach den> Pointer> um 4Bytes erhöhen. Wenn er als nächstes '%f' findet dann einfach die> 4Bytes auf die gerade der Pointer zeigt hernehmen und diese als float> darstellen. Pointer erhöhen usw...
Genau das wird in der von DirkB vorgestellten Methode auch gemacht. Du
kannst anstelle der dort definierten Strukturen und Arrays einen
beliebigen Pointer übergeben, also bspw. auch einen, der auf den Anfang
deiner Daten im SDRAM zeigt. Probier's einfach mal aus.
Allerdings ist die Methode von der Sorte, die ich oben "schmutzig" und
unportabel bezeichnet habe, denn ihr Funktionieren hängt stark davon ab,
wie der Typ va_list intern aufgebaut ist und wie das Alignment der Daten
im Speicher ist. Auf 8-Bit-AVRs (vielleict auch auf AVR32) könnte die
Sache zu funktionieren. Auf meinem PC hingegen geht der Code nicht
einmal durch den GCC, da ein Array (bzw. ein Pointer) nicht in va_list
gecastet werden kann. va_list ist hier nämlich im Gegensatz zum AVR
nicht ein einfacher Pointer, sondern eine etwas komplexrere
Datenstruktur.
Ok, sorry habe das Beispiel beim ersten mal hinsehen falsch
interpretiert.
Leider komme ich erst wieder am Montag zu dem AVR32 Board damit ich den
Code Testen kann.
Ich wollte es jetzt unter Ubuntu (64Bit) mit Eclipse testen.
Leider bekommen ich folgenden Fehler:
Typkonvertierung gibt Feldtyp an
Naja ich werde es am Montag noch mal im Atmel Studio probieren.
Aber vielen Dank für eure Hilfe und Tipps! :)
godi S. schrieb:> Ich wollte es jetzt unter Ubuntu (64Bit) mit Eclipse testen.
Der Compiler und die Bitbreite vom Code sind entscheidend.
Ich hatte das mit Code::Blocks und dem mitgelieferten GCC unter 32-Bit
Windows 7 getestet.
Da brauchte ich das __attribute__((packed)).
Bei 64-Bit kann das wieder anders aussehen.
Du kannst dir ja mal für dein System die stdarg.h ansehen.
(Und auch die Dateien die dort mit #include eingebunden werden)
Dirk B. schrieb:> Bei 64-Bit kann das wieder anders aussehen.
Da auf i86_64 Argumente variabler Anzahl nicht nur auf dem Stack,
sondern auch in Registern (sowohl GP- als auch FP-Register) übergeben
werden, wird das Vorhaben, selber eine va_list nachzubilden, um einiges
komplizierter.
Ich kann mir aber gut vorstellen, dass auf dem AVR32 die Argumente ganz
klassisch auf dem Stack übergeben werden, so dass die Methode von Dirk
B. dort durchaus funktionieren könnte.
Hallo,
ich habe Gestern noch damit herumprobiert, aber leider bin ich zu keinen
brauchbaren Ergebnis gekommen.
Wenn ich zwei Werte (uint32 und float) gespeichert habe dann habe ich
das Problem dass er für float Werte 8 Bytes ausgelesen hat und nicht 4.
Wenn ich ein Format von '%u%f%u%f' wollte dann hat sich der AVR32 wider
eine Exception geworfen.
Also irgendwie funktioniert der Ansatz ein Array in eine Variable
Argumentenliste zu Wandeln mehr schlecht als recht.
Jetzt habe ich mir die Source der Toolchain von Atmel downgeloaded und
mir das File vfprintf.c angesehen.
Naja das beeinhaltet fast 2000 LOC!
So einfach wie ich mir das vorgestellt habe mit ein wenig umschreiben
ist es wohl auch nicht.
Hat vielleicht jemand noch eine andere Idee?
Hier noch der link zur Atmel Toolchain (newlib):
http://distribute.atmel.no/tools/opensource/Atmel-AVR-Toolchain-3.4.2/avr32/http://distribute.atmel.no/tools/opensource/Atmel-AVR-Toolchain-3.4.2/avr32/avr32-newlib-1.16.0.tar.gz
godi S. schrieb:> Wenn ich zwei Werte (uint32 und float) gespeichert habe dann habe ich> das Problem dass er für float Werte 8 Bytes ausgelesen hat und nicht 4.
Mit float-Werten kann das nicht funktionieren, da diese bei der Übergabe
an Funktionen mit variabler Argumentliste immer in double umgewandelt
werden. Entsprechend erwartet das %f-Format kein float, sondern ein
double, und liest deswegen 8 Bytes. Siehe auch Hinweis von Dirk:
DirkB schrieb:> Wenn du dich an die Eigenheiten der variablen Argumente hälst> (Erweiterung von float auf double, integrale Typen mindestens auf int)> geht es z.B. so:godi S. schrieb:> Hat vielleicht jemand noch eine andere Idee?
Welche Datentypen und welche Format-Optionen (Feldlänge, Anzahl der
Nachkommastellen usw.) brauchst du tatsächlich? Wenn es nur ein paar
wenige sind, bietet es sich an, eine stark abgespeckte Version von
vsprintf zu schreiben, die anstelle der va_list einen uint8_t-Pointer
entgegennimmt, und die auch mit 4-Byte-Floats umgehen kann.
Yalu X. schrieb:> Welche Datentypen und welche Format-Optionen (Feldlänge, Anzahl der> Nachkommastellen usw.) brauchst du tatsächlich? Wenn es nur ein paar> wenige sind, bietet es sich an, eine stark abgespeckte Version von> vsprintf zu schreiben, die anstelle der va_list einen uint8_t-Pointer> entgegennimmt, und die auch mit 4-Byte-Floats umgehen kann.
Ja das habe ich mir auch schon gedacht.
Für meinen Fall würde ich die ganzen Formatoptionen eh nicht brauchen,
da die Werte nur in ein csv File geschrieben werden sollen.
Von den Datentypen her würden mir die 32Bit-Typen genügen.
uint32, int32, float, eventuell noch Hex.
Naja da werde ich mich dann am Montag drüberstürzen. :)
Hallo,
so jetzt habe ich das implementiert und getestet.
Funktioniert mal so weit für meinen Anwendungsfall.
Jedoch habe ich irgendwie das Gefühl, dass dies einfacher zu
implementieren geht. ;)
Hat jemand einen Tipp für mich?
Leider unterstützt auch der AVR32 die Funktionen utoa, itoa, ftoa nicht.