Guten Abend,
meine im EEPROM hinterlegten Daten (Einstellungen) sind ein großes
globales struct mit dem Namen "Settings". Da ich zu
Dokumentationszwecken ohnehin Daten über den UART sende, will ich jetzt
auch die Einlstellungen zum Programmstart senden.
Der erste naive Ansatz:
Bis ich diese Funktion implementiert habe, habe ich ein großes Stück
Schreibarbeit vor mir. Außerdem ist das fehlerträchtig.
Wenn ich mir die Arbeit vereinfachen will- welche Möglichkeiten habe
ich? Muß ich meinen eigenen Parser in Python oder Matlab schreiben, oder
gibt es schon Werkzeuge, die ein struct mundgerecht vorzerlegen können?
Da dürfte wohl die einfachste Lösung sein - Du klopft Quick&Dirty ein
Script zusammen, das sich auf die Zeilenstruktur verlässt und mit
Regular Expressions arbeitet.
Wahrscheinlich wirst du das Script sowieso noch ein paar mal umbauen.
Weil dir dein erster Ansatz nicht mehr gefällt, oder weil du andere
Felder haben willst, oder eine mehrspaltige Tabelle...
Selbst wenn es ein Tool gibt - entweder macht es nicht das, was du haben
willst, oder konfigurieren wird aufwendiger als selbst zusammen
schustern.
Man kann die Struct-Deklaration und Zugriffe auf jedes Feld im struct
auch vom preprocessor generieren lassen. Das ist maximal hässlich, aber
manchmal nützlich. Mit deinen verschachtelten structs wird es
wahrscheinlich schwierig.
fields.inc
1
STRUCT_ENTRY(price,float,"%f",0.234)
2
STRUCT_ENTRY(weight,float,"%f",5.0)
3
STRUCT_ENTRY(count,int,"%d",0)
example.c
1
#include<stdio.h>
2
3
typedefstruct{
4
#define STRUCT_ENTRY(n, t, foo, fooo) t n;
5
#include"fields.inc"
6
#undef STRUCT_ENTRY
7
}tConfig;
8
9
voiddisp(tConfigbla)
10
{
11
#define STRUCT_ENTRY(n, foo, u, fooo) printf("\t" #n "=" u "\n", bla.n );
Nicht wirklich toll; vermindert die Schreibarbeit auf ca. die Hälfte:
1
#define Out(fmt, var) \
2
do { \
3
snprintf (buffer, 80, #var "=" fmt, var); \
4
usart_putstr (buffer); \
5
} while (0)
6
7
voidsettings_send(void)
8
{
9
charbuffer[81];
10
Out("%f",Settings.StepmotorDriver.currentRun_mA);
11
...
12
}
Ansonsten sehe ich da i.W. 2 Ansätze:
1) Einen Parser (z.B: aus cer C/C++ Quelle oder aus der Debug-Info).
2) Beschreibung der Datenstruktur in einer konfortablen Darstellung und
daraus dann Typdefinition und Output-Code generieren.
Bei beiden Ansätzen braucht's Host-Interaktion, d.h. auto-generierten
Code, was den Build-Prozess dann komplizierter macht.
Man käme auch ohne auto-generierten Code (für's Target) aus, wenn man
einfach Settings ein einem Stück binär rüberschiebt und der Host dann
die Meta-Info wie Komponentennamen beisteuert; was dann aber eine App
auf dem Host erfordert statt die Infos einfach auf einem Terminal
darzustellen.
Ganz ohne Host-Code bekäme man das auch über XMacros hin, also über eine
Datei, die man im Gegensatz zu einem Header mehrfach in
unterschiedlichen Kontexten includiert, siehe Beispiel von Tom. Aber
schön ist anders...
Die X-Macro-Methode hat den Vorteil, dass man bei Erweiterungen der
Struktur die Änderungen nur an einer einzigen Stelle vornehmen muss, was
nicht nur weniger mühsam, sondern auch weniger fehleranfällig ist.
Die Hässlichkeit dieser Methode liegt im Auge des Betrachters, ich
persönlich finde haufenweise Boilerplate ich noch hässlicher. Selbst
wenn es andersherum wäre, würde ich hier dennoch den Pragmatismus über
die Ästhetik siegen lassen, d.h. die X-Macros verwenden.
hab viel Erfahrung mit XMACROS.
Das lohnt sich nur, wenn sich deine Settings regelmäßig ändern und die
Struktur flach ist. Verschachtelte Structs machen Kopfschmerzen.
Und wenn deine Settings fix sind, dann sende auch mit statischem Code.
Alternative: Jedes Setting bekommt denselben Datentyp und du überträgst
die Rohdaten als Vektor. Vielleicht hast du am PC ja bessere
Möglichkeiten, die Struktur zu parsen und anzuzeigen?
Johann L. schrieb:> Nicht wirklich toll; vermindert die Schreibarbeit auf ca. die Hälfte:
#facepalm#
Danke Johann! Ich hatte ganz vergessen, daß man Strings einfach
hintereinanderschreiben kann. Und daß ein Argument gleichzeitig als
String und als Variable nutzen kann.
Das ist schon deutlich angenehmer, als separat ein Skript zu pflegen.
Eigentlich bin ich mit dem Makro auch schon ganz glücklich. Im Vergleich
zu vorher ist es ein Traum.
Yalu X. schrieb:> Die Hässlichkeit dieser Methode liegt im Auge des Betrachters, ich> persönlich finde haufenweise Boilerplate noch hässlicher. Selbst> wenn es andersherum wäre, würde ich hier dennoch den Pragmatismus über> die Ästhetik siegen lassen, d.h. die X-Macros verwenden.
An sich finde ich XMacros nicht so schlecht, werden z.B. auch im GCC
verwendet für Devices, Built-ins etc:
http://gcc.gnu.org/viewcvs/gcc/trunk/gcc/config/avr/avr-mcus.def?revision=267494&view=markup#l36http://gcc.gnu.org/viewcvs/gcc/trunk/gcc/config/avr/builtins.def?revision=267494&view=markup#l18
Im vorliegenden Fall dürfte es aber nicht einfach sein, die
Ausgabe(strings) so wie von Walter gewünscht zu erzeugen, und schon die
Typdefinition von Settings und statische Initializer etc. zu erzeugen
dürfte hässlich werden — das hässlich bezog sich also i.W. auf die im
konkreten Fall umzusetzenden Anforderungen.