Forum: Compiler & IDEs Boost.preprocessor library


von Uhu U. (uhu)


Angehängte Dateien:

Lesenswert?

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.

: Bearbeitet durch User
von Dr. Sommer (Gast)


Lesenswert?

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.

von Uhu U. (uhu)


Lesenswert?

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.

: Bearbeitet durch User
von Dr. Sommer (Gast)


Lesenswert?

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.

von Dr. Sommer (Gast)


Lesenswert?

PS: Man beachte:
http://www.boost.org/doc/libs/1_63_0/libs/preprocessor/doc/ref/div.html
"Valid values range from 0 to BOOST_PP_LIMIT_MAG."

http://www.boost.org/doc/libs/1_63_0/libs/preprocessor/doc/ref/limit_mag.html
"The BOOST_PP_LIMIT_MAG macro defines the maximum input and result 
agnitudes of arithmetic. [...] This macro currently expands to 256."

Da deine Zahlen aber größer als 256 sind, funktionieren die 
Boost-Arithmetik-Funktionen nicht.

Selbst diese simple Evaluierung funktioniert schon nicht, da brauchst du 
deinen ganzen Makro-Wurst gar nicht:
1
int i5 = BOOST_PP_ADD(8000000, 8);
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...

von Uhu U. (uhu)


Lesenswert?

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.

von Dr. Sommer (Gast)


Lesenswert?

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.

von Uhu U. (uhu)


Angehängte Dateien:

Lesenswert?

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
1
#define BAUDRATES  (4800)(9600)(19200)(38400)(57600)(115200)

eine passende Tabelle mit den Einstellungsdaten für den UART und einen 
String mit der Baudrate generieren kann:
1
const __flash BaudRate Bdrate[] = {
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...

: Bearbeitet durch User
von Johann L. (gjlayde) Benutzerseite


Lesenswert?

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ücken

rä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?

von M.K. B. (mkbit)


Angehängte Dateien:

Lesenswert?

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.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

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 :-)

von M.K. B. (mkbit)


Lesenswert?

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

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

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).

von Uhu U. (uhu)


Lesenswert?

Wie kann man mit Boost eine Sequenz (1)(2)(3)... aufsummieren?

von Yalu X. (yalu) (Moderator)


Lesenswert?

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:

1
#include <boost/preprocessor.hpp>
2
3
#define SEQ (1)(2)(3)
4
5
#define OP(s, state, x) BOOST_PP_ADD(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
6

von Uhu U. (uhu)


Lesenswert?

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.

von Yalu X. (yalu) (Moderator)


Lesenswert?

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.

: Bearbeitet durch Moderator
von Uhu U. (uhu)


Lesenswert?

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.

von Uhu U. (uhu)


Angehängte Dateien:

Lesenswert?

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.

: Bearbeitet durch User
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
Noch kein Account? Hier anmelden.