Ich habe versucht, mit der Preprocessor-Lib die Konstanten für eine
Baudratentabelle mit 5 Einträgen mit jeweils 2 enum-Konstanten pro
Baudrate (Teiler und 2x-Modus) zu generieren.
Leider klappt das nicht, vermutlich, weil der Preprocessor anfängt,
Unsinn zu machen, wenn das Ergebnis der Macrosubstitutionen 500 kB
überschreitet.
Symptom sind ellenlange Textwürmer aus konkatenierten Macronamen in der
.i-Ausgabe - Macronamen dürfen im Ergebnis des Preprozessors nicht mehr
vorkommen, zumindest wenn alle erforderlichen Header includet sind.
Außerdem unsinnige Fehlermeldungen über ungültige Preprocessor-Tokens
bestehend aus einem Macronamen und '(' oder ')'
Eine interne Fehlermeldung des Preprozessors kommt nicht.
Uhu U. schrieb:> Symptom sind ellenlange Textwürmer aus konkatenierten Macronamen in der> .i-Ausgabe - Macronamen dürfen im Ergebnis des Preprozessors nicht mehr> vorkommen, zumindest wenn alle erforderlichen Header includet sind.
Das stimmt nicht ganz, wenn gültige Makro Aufrufe erst nach Substitution
anderer Makros entstehen bleiben die unevaluiert, es sei denn man fügt
einen extra Evaluations Level hinzu.
So etwas mit dem Preprocessor zu machen ist enorm fummelig, versuche es
vielleicht lieber mit C++ constexpr Funktionen oder templates, das kann
viel einfacher sein (aber genauso effizient da vom Compiler
berechnet)... Aber es ist mir aus deinem Code auch nicht ganz klar was
du da berechnen möchtest.
Dr. Sommer schrieb:> Aber es ist mir aus deinem Code auch nicht ganz klar was> du da berechnen möchtest.
Es sind 10 enum-Konstante, die damit definiert werden sollen, aber das
ist im Prinzip egal - Kern der Sache ist, dass es den cpp aus den
Latschen haut, wenn das Ergebnis zu groß wird.
> Das stimmt nicht ganz, wenn gültige Makro Aufrufe erst nach Substitution> anderer Makros entstehen bleiben die unevaluiert, es sei denn man fügt> einen extra Evaluations Level hinzu.
Das ist hier nicht der Fall und es wäre auch nicht sehr sinnvoll,
Macroaufrufe zu gerieren, die dann unevaluiert dem Compiler zum Fraß
vorgeworfen werden.
> So etwas mit dem Preprocessor zu machen ist enorm fummelig...
Mit der Boost.preprocessor nicht, wenn man das Prinzip, nach dem sie
arbeitet, mal verstanden hat.
Uhu U. schrieb:> Kern der Sache ist, dass es den cpp aus den> Latschen haut, wenn das Ergebnis zu groß wird.
Da wäre ich mir nicht so sicher. Der CPP ist ziemlich alt und stabil. Es
ist hingegen sehr einfach, ihn falsch zu verwenden, was bei dir der Fall
sein könnte. Der CPP wurde niemals für Iterationen und dergleichen
entwickelt. Die Iterationstiefe in Boost.PP ist ja auch begrenzt. Wenn
du die überschreitest passiert Unfug.
Uhu U. schrieb:> Das ist hier nicht der Fall
In Boost.PP intern ist das ziemlich sicher der Fall und es kann gut sein
dass das durch falsche Aufrufe getriggert wird.
> und es wäre auch nicht sehr sinnvoll,> Macroaufrufe zu gerieren, die dann unevaluiert dem Compiler zum Fraß> vorgeworfen werden.
Es ist auch nicht sinnvoll Nullpointer zu dereferenzieren, trotzdem
machen das die Leute...
Uhu U. schrieb:> Mit der Boost.preprocessor nicht, wenn man das Prinzip, nach dem sie> arbeitet, mal verstanden hat.
Okay dann hast du da andere Erfahrungen gemacht. Nun, wenn du dir so
sicher bist dass der Fehler im GCC liegt, kann dir hier vermutlich auch
keiner helfen. Nun gut, du hast ja auch keine konkrete Frage gestellt.
Hat also nichts mit dem GCC zu tun, CPP und Boost.PP sind einfach für so
etwas nicht gemacht.
Mit constexpr Funktionen/templates hast du dieses Problem nicht...
Dr. Sommer schrieb:> Da deine Zahlen aber größer als 256 sind, funktionieren die> Boost-Arithmetik-Funktionen nicht.
Oh ja... Das degradiert die Lib zum Spielzeug.
Selbst BOOST_PP_LIMIT_MAG auf 115200 hoch zu setzen, wird wenig bringen,
weil der cpp dann Stunden brauchen wird, das alles zusammenzufrickeln.
Ich habe mal etwas ausgiebiger mit m4 rumgespielt - der ist nicht ganz
so doof, wie der cpp, aber etwas kompliziertere Macros produzieren auch
dort einen immensen Aufwand und gehörige Laufzeiten.
Uhu U. schrieb:> Oh ja... Das degradiert die Lib zum Spielzeug.
Nicht alles, was nicht deinen Zwecken entspricht, ist ein Spielzeug. Die
Boost.PP ist keine Numerik-Bibliothek. Man muss halt seine Werkzeuge
sinnvoll auswählen.
Die Arithmetik-Macros muss man nicht in Boost implementieren - da
reichen ganz normale cpp-Macros.
Das Ergebnis kann man dann mit Boost-Textmacros zu den gewünschten
Strukturen zusammenbasteln.
Etwas mysteriös ist eine Warnung
"large integer implicitly truncated to unsigned type [-Woverflow]|"
beim Erzeugen der Datenstruktur (Zeile 42 in uart-test.c)
Versuche durch selektives Löschen von Teilausdrücken haben bisher nur
ergeben, dass der Fehler mit dem ersten Wert (Bxxxx) im Initialisierer
zusammenhängt; warum die Warnung nur einmal auftritt, verstehe ich
nicht. (Im [zusammengestutzten] .i-File ist das Zeile 23.)
Im Anhang die geänderten Quellen und das Ergebnis des cpp.
Letztendlicher Witz der Sache ist, dass man mit einer einzigen
Definition wie
eine passende Tabelle mit den Einstellungsdaten für den UART und einen
String mit der Baudrate generieren kann:
1
const__flashBaudRateBdrate[]={
2
{B4800,B4800_2X,"4800"},
3
{B9600,B9600_2X,"9600"},
4
{B19200,B19200_2X,"19200"},
5
{B38400,B38400_2X,"38400"},
6
{B57600,B57600_2X,"57600"},
7
{B115200,B115200_2X,"115200"},
8
};
Die Symbole Bxxxx und Bxxxx_2X sind enum-Konstanten, die die
eigentlichen Werte definieren. Die Enumeration wird als Zwischenschritt
erzeugt, der generierte Text ist - wie man in .i sieht - etwas
unübersichtlich...
Uhu U. schrieb:> Die Arithmetik-Macros muss man nicht in Boost implementieren - da> reichen ganz normale cpp-Macros.>> Das Ergebnis kann man dann mit Boost-Textmacros zu den gewünschten> Strukturen zusammenbasteln.>> Etwas mysteriös ist eine Warnung> "large integer implicitly truncated to unsigned type [-Woverflow]|"> beim Erzeugen der Datenstruktur (Zeile 42 in uart-test.c)
Verwende unsigned Konstanten.
> Versuche durch selektives Löschen von Teilausdrückenräusper
soviel zu wartbarem Code :-)
> Witz> const __flash BaudRate Bdrate[] = {> { B4800, B4800_2X, "4800" },> { B9600, B9600_2X, "9600" },> { B19200, B19200_2X, "19200" },> { B38400, B38400_2X, "38400" },> { B57600, B57600_2X, "57600" },> { B115200, B115200_2X, "115200" },> };
Und dafür brauch man Boost?
Ich habe mal deinen Quellcode überarbeitet, um ein Lösung mit C++11
constexpr zu zeigen, wenn dies vom Compiler unterstützt wird. Im Prinzip
ist es nichts anderes als die Macros, nur dass der Compiler bessere
Meldungen erzeugen kann und die Boost Header mit den Macros für die
Berechnung nicht mehr nötig sind.
Ich habe angenommen, dass die enum Konstanten (B4800, B4800_2X) im
weiteren Code nicht verwendet werden und nur für die Befüllung der
Tabelle nötig waren.
Zur Erklärung:
In setbaud.h habe ich die Makros durch Funktionen mit constexpr ersetzt.
Damit ist auch direkt ersichtlich, welcher Datentyp von der Funktion
erwartet wird und zurückgegeben werden soll. Die Warnings mit truncated
Integern würden dann direkt in der entsprechenden Funktion gemeldet und
nicht erst, wenn das ganze expandierte Makro ausgewertet wird.
Das constexpr garantiert, dass die Funktion zur Compilezeit ausgewertet
werden kann, wenn sie mit Konstanten Argumenten aufgerufen wird.
Im Prinzip ginge es auch mit C Mitteln ohne constexpr, dann müsste man
sich aber auf die Optimierung des Compilers verlassen, dass die
Berechnungen zur Compilezeit stattfinden.
M.K. B. schrieb:> Ich habe mal deinen Quellcode überarbeitet, um ein Lösung mit C++11> constexpr zu zeigen,
Fehlen nur noch Kleinigkeiten wie __flash :-)
Johann L. schrieb:> Fehlen nur noch Kleinigkeiten wie __flash :-)
Da hast du Recht. Ich muss zugeben, dass ich das gerade nur auf einem
Desktop Linux mit gcc ausprobiert habe.
Aber __flash legt doch nur fest, dass die Konstanten später im
Flashspeicher landen. Wie der Compiler die Konstanten bestimmt sollte
davon eigentlich unabhängig sein. Ob das der entsprechende Embedded
Compiler aber auch so sieht, weiß man nicht. :-D
M.K. B. schrieb:> Johann L. schrieb:>> Fehlen nur noch Kleinigkeiten wie __flash :-)>> Da hast du Recht. Ich muss zugeben, dass ich das gerade nur auf einem> Desktop Linux mit gcc ausprobiert habe.>> Aber __flash legt doch nur fest, dass die Konstanten später im> Flashspeicher landen.
Nö, es wird auch anders auf die Daten zugegriffen (im Gegensatz zu
progmem).
Uhu U. schrieb:> Dr. Sommer schrieb:>> Da deine Zahlen aber größer als 256 sind, funktionieren die>> Boost-Arithmetik-Funktionen nicht.>> Oh ja... Das degradiert die Lib zum Spielzeug.
Arithmetik mit Integer-Zahlen >256 und FP-Zahlen kann der Compiler, dazu
muss man nicht den Präprozessor vergewaltigen. Aber das hast du ja
mittlerweile selber herausgefunden.
Uhu U. schrieb:> Wie kann man mit Boost eine Sequenz (1)(2)(3)... aufsummieren?
Indem man zuerst die Dokumentation liest und danach passend dazu den
Code hinschreibt:
Yalu X. schrieb:> Arithmetik mit Integer-Zahlen >256 und FP-Zahlen kann der Compiler, dazu> muss man nicht den Präprozessor vergewaltigen.
Kommt darauf an, was man damit machen will... Die Arithmetik von PP
braucht man zum Glück nur für Werte, die den Ablauf der Lib betreffen.
Ausdrücke, mit denen der Compiler oder der cpp außerhalb von PP was
machen sollen, kann man so hinschreiben.
Für die Konfiguration von Quelltextbibliotheken ist PP auf jeden Fall
eine Option, wenn man nur c zur Verfügung hat.
Uhu U. schrieb:> Kommt darauf an, was man damit machen will... Die Arithmetik von PP> braucht man zum Glück nur für Werte, die den Ablauf der Lib betreffen.
Eben. Und dafür reichen i.Allg. auch kleine Zahlenwerte. In deinem
Beispiel vom 04.01.2017 17:20 würdest erst an eine Grenze stoßen, wenn
du mit mehr als 256 unterschiedlichen Bauraten hantieren wolltest.
Auch für das Aufsummieren der Elemente einer Boost-PP-Sequenz kann man
den Compiler zuhilfe nehmen und ist dann nicht durch die Boost-PP-Limits
beschränkt:
1
#include<boost/preprocessor.hpp>
2
3
#define SEQ (1123.4)(765.9)(83734.1)
4
5
#define OP(s, state, x) state+x
6
#define SUM BOOST_PP_SEQ_FOLD_LEFT(OP, 0, SEQ)
7
8
SUM
Test:
1
$ cpp pctest.c |grep -v '^\(#\s*[0-9]\|$\)'
2
0 +1123.4 +765.9 +83734.1
Diese Summe berechnet der Compiler, so dass zur Laufzeit dafür keine
Rechenzeit verbraucht wird. Nur kann das Ergebnis eben nicht mehr im
Präprozessor verwendet werden.
Yalu X. schrieb:> Diese Summe berechnet der Compiler, so dass zur Laufzeit dafür keine> Rechenzeit verbraucht wird.
Rechnen kann der cpp eben nicht selber... nur mit Texten jonglieren.
Und hier die funktionsfähige Baudratentabelle - siehe Anhang.
uart.c:
In Zeile 20 wird die zulässige Baudraten-Toleranz definiert.
In Zeile 23 werden die die gewünschten Baudraten als Boost-Sequenz
definiert.
In Zeile 28 werden die Baudratengenerierungsparameter für einen ATmega
328 in Form von emum-Konstanten berechnet.
In Zeile 30 wird geprüft, ob einzelne Baudraten die Toleranz
überschreiten.
In Zeile 38 werden die Initialisierungen eines Arrays vom Typ struct
BaudRate (am Ende von setbaud.h definiert) berechnet.
In Zeile 41 werden die Macrodefinitionen für die ganze Übung entsorgt.
Die Boost-Header-Includes könnte man auch noch in setbaud.h verschwinden
lassen...
Der Headerfile setbaud.h enthält die ganze Boost-Definition - man muss
ihn nicht mehr anfassen...
Die (Zwischen-)Ergebnisse für 2 Taktfrequenzen sind im letzten Anhang
aufgelistet.