Forum: PC-Programmierung Ich versuch es ja, aber ich werde mit C++ einfach nicht warm :(


Announcement: there is an English version of this forum on EmbDev.net. Posts you create there will be displayed on Mikrocontroller.net and EmbDev.net.
von Kaj G. (Firma: RUB) (bloody)


Bewertung
-3 lesenswert
nicht lesenswert
Guten Morgen, ich wünsche allen einen angenehmen dritten Advent :)

Aufgrund der gar nicht mal so wenigen Threads hier zu Themen wie "C++ 
auf Mikrocontrollern", "C vs.C++", usw. und ich auch gerade im Zuge 
meines Studiums (mehr oder minder) dazu gezwungen bin mich mal wieder 
mit C++ zu beschaeftigen, dachte ich mir: Gut, beschäftigst du dich halt 
mal wieder mit C++.

Um meine etwas eingerosteten Kenntnisse aufzufrischen hab ich mir ein 
paar Bücher besorgt, z.B. "Real-Time C++" und "Effektives modernes C++".

Als ich die Bücher in meinen Händen hielt und einfach nur etwas 
durchgeblättert habe wurde mir etwas schlecht.

Von den C++-Verfechtern werden immer gerne Argumente gebracht wie:
"C++ ist lesbarer, wartbarer, sicherer als C."

1. Beispiel
1
// C++ register access
2
*reinterpret_cast<volatile std::uint8_t*>(port) ^= value;
3
4
// Equivalent C-style
5
*((volatile uint8_t*) port) ^= value;

2. Beispiel
1
constexpr std::array<std::uint8_t, 9U> data =
2
{{
3
    UINT8_C(0x31), UINT8_C(0x32), UINT8_C(0x33),
4
    UINT8_C(0x34), UINT8_C(0x35), UINT8_C(0x36),
5
    UINT8_C(0x37), UINT8_C(0x38), UINT8_C(0x39),
6
}};
Hab ich was verpasst? Hab ich die Stelle überlesen an der steht, dass 
jetzt negativ Beispiele kommen?
Entschuldigung, aber sowas ist weder lesbarer noch irgendwas. Im 
Gegenteil, es ist sogar sehr viel unleserlicher.
Das einzige was hier besser werden mag, ist die Arbeit fuer den 
Compiler. Für jeden Entwickler, und jeden der sich solchen Code 
anschauen muss wird es schlechter.

Ein 3. Beispiel: auto
Da wird dann als erstes dieses Beispiel gebracht:
1
int  x1;        // potenziell nicht initialisiert
2
auto x2;        // Fehler! Initialisierung notwenig
3
auto x3 = 0;    // Gut, wohl definierter Wert
Okay, auto mag Vorteile habe, aber bei einem primitiven Datentyp sehe 
ich die nicht. Die erzwungene initialisierung? Jeder halbwegs 
vernünftige Compiler warnt, wenn man lesend auf eine nicht 
initialisierte Variable zugreift. Das ist für mich also kein Argument 
jetzt alles mit auto zuzupflastern. Bei Objekten mag das anders sein, 
aber bei primitiven Datentypen sehe ich keinen Vorteil.

Ich geb mir wirklich Mühe C++ besser zu verstehenund und zu nutzen. Wenn 
mir aber sowas als "effektiv und modern", womöglich sogar als hip, 
verkauft werden soll,...

Sowas ist nicht lesbarer, ganz im Gegenteil. Und wenn es nicht lesbar 
ist, ist es schon gar nicht wartbar (nur weil es lesbar ist muss es aber 
auch nicht wartbar sein).

Nicht falsch verstehen: Ich erkenne an, dass C++ gute Vorteile gegenüber 
C hat. Aber ich habe das Gefühl, dass C++ einfach nur noch aufgeblasen 
und überladen bis zum geht nicht mehr ist. :(

Ich werde mit C++ einfach nicht warm...

Grüße

von Christian M. (Gast)


Bewertung
-12 lesenswert
nicht lesenswert
C wurde speziell dafür entwickelt, um unleserlich zu sein!

Gruss Chregu

von g457 (Gast)


Bewertung
4 lesenswert
nicht lesenswert
> Ich werde mit C++ einfach nicht warm...

Dann nimms halt nicht her. Das war jetzt einfach.

von (prx) A. K. (prx)


Bewertung
-4 lesenswert
nicht lesenswert
Kaj G. schrieb:
> Von den C++-Verfechtern werden immer gerne Argumente gebracht wie:
> "C++ ist lesbarer, wartbarer, sicherer als C."

Ich würde es etwas provokant so umformulieren:
  C++ ist weder lesbar, noch wartbar, noch sicher - aber
  C++ ist lesbarer, wartbarer, sicherer als C. ;-)

Das mit der Lesbarkeit ist allerdings eine "tough love", die sich nicht 
ganz so leicht erschliesst.

: Bearbeitet durch User
von Bernd K. (prof7bit)


Bewertung
2 lesenswert
nicht lesenswert
Ich glaub es ist ein klitzekleines bisschen typsicherer als C. 
Typsicherheit ist was sehr hilfreiches und erstrebenswertes, jedes 
bisschen mehr als in C hilft.

Und es hat Klassen, Vererbung und Templates. Das ist auch ne feine 
Sache, das kann viel haarsträubendes Makro-Gewürge oder 
Code-Duplizierung oder andere Kompromisse ersetzen.

Insgesamt leserlicher als C ist es jedoch tatsächlich nicht, böse Zungen 
weisen sogar darauf hin daß es eine unentscheidbare Grammatik hat und 
eigentlich gar nicht geparst werden können dürfe, aber an den Stellen an 
denen Würgarounds aus CPP + C mit den korrekten Sprachmitteln in C++ neu 
implementiert werden wird das Endergebnis trotzdem lesbarer und 
eleganter sein.

von Yalu X. (yalu) (Moderator)


Bewertung
9 lesenswert
nicht lesenswert
C++ wurde mit dem Ziel geschaffen, folgende Anforderungen zu erfüllen:

A1: Es soll ein hohes Abstraktionsniveau bieten.

A2: Trotzdem soll es maschinennahe und effiziente Programmierung
    ermöglichen.

A3: Zudem soll es abwärtskompatibel zu C sein.

Dabei stehen aber A2 und A3 der Anforderung A1 etwas im Wege.

Ein hohes Abstraktionsniveau dient eigentlich dazu, Programmcode kürzer,
übersichtlicher, wartbarer und leichter wiederverwendbar zu machen. Im
Gegensatz zu anderen Sprachen, die ausschließlich diese Ziele vorfolgen
(und deswegen auf A2 und A3 verzichten), ist dies bei C++ leider nur
teilweise gelungen.

Ich kenne aber auch keine Alternative, die die drei Anforderungen besser
erfüllt als C++. Lässt man A3 weg, wird die Auswahl schon etwas größer,
da fallen mir bspw. D, Rust, Nim und Go ein. Keine dieser Sprachen hat
sich aber bisher wirklich etablieren können.

Wenn also maschinennahe und effiziente Programmierung gefordert ist und
man nicht ganz am Rand der Softwarecommunity stehen möchte, bleiben
letzendlich nur C und C++ übrig. Bei C muss man dann eben auf höhere
Sprachfeatures, bei C++ auf ein einfaches Sprachkonzept und die leichte
Erlernbarkeit verzichten.

Die Leute (zu denen ich auch gehöre), die mit C aufgewachsen sind und
den Werdegang von C++ mitverfolgt haben, haben i.Allg. wenig Probleme
mit den teilweise etwas "schrägen" Eigenschaften von C++ und schaffen es
deswegen, dessen Vorteile gewinnbringend für sich zu nutzen. Für
Neueinsteiger ist es aber IMHO eine der am schwierigsten zu lernenden
Sprachen.

Ich selber benutze wegen des in einigen Anwendungsbereichen bestehenden
Mangels an Alternativen C++ sowohl beruflich als auch privat, obwohl ich
wie du nie richtig "warm" damit geworden bin. Immerhin haben sich seit
C++11 einige Dinge deutlich verbessert, so dass man nun bspw. anstelle
von diesem Monster

1
  for(std::vector<ElementType>::iterator it = v.begin(); it != v.end(); it++)
2
    use(*it);


auch einfach dieses schreiben kann:

1
  for(auto el: v)
2
    use(el);
3
}

Das ist nur eins von vielen Beispielen, wo sich C++ tatsächlich
verbessert hat.

von lalala (Gast)


Bewertung
1 lesenswert
nicht lesenswert
Yalu X. schrieb:
> A2: Trotzdem soll es maschinennahe und effiziente Programmierung
>     ermöglichen.

Richtig. Auch 'zero-overhead' zur Run-time genannt. Stroustrup war der 
Meinung, dass wenn die Sprache auch nur nano-Sekunden langsamer als C 
wäre, würde niemand von C umsteigen. (Deswegen haben Exceptions auch 
lange gebraucht anerkannt zu werden, da angeblich die Laufzeit langsamer 
wird. Fundierte Daten dazu habe ich jedoch nicht gefunden).

Wenn man das beherzigt, versteht man auch viele Sachen: - warum z.B. 
eine lokale Variable nicht automatisch initialisiert wird. Und warum es 
keine Garbage Collection gibt. Und warum nur 'at' den Zugriff prüft 
(z.B. bei stl Vektoren) und [] ungeprüft ist. Und warum ein Iterator 
ungültig werden kann falls man ein Element einfügt.

Zu Deinem Beispiel: auto hilft viel, wenn man den Rückgabewert einer 
stark template erzeugten Funktion einfach in eine  Variable speichern 
will (und dann natürlich entsprechend in einer angepassten Funktion 
verwenden). (z.B. ein pair als Rückgabetyp, oder wie by Yalu 
Iteratoren).

Zu den Casts: ja, die unterschiedlichen Casts machen es schon etwas 
sicherer, da jetzt im Code steht was wirklich gemacht wird.

Wenn man das beherzigt, ist alles etwas einfacher. C++ ist eine Sprache, 
für hardwarenahe Programmierung und für libs wenn es auf Perfomance 
ankommt. Im Prinzip gleicher Anwendungsfall wie C, nur sind halt dann 
schon einige Sachen gelöst, die man sonst erst lösen müsste.

von Reinhard M. (reinhardm)


Bewertung
4 lesenswert
nicht lesenswert
> Als ich die Bücher in meinen Händen hielt und einfach nur etwas
> durchgeblättert habe wurde mir etwas schlecht.

Yo, also das "Real-Time C++" ist wohl das schlechteste Buch, was Du Dir 
antun konntest. Dient nur der Geldmacherei und ist eigentlich™ nur 
geeignet, Dich von C++ abzuschrecken, um es nie wieder anzufassen.
Bei "Real-Time C++" wird zudem nur plain C mit C++-Syntax gemacht. Ist 
nicht mehr als Augenwischerei. Also nicht mal ansatzweise das, was man 
von dem Buch erwartet :(

Gute Literatur ist leider extrem selten. Die meisten Autoren benutzen 
C++ zum Geld drucken. Es gibt viele Lernwillige, denen man das Geld aus 
der Tasche ziehen kann ...

Von meyers gibt es "effektives c++ in embedded systems" auch als pdf.

Der einzige Weg, C++ "richtig" zu lernen, ist meiner Ansicht nach der, 
ein Projekt auf- und umzusetzen. Dabei jeweils den Hut eines 
Bibliotheks-Entwicklers und eines Anwendungsentwicklers abwechselnd 
aufzusetzen.
... oder anders ausgedrückt: man sollte sich Gedanken machen, wie will 
ich die Klasse anwenden und dann wie kann ich die Anforderungen an die 
öffentliche Schnittstelle umsetzen. Letztlich müssen beide auf einander 
zugehen und Kompromisse eingehen.

Vor einer Weile habe ich meine C-Module in eine C++-Bibliothek 
überführt. Einhellige Meinung nach der Vorstellung hier: ist ja trivial 
und so kann man doch keine MCs programmieren. Viel zu aufgebläht. Eine 
C++-Bibliothek muss einfach templates enthalten.

Inzwischen habe ich die template-Variante der Bibliothek fast fertig und 
entgegen aller Prognosen scheint sich meine Vorahnung zu bestätigen: die 
Beispielanwendung wird mit templates fast genauso groß wie ohne :D :D :D

Ja, solange man sich nur kleine Beispiele anschaut, bleibt die 
template-Bibliothek kleiner, aber wenn man alle Funktionen haben will, 
dann wird die Codegröße nahezu identisch :)

Wenn ich jetzt beide Entwicklungsphasen vergleiche:

- die erste Variante (ohne templates) ließ sich problemlos erstellen, 
hat eine saubere öffentliche Schnittstelle, ist einfach zu benutzen und 
war schnell erstellt. Compiler-Meldungen führen zielsicher zum Fehler.

- die Entwicklung der template-Variante war haarsträubend und ich war 
mehrfach an einem Punkt, wo die compiler Meldungen keinen 
Erkenntnisgewinn mehr brachten und ich auf fremde Hilfe angewiesen war. 
Dann musste ich erkennen, dass manches von dem, was ich umsetzen wollte 
illegal ist und nicht von C++ unterstützt wird. Also wieder umdenken ...
Inzwischen ist die Bibliothek fast fertig. Der Gewinn an Typsicherheit 
ist marginal, wenn überhaupt vorhanden. Die Benutzerschnittstelle ist - 
sagen wir mal gewöhnungsbedürftig. Kein Vergleich mit der eingängigen 
Schnittstelle der Bibliothek ohne templates.
... und was die Wartbarkeit angeht: ein Tippfehler bei einem Template 
ergibt eine Orgie an Compilermeldungen, von denen in den seltensten 
Fällen die Meldung tatsächlich mit dem Problem zu tun hat, bzw. dabei 
hilft, dieses aufzuspüren. Man muss wirklich alle Fehler gemacht haben, 
um zu wissen, was der Compiler wirklich sagen will :(

Ich mach die Bibliothek zwar noch fertig, aber für mich ist jetzt schon 
klar, dass die templates ihre Daseinsberechtigung nicht ausspielen 
konnten. Wenn ich wirklich auf C++ setze, dann ist mir eine eingängige 
öffentliche Schnittstelle und die Wartbarkeit der Bibliothek wichtig. 
Wichtiger auf jeden Fall, als ein möglicher theoretischer Anspruch ...

Wenn die Theoretiker über meine Bibliothek die Nase rümpfen - dann ist 
das etwas, mit dem ich locker leben kann :)

von Rolf M. (rmagnus)


Bewertung
2 lesenswert
nicht lesenswert
Kaj G. schrieb:
> 1. Beispiel// C++ register access
> *reinterpret_cast<volatile std::uint8_t*>(port) ^= value;
>
> // Equivalent C-style
> *((volatile uint8_t*) port) ^= value;
>
> 2. Beispiel
> constexpr std::array<std::uint8_t, 9U> data =
> {{
>     UINT8_C(0x31), UINT8_C(0x32), UINT8_C(0x33),
>     UINT8_C(0x34), UINT8_C(0x35), UINT8_C(0x36),
>     UINT8_C(0x37), UINT8_C(0x38), UINT8_C(0x39),
> }};
> Hab ich was verpasst? Hab ich die Stelle überlesen an der steht, dass
> jetzt negativ Beispiele kommen?
> Entschuldigung, aber sowas ist weder lesbarer noch irgendwas. Im
> Gegenteil, es ist sogar sehr viel unleserlicher.
> Das einzige was hier besser werden mag, ist die Arbeit fuer den
> Compiler. Für jeden Entwickler, und jeden der sich solchen Code
> anschauen muss wird es schlechter.

Casts sollen hässlich aussehen, denn sie sind was hässliches und 
sollen nur verwendet werden, wenn unbedingt nötig. Des weiteren sind die 
C++-Casts nicht für den Compiler, sondern für den Programmierer so 
gemacht. Das Problem mit den C-Casts ist, dass sie aus einer Reihe 
möglicher Konvertierungen einfach die auswählen, die irgendwie aus dem 
Quelltyp den Zieltyp macht. Mit den C++-Casts kann man dagegen genauer 
ausdrücken, welche Konvertierung man wünscht. Das macht es klarer, was 
passiert und gibt bessere Möglichkeiten für Fehlermeldungen, wenn die 
Konvertierung so wie gewünscht nicht geht.

> Die erzwungene initialisierung?

Darum ging es meines Erachtens in diesem Beispiel nicht, sondern eher 
darum, zu zeigen, dass die Verwendung von auto natürlich eine 
Initialisierung voraussetzt, da sonst der Typ ja nicht automatisch 
erkannt werden kann. Wenn man die Variable initialisieren will, kann man 
das ja genauso ohne auto machen. Es sollte also nicht sagen: "Wenn du 
initialisieren willst, nimm auto", sondern "wenn du auto nimmst, musst 
du initialisieren".

> Jeder halbwegs vernünftige Compiler warnt, wenn man lesend auf eine
> nicht initialisierte Variable zugreift. Das ist für mich also kein
> Argument jetzt alles mit auto zuzupflastern. Bei Objekten mag das anders
> sein, aber bei primitiven Datentypen sehe ich keinen Vorteil.

Ich kann mir schon sinnvolle Anwendungsmöglichkeiten auch bei primitiven 
Typen vorstellen. Um beispielsweise nicht erst nachschauen zu müssen, ob 
eine Funktion jetzt einen int, einen unsigned long oder einen size_t 
zurückliefert. Oder um dafür gerüstet zu sein, dass eine Funktion im 
Programm, die heute noch einen 32-Bit-Integer zurückgibt, irgendwann auf 
64 Bit umgerüstet wird. Mit auto sagst du, dass das Ergebnis nicht in 
einen bestimmten an der Stelle festgelegten Typ kommt, sondern einfach 
in den richtigen.

lalala schrieb:
> (Deswegen haben Exceptions auch lange gebraucht anerkannt zu werden, da
> angeblich die Laufzeit langsamer wird. Fundierte Daten dazu habe ich
> jedoch nicht gefunden).

Das hängt von der Implementation ab. In den Anfängen war Code, der 
Exceptions unterstützt hat, wohl wirklich langsamer als ohne. Die heute 
verwendeten Mechanismen sind aber darauf ausgelegt, dass es keinen 
Overhead gibt, wenn keine Exception auftritt. Dafür ist es dann 
aufwändiger, eine Exception zu behandeln, wenn sie auftritt.

von lalala (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Rolf M. schrieb:
> Die heute verwendeten Mechanismen sind aber darauf ausgelegt, dass es
> keinen Overhead gibt, wenn keine Exception auftritt.

Gibt es dazu auch irgendwo detaillierte Infos?

von Piet (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Bernd K. schrieb im Beitrag #4824600:
> Na wunderbar. Hat ja nicht lange gedauert bis die alte Leier wieder
> anfängt.

Was wahr ist ist wahr.
Ob sich der ganze C(++) Zirkus nämlich wirklich lohnt hängt ganz 
empfindlich von Hardware-Architektur und der zu erstellenden 
Software-Projektgrößen/typen ab. Es soll ja nicht bloß darauf 
hinauslaufen, daß man die Zeit, die man mit den vielen 'modernen' 
Sprachmitteln einzusparen imstande ist in gleichem Umfang vorab und in 
steter Weiterbildung in die Beherrschung des ganzen 
Sprach-Instrumentariums stecken muß. Da lohnt sich schon ein wenig 
Hinterfragen nach Sinn und Zweck, zumindest für den, der sich das 
leisten kann :)

von Rolf M. (rmagnus)


Bewertung
2 lesenswert
nicht lesenswert
lalala schrieb:
> Rolf M. schrieb:
>> Die heute verwendeten Mechanismen sind aber darauf ausgelegt, dass es
>> keinen Overhead gibt, wenn keine Exception auftritt.
>
> Gibt es dazu auch irgendwo detaillierte Infos?

Suche mal nach Stichwörtern wie "sjlj", "dwarf-2 exception" oder 
"zero-cost exception handling". Direkt parat hab ich nichts, weil das 
schon ein paar Jahre her ist, dass ich das gelesen hab.

von Kaj G. (Firma: RUB) (bloody)


Bewertung
0 lesenswert
nicht lesenswert
Ich hab ja kein Grundsaetzliches Problem mit C++, aber was mir da als 
"besser" verkauft werden soll...
1
constexpr std::array<std::uint8_t, 9U> data =
2
{{
3
    UINT8_C(0x31), UINT8_C(0x32), UINT8_C(0x33),
4
    UINT8_C(0x34), UINT8_C(0x35), UINT8_C(0x36),
5
    UINT8_C(0x37), UINT8_C(0x38), UINT8_C(0x39),
6
}};

Wenn ich mir mal dieses ominoese UINT8_C() anschaue dann, dann ist dass 
nicht mehr als:
1
# define UINT8_C(c)  c
Bei aller Liebe, aber es erschliesst sich mir nicht im geringsten, 
weshalb man UINT8_C(0x31) anstatt einfach 0x31 schreiben sollte...

Das ist vielleicht auch einfach nur ein unglaublich schlechtes Beispiel, 
keine Ahnung.

Bei dem Beispiel von Yalu kann ich sehr wohl einen Mehrwert an 
Lesbarkeit erkennen. Auch andere neue Features wie das "if mit 
initialisierung" halte ich fuer tatsaechlich sinnvoll.

Reinhard M. schrieb:
> Yo, also das "Real-Time C++" ist wohl das schlechteste Buch, was Du Dir
> antun konntest. Dient nur der Geldmacherei und ist eigentlich™ nur
> geeignet, Dich von C++ abzuschrecken, um es nie wieder anzufassen.
Ja, das schafft das Buch tatsaechlich sehr gut. :(

Reinhard M. schrieb:
> Gute Literatur ist leider extrem selten.
"Der C++-Programmierer" von Ulrich Breymann finde ich gar nicht mal so 
schlecht.


Mir ist klar, dass C++ mehr ist, als die hier aufgefuehrten Beispiele. 
Buecher wie die beiden gennanten ("Real-Time C++", "Effektives modernes 
C++") vermitteln mir aber mehr den Eindruck, dass "moderner, effektiver" 
Code in C++ haesslich sein muss.

Vielleicht verzichte ich einfach auf das "modern und effektiv".
Bei "moderner Kunst" streiten sich die Geister ja auch, ob das nun Kunst 
oder Kompost ist ;)

von Vincent H. (vinci)


Bewertung
1 lesenswert
nicht lesenswert
Kaj G. schrieb:
> Ich werde mit C++ einfach nicht warm...

Du nutzt C++ schlichtweg falsch. Du versuchst mit einem Sportwagen die 
Kartoffelernte einzufahren.

1.) Ja der Cast ist leserlicher, da er mir den Typ des Casts verrät.

2.) Constexpr Daten, die sich durch einen Algorithmus abbilden lassen, 
schreibt man nicht von Hand.

3.) Auto glänz nicht beim Anlegen primitiver Typen, siehe yalus post.



Reinhard M. schrieb:
> Wenn die Theoretiker über meine Bibliothek die Nase rümpfen - dann ist
> das etwas, mit dem ich locker leben kann :)

Hah ja, diese Trotteln!
Wie diese ganzen Theoretiker, die die standard TEMPLATE library 
nutzen...

von (prx) A. K. (prx)


Bewertung
3 lesenswert
nicht lesenswert
Kaj G. schrieb:
> # define UINT8_C(c)  c

Das hat reinweg nichts mit C++ zu tun.

Zumal Stroustrup ein Gegner des Präprozessors ist. Innige Verwendung 
desselbigen widerspricht eigentlich dem Grundmotiv von C++. Was die 
Leute dann daraus machen ist ein anderes Thema.

: Bearbeitet durch User
von Kaj G. (Firma: RUB) (bloody)


Bewertung
0 lesenswert
nicht lesenswert
Vincent H. schrieb:
> Du nutzt C++ schlichtweg falsch.

A. K. schrieb:
> Das hat reinweg nichts mit C++ zu tun.
Ich hab hier nur Beispiele aus den genannten Buechern gebracht, die mir 
ja angeblich besseres, modernes C++ zeigen wollen (und das Geld aus der 
Tasche ziehen).

A. K. schrieb:
> Zumal Stroustrup ein Gegner des Präprozessors ist. Innige Verwendung
> desselbigen widerspricht eigentlich dem Grundmotiv von C++.
Okay, wenigstens etwas gelernt. :)

von Torsten R. (Firma: Torrox.de) (torstenrobitzki)


Bewertung
1 lesenswert
nicht lesenswert
Hai Kai,

Kaj G. schrieb:
> Guten Morgen, ich wünsche allen einen angenehmen dritten Advent :)
>
> Aufgrund der gar nicht mal so wenigen Threads hier zu Themen wie "C++
> auf Mikrocontrollern", "C vs.C++", usw. und ich auch gerade im Zuge
> meines Studiums (mehr oder minder) dazu gezwungen bin mich mal wieder
> mit C++ zu beschaeftigen, dachte ich mir:

sich "gezwungen" zu fühlen ist schon mal schlecht.

> Um meine etwas eingerosteten Kenntnisse aufzufrischen hab ich mir ein
> paar Bücher besorgt, z.B. "Real-Time C++" und "Effektives modernes C++".

Klingt nach einer "spannenden" Mischung. Bei C++ sollte man (wie bei 
jeder anderen Sprache auch) vorne anfangen. Die beiden Bücher fangen 
aber bestimmt nicht vorne an.

> Als ich die Bücher in meinen Händen hielt und einfach nur etwas
> durchgeblättert habe wurde mir etwas schlecht.
>
1
> // C++ register access
2
> *reinterpret_cast<volatile std::uint8_t*>(port) ^= value;
3
> 
4
> // Equivalent C-style
5
> *((volatile uint8_t*) port) ^= value;
6
>

Der Code hat fast keinen Unterschied, wenn man ihn mal genauer anguckt. 
Der Unterschied zwischen C und C++ ist hier nur, dass man in C++ etwas 
genauer angeben kann, an welcher Stelle man das Typensystem aushebeln 
möchte. Störst Du dich jetzt wirklich an dem Schlüsselwort 
"reinterpret_cast"?


> Gegenteil, es ist sogar sehr viel unleserlicher.

Keiner hindert Dich daran, in C++ ein konstantes Array zu definieren. 
std::array<> verwendet man typischerweise, wenn die Länge des Arrays mit 
in den Typen rein spielen soll, oder man den "Komfort" einer Klasse an 
der Stelle haben möchte (z.B. data.size() anstatt 
sizeof(data)/sizeof(data[0]).

Du must es nicht nutzen. In C++ wurden Arrays nicht abgeschafft.

> Das einzige was hier besser werden mag, ist die Arbeit fuer den
> Compiler. Für jeden Entwickler, und jeden der sich solchen Code
> anschauen muss wird es schlechter.

Ist eine Frage der Gewohnheit. Und man muss es natürlich wollen.

> Ein 3. Beispiel: auto
> Da wird dann als erstes dieses Beispiel gebracht:
>
1
> int  x1;        // potenziell nicht initialisiert
2
> auto x2;        // Fehler! Initialisierung notwenig
3
> auto x3 = 0;    // Gut, wohl definierter Wert
4
>
> Okay, auto mag Vorteile habe, aber bei einem primitiven Datentyp sehe
> ich die nicht. Die erzwungene initialisierung?

Das ist aber kein Problem der Sprache C++ sondern, der Bücher, die Du 
ließt. Möchte ich den Typen explizit ausdrücken, oder möchte ich 
insbesondere sogar sicher sein, dass ein Ausdruck einen bestimmten Typen 
hat, dann schreibe ich ihn hin. Wenn es nervig zu tippender, langer Text 
ist, dann schreibe ich `auto`. In C++ darf man übrigens nach wie vor den 
Typen explizit benennen (wurde nicht abgeschafft in C++11).


> Ich geb mir wirklich Mühe C++ besser zu verstehenund und zu nutzen. Wenn
> mir aber sowas als "effektiv und modern", womöglich sogar als hip,
> verkauft werden soll,...

Nein, Du hast das Gefühl, dich mit dem Thema beschäftigen zu müssen und 
suchst jetzt nach Argumenten, Deine Vorurteile zu bestätigen. Das ist 
eine Strategie, die jeder von uns hier und da verwendet, die einen aber 
nicht weiter bringt.

> Nicht falsch verstehen: Ich erkenne an, dass C++ gute Vorteile gegenüber
> C hat. Aber ich habe das Gefühl, dass C++ einfach nur noch aufgeblasen
> und überladen bis zum geht nicht mehr ist. :(

Dann nutze doch einfach nur die Teile, die Du für Deine Arbeit als 
Vorteilhaft erkannt hast. Bleib neugierig, bleib kritisch. Es gibt auch 
extrem viele schlechte Bücher über C++ (wobei "Effektives modernes C++" 
sicher nicht dazu gehört, aber auch nix ist, mit dem man anfängt).

> Ich werde mit C++ einfach nicht warm...

wird schon! ;-)

mfg Torsten

von Linus (Gast)


Bewertung
-7 lesenswert
nicht lesenswert
Kaj G. schrieb:
> Okay, wenigstens etwas gelernt. :)

Wohl kaum. Jedenfalls ist davon nichts zu sehen.

Manche sind leider interlektuell zu sehr herausgefordert, als dass sie 
mit C oder C++ zurechtkommen könnten. Für euch gibt es VB, Excel, 
Simulink u.ä., oder einfach Sozial"wissenschaften", Banklehre, Kassierer 
und andere schöne Berufe, bei denen du nichts begreifen musst.

von Rolf M. (rmagnus)


Bewertung
0 lesenswert
nicht lesenswert
Kaj G. schrieb:
> Wenn ich mir mal dieses ominoese UINT8_C() anschaue dann, dann ist dass
> nicht mehr als:
> # define UINT8_C(c)  c

Du weißt aber, dass das eigentlich ein C-Feature ist, dass C++ nur 
übernommen hat? Du solltest dich also diesbezüglich eher über C 
beschweren als über C++.
Im übrigen ist deine Aussage falsch. 0x31 ist vom Typ int. UINT8_C(0x31) 
ist dagegen vom Typ uint8_t.

von Vincent H. (vinci)


Bewertung
0 lesenswert
nicht lesenswert
Kaj G. schrieb:
> A. K. schrieb:
>> Das hat reinweg nichts mit C++ zu tun.
> Ich hab hier nur Beispiele aus den genannten Buechern gebracht, die mir
> ja angeblich besseres, modernes C++ zeigen wollen (und das Geld aus der
> Tasche ziehen).


Effective Modern C++ ist eine Bibel. Das ist der Tietze-Schenk der C++ 
Programmierung und mit Sicherheit eines der besten C++ Bücher überhaupt.

Zum Thema "auto" steht in besagtem Buch als erstes Beispiel übrigens das 
hier:
1
template<typename It>
2
// algorithm to dwim ("do what I mean")
3
void dwim(It b, It e)
4
// for all elements in range from
5
{
6
// b to e
7
while (b != e) {
8
typename std::iterator_traits<It>::value_type
9
currValue = *b;
10
...
11
}
12
13
template<typename It>
14
void dwim(It b, It e)
15
{
16
while (b != e) {
17
auto currValue = *b;
18
...
19
}
20
}

von Bernd K. (prof7bit)


Bewertung
2 lesenswert
nicht lesenswert
Piet schrieb:
> Ob sich der ganze C(++) Zirkus nämlich wirklich lohnt hängt ganz
> empfindlich von Hardware-Architektur

Für die meisten heute gängigen Architekturen gibts C und meist auch C++ 
Compiler.

von Torsten R. (Firma: Torrox.de) (torstenrobitzki)


Bewertung
2 lesenswert
nicht lesenswert
Rolf M. schrieb:
> Kaj G. schrieb:
>> # define UINT8_C(c)  c
>

> Im übrigen ist deine Aussage falsch. 0x31 ist vom Typ int. UINT8_C(0x31)
> ist dagegen vom Typ uint8_t.

Nicht bei der obigen Definition von UINT8_C. ;-)

von Kaj G. (Firma: RUB) (bloody)


Bewertung
1 lesenswert
nicht lesenswert
Vincent H. schrieb:
> Zum Thema "auto" steht in besagtem Buch als erstes Beispiel übrigens das
> hier:
In meinem Buch steht genau zwischen diesen beiden Codeschnipseln mein 
zitiertes Beispiel.

Rolf M. schrieb:
> Im übrigen ist deine Aussage falsch. 0x31 ist vom Typ int. UINT8_C(0x31)
> ist dagegen vom Typ uint8_t.
Und welche Aussage soll falsch sein? Das #define hab ich 1:1 aus der 
stdint.h kopiert.
Dann erklaer mir doch bitte an welcher stelle da ein uint8_t drauss 
wird. Einen Cast sehe ich da nicht. Alles was ich da sehe ist ein Makro, 
dass HansPeter heisst, und HansPeter moechte einen Apfel haben.


Torsten R. schrieb:
> Bei C++ sollte man (wie bei
> jeder anderen Sprache auch) vorne anfangen.
Ich fang nicht bei 0 an, keine Sorge. :)
Vielleicht muss man auch erst 10 Jahre C++ programmiert haben, damit 
sich einem diese Buecher erschliessen :-/

von Allu (Gast)


Bewertung
1 lesenswert
nicht lesenswert
Kaj G. schrieb:
> nd ich auch gerade im Zuge
> meines Studiums (mehr oder minder) dazu gezwungen bin mich mal wieder
> mit C++ zu beschaeftigen

Ob es einen gefällt oder nicht, es ist in diesen Jahren eine Sprache die 
im Arbeitsleben oft gefordert wird. Da Du noch jung bist sollte dir das 
erlernen nicht ganz so schwer fallen.

Also für "Profis" C, C++ usw. und für alle anderen Spassprogrammierer 
gibt es Bascom um gute und lesbare Progrogramme zu schreiben.

von Vincent H. (vinci)


Bewertung
0 lesenswert
nicht lesenswert
Kaj G. schrieb:
> Vincent H. schrieb:
>> Zum Thema "auto" steht in besagtem Buch als erstes Beispiel übrigens das
>> hier:
> In meinem Buch steht genau zwischen diesen beiden Codeschnipseln mein
> zitiertes Beispiel.

Du verstehst ja nicht einmal, dass diese 3 Zeilen Code primär ein 
Beispiel von type deduction (oder besser gesagt der nicht vorhandenen 
type deduction ohne type) sind, nicht von auto...

von Rolf M. (rmagnus)


Bewertung
0 lesenswert
nicht lesenswert
Kaj G. schrieb:
> Rolf M. schrieb:
>> Im übrigen ist deine Aussage falsch. 0x31 ist vom Typ int. UINT8_C(0x31)
>> ist dagegen vom Typ uint8_t.
> Und welche Aussage soll falsch sein? Das #define hab ich 1:1 aus der
> stdint.h kopiert.

Aus deiner stdint.h. Das scheint dann wohl ein Bug in der stdint.h zu 
sein. Es müsste eigentlich der kleinstmögliche vorzeichenlose Typ sein, 
der mindestens 8 Bit breit ist, also in der Regel uint8_t. Bei deiner 
stdint.h wird das offenbar überhaupt nicht berücksichtigt.
Die Definition in der avr-libc sieht eher so aus, wie ich mir die 
vorstellen würde:
1
#define UINT8_C(value) ((uint8_t) __CONCAT(value, U))

von Kaj G. (Firma: RUB) (bloody)


Bewertung
0 lesenswert
nicht lesenswert
Rolf M. schrieb:
> Aus deiner stdint.h.
Naja, aus der die unter /usr/include liegt.

Hier wirds genauso beschrieben:
http://tigcc.ticalc.org/doc/stdint.html#UINT8_C

Das es anders sein sollte sehe ich ja ein, ist es aber nicht.
UINT32_C() sieht so aus:
1
# define UINT32_C(c)  c ## U
Okay, das leuchtet mir halbwegs ein.

Ob das mit dem UINT8_C() jetzt wirklich ein "Bug" ist kann ich nicht 
beurteilen.

von Wilhelm M. (wimalopaan)


Bewertung
0 lesenswert
nicht lesenswert
Kaj G. schrieb:
> Guten Morgen, ich wünsche allen einen angenehmen dritten Advent :)

> 1. Beispiel
>
1
> // C++ register access
2
> *reinterpret_cast<volatile std::uint8_t*>(port) ^= value;
3
> 
4
> // Equivalent C-style
5
> *((volatile uint8_t*) port) ^= value;
6
>

Hier ist es doch wunderbar, dass der cast dort steht, vielleicht sogar 
noch in leuchtendem Pink in Deiner IDE.

> 2. Beispiel
>
1
> constexpr std::array<std::uint8_t, 9U> data =
2
> {{
3
>     UINT8_C(0x31), UINT8_C(0x32), UINT8_C(0x33),
4
>     UINT8_C(0x34), UINT8_C(0x35), UINT8_C(0x36),
5
>     UINT8_C(0x37), UINT8_C(0x38), UINT8_C(0x39),
6
> }};
7
>

Dieser Beispiel ist wirklich absurd.

Entweder:
1
constexpr std:array<uint8_t, 9> = {{1, 2, 3}};

oder
1
constexpr int foo(size_t v) {
2
    return v + 0x30;
3
}
4
5
template<typename F, size_t... II>
6
constexpr auto createArray(F f, std::index_sequence<II...>) {
7
    return std::array<int, sizeof...(II)> {{f(II)...,}};
8
}
9
        
10
int main()
11
{
12
    constexpr auto a = createArray(foo, std::make_index_sequence<10>{});
13
    
14
    std::copy(std::begin(a), std::end(a), std::ostream_iterator<double>(std::cout, ", "));
15
}



> Ein 3. Beispiel: auto
> Da wird dann als erstes dieses Beispiel gebracht:
>
1
> int  x1;        // potenziell nicht initialisiert
2
> auto x2;        // Fehler! Initialisierung notwenig
3
> auto x3 = 0;    // Gut, wohl definierter Wert
4
>

Auch dieses Beispiel ist absurd. Aber oben siehst Du eine Anwendung von 
auto, die sinnvoll ist.

von Roland F. (rhf)


Bewertung
3 lesenswert
nicht lesenswert
Vincent H. schrieb:

> Effective Modern C++ ist eine Bibel. Das ist der Tietze-Schenk der C++
> Programmierung und mit Sicherheit eines der besten C++ Bücher überhaupt.

Und was ist mit

   Bjarne Stroustrup, Einführung in die Programmierung mit C++

vom Erfinder der Sprache?

rhf

von Rolf M. (rmagnus)


Bewertung
1 lesenswert
nicht lesenswert
Kaj G. schrieb:
> Ob das mit dem UINT8_C() jetzt wirklich ein "Bug" ist kann ich nicht
> beurteilen.

Ich würde sagen, dass es einer ist.
Im C-Draft unter 
http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1256.pdf
steht folgendes:

7.18.4.1 Macros for minimum-width integer constants

The macro INTN_C(value) shall expand to an integer constant expression
corresponding to the type int_leastN_t. The macro UINTN_C(value) shall 
expand
to an integer constant expression corresponding to the type 
uint_leastN_t. For example, if uint_least64_t is a name for the type 
unsigned long long int,
then UINT64_C(0x123) might expand to the integer constant 0x123ULL.

von (prx) A. K. (prx)


Bewertung
2 lesenswert
nicht lesenswert
Mit uint_least8_t == int wäre es zumindest bei UINT_C(1) korrekt. 
Allerdings nicht bei UINT8_C(1UL). Von den fehlenden Klammern ganz 
abgesehen. So führt
  #define UINT8_C(c) c
bei
  UINT8_C(1+2)*UINT8_C(3)
nicht etwa zu ((1+2)*3)=9, sondern zu (1+2*3)=7.

Da war kein Meister seines Faches am Werk.

: Bearbeitet durch User
von Wilhelm M. (wimalopaan)


Bewertung
0 lesenswert
nicht lesenswert
Genau deswegen sollte man eben den Cpp und hauptsächlich die 
Parameter-Makros des Cpp endgültig verbannen ...

von Kaj G. (Firma: RUB) (bloody)


Bewertung
0 lesenswert
nicht lesenswert
Na, dann hat das ganze hier ja immerhin den netten Effekt das wir einen 
Bug bzw. ein etwas ungleucklich formuliertes Stueck Code gefunden haben 
:)

Aber die anderen defines sind dann ja auch nicht wirklich besser, da da 
ja auch die Klammern fehlen.
1
/* Signed.  */
2
# define INT8_C(c)  c
3
# define INT16_C(c)  c
4
# define INT32_C(c)  c
5
# if __WORDSIZE == 64
6
#  define INT64_C(c)  c ## L
7
# else
8
#  define INT64_C(c)  c ## LL
9
# endif
10
11
/* Unsigned.  */
12
# define UINT8_C(c)  c
13
# define UINT16_C(c)  c
14
# define UINT32_C(c)  c ## U
15
# if __WORDSIZE == 64
16
#  define UINT64_C(c)  c ## UL
17
# else
18
#  define UINT64_C(c)  c ## ULL
19
# endif
20
21
/* Maximal type.  */
22
# if __WORDSIZE == 64
23
#  define INTMAX_C(c)  c ## L
24
#  define UINTMAX_C(c)  c ## UL
25
# else
26
#  define INTMAX_C(c)  c ## LL
27
#  define UINTMAX_C(c)  c ## ULL
28
# endif

von Rolf M. (rmagnus)


Bewertung
0 lesenswert
nicht lesenswert
A. K. schrieb:
> Mit uint_least8_t == int wäre es zumindest bei UINT_C(1) korrekt.

uint_least8_t darf aber nicht int sein, da int vorzeichenbehaftet ist. 
Und dass auf Kajs Plattform int der kleinste Integertyp ist, ist zwar 
nicht unmöglich, aber doch eher unwahrscheinlich.

> Allerdings nicht bei UINT8_C(1UL).

Die Implementation in der avr-libc würde damit nicht mal compilieren, 
denn es käme nach der Makro-Auflösung
1
((uint8_t) 1ULU)
raus, und ULU ist kein gültiger Suffix.

von Kaj G. (Firma: RUB) (bloody)


Bewertung
0 lesenswert
nicht lesenswert
Meine Platform ist nur ein ganz normales Arch Linux mit aktuellem Kernel 
4.8.12, gcc-multilib 6.2.1-1 und glibc 2.24-2.

von (prx) A. K. (prx)


Bewertung
0 lesenswert
nicht lesenswert
Rolf M. schrieb:
> uint_least8_t darf aber nicht int sein, da int vorzeichenbehaftet ist.

Stimmt.

> Und dass auf Kajs Plattform int der kleinste Integertyp ist, ist zwar
> nicht unmöglich, aber doch eher unwahrscheinlich.

Bei DSPs mag es das geben.

: Bearbeitet durch User
von Rolf M. (rmagnus)


Bewertung
0 lesenswert
nicht lesenswert
Mir ist grad aufgefallen, dass es bei der avr-libc-Implementation auch 
Probleme wegen eines fehlenden Klammernpaares geben kann:
Aus
1
UINT8_C(1+2)
wird
1
((uint8_t) 1+2U)

Damit ist am Ende der Datentyp unsigned int.

von (prx) A. K. (prx)


Bewertung
0 lesenswert
nicht lesenswert
Kaj G. schrieb:
> Meine Platform ist nur ein ganz normales Arch Linux mit aktuellem Kernel
> 4.8.12, gcc-multilib 6.2.1-1 und glibc 2.24-2.

Als Zielplattform? Wieso steht oben dann "tigcc"?

von Kaj G. (Firma: RUB) (bloody)


Bewertung
0 lesenswert
nicht lesenswert
A. K. schrieb:
> Als Zielplattform? Wieso steht oben dann "tigcc"?
Das ist nur eine Seite wo ich was auf die schnelle dazu gefunden habe.

von (prx) A. K. (prx)


Bewertung
0 lesenswert
nicht lesenswert
Kaj G. schrieb:
> A. K. schrieb:
>> Als Zielplattform? Wieso steht oben dann "tigcc"?
> Das ist nur eine Seite wo ich was auf die schnelle dazu gefunden habe.

Na super. Also komplette Verarsche. Danke.

In /usr/include zu suchen wär zuviel Arbeit gewesen?

: Bearbeitet durch User
von Kaj G. (Firma: RUB) (bloody)


Bewertung
0 lesenswert
nicht lesenswert
A. K. schrieb:
> Na super. Also komplette Verarsche. Danke.
Warum? Was aendert das denn am Problem?

von Kaj G. (Firma: RUB) (bloody)


Bewertung
0 lesenswert
nicht lesenswert
A. K. schrieb:
> In /usr/include zu suchen wär zuviel Arbeit gewesen?

Lesen...

Kaj G. schrieb:
> Rolf M. schrieb:
>> Aus deiner stdint.h.
> Naja, aus der die unter /usr/include liegt.

von (prx) A. K. (prx)


Bewertung
1 lesenswert
nicht lesenswert
Ok, jetzt wird es interessant. In /usr/include/stdint.h steht das hier 
nämlich auch so drin. Vorausgesetzt es ist C am Werk, nicht C++.

von Yalu X. (yalu) (Moderator)


Bewertung
1 lesenswert
nicht lesenswert
Rolf M. schrieb:
> Kaj G. schrieb:
>> Ob das mit dem UINT8_C() jetzt wirklich ein "Bug" ist kann ich nicht
>> beurteilen.
>
> Ich würde sagen, dass es einer ist.
> Im C-Draft unter
> http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1256.pdf
> steht folgendes:
>
> 7.18.4.1 Macros for minimum-width integer constants
>
> The macro INTN_C(value) shall expand to an integer constant expression
> corresponding to the type int_leastN_t. The macro UINTN_C(value) shall
> expand
> to an integer constant expression corresponding to the type
> uint_leastN_t.

Du musst auch den Abschnitt darüber lesen:

1
Each invocation of one of these macros shall expand to an integer
2
constant expression suitable for use in #if preprocessing directives.
3
The type of the expression shall have the same type as would an
4
expression of the corresponding type converted according to the integer
5
promotions.

Die Integer-Promotion macht aus uint_least8_t ein int. Also muss
UINT8_C() ein int liefern. Das tut es, indem sie die als Argument
übergebene Integer-Konstante unverändert stehen lässt.

A. K. schrieb:
> Mit uint_least8_t == int wäre es zumindest bei UINT_C(1) korrekt.
> Allerdings nicht bei UINT8_C(1UL).

1UL (mit dem UL-Suffix) ist kein erlaubtes Argument für das
UINT8_C-Makro:

1
The argument in any instance of these macros shall be an unsuffixed
2
integer constant (as defined in 6.4.4.1) with a value that does not
3
exceed the limits for the corresponding type.

> Von den fehlenden Klammern ganz
> abgesehen. So führt
>   #define UINT8_C(c) c
> bei
>   UINT8_C(1+2)*UINT8_C(3)
> nicht etwa zu ((1+2)*3)=9, sondern zu (1+2*3)=7.

Dto.: Es muss eine Integer-Konstante übergeben werden. Ein Integer-
Ausdruck, selbst wenn er einen konstanten Wert hat, ist nicht erlaubt.

Der Sinn dieser Makros liegt darin, einer Integer-Konstanten den zu den
in stdint.h definierten Integer-Typen jeweils passenden Suffix (L, LL,
U, UL, ULL oder auch überhaupt keinen) anzuhängen. Das ist etwas
Anderes, als einen Ausdruck in einen dieser Integer-Typen zu casten.
Deswegen würden eine Konstante, die bereits einen Suffix hat, oder ein
Ausdruck als Argument überhaupt keinen Sinn ergeben.

Rolf M. schrieb:
> Die Definition in der avr-libc sieht eher so aus, wie ich mir die
> vorstellen würde:
> #define UINT8_C(value) ((uint8_t) __CONCAT(value, U))

DAS ist IMHO ein Bug, weil hier ein Wert vom Typ uint8_t statt int
zurückgegeben wird. Bei neueren GCC-Versionen sind diese Macros mit dem
Präfix __ bereits im Präprozessor vordefiniert. In stdint.h steht dann:

1
#define UINT8_C(c) __UINT8_C(c)

Und __UINT8_C verhält sich wiederum wie

1
#define __UINT8_C(c) c

Da kommt dann auch das richtige Ergebnis heraus.

: Bearbeitet durch Moderator
von (prx) A. K. (prx)


Bewertung
1 lesenswert
nicht lesenswert
Yalu X. schrieb:
> 1UL (mit dem UL-Suffix) ist kein erlaubtes Argument für das
> UINT8_C-Makro:

Ok, so wird ein Schuh draus. Aber es bleibt eine böse Falle. Würde ich 
lieber einen Bogen drum machen, soweit möglich. Zu riskant, dass man das 
missversteht.

: Bearbeitet durch User
von Kaj G. (Firma: RUB) (bloody)


Bewertung
0 lesenswert
nicht lesenswert
Yalu X. schrieb:
> Each invocation of one of these macros shall expand to an integer
> constant expression suitable for use in #if preprocessing directives.
Das heisst, dass dieses Makro gar nicht zur Verwendung in normalem Code, 
gedacht ist, sondern nur zur Verwendung in #if gedacht ist?

von Yalu X. (yalu) (Moderator)


Bewertung
0 lesenswert
nicht lesenswert
Kaj G. schrieb:
> Das heisst, dass dieses Makro gar nicht zur Verwendung in normalem Code,
> gedacht ist, sondern nur zur Verwendung in #if gedacht ist?

Nicht nur, aber auch. Der Präprozessor hat gegenüber dem Compiler nur
eingeschränkte Rechenfähigkeiten. Damit das Makro auch in #if-Direktiven
verwendet werden kann, darf der Ausdruck, den UINT8_C() liefert, nicht
allzu kompliziert werden. In der Praxis ist das aber kein Problem, da
das Makro bei praktisch allen Implementationen einfach nur einen Suffix
(L, U usw.) an die Integer-Konstante anhängt. Da gibt es sowieso nichts
zu rechnen.

von Rolf M. (rmagnus)


Bewertung
1 lesenswert
nicht lesenswert
Yalu X. schrieb:
> Du musst auch den Abschnitt darüber lesen:
>
> Each invocation of one of these macros shall expand to an integer
> constant expression suitable for use in #if preprocessing directives.
> The type of the expression shall have the same type as would an
> expression of the corresponding type converted according to the integer
> promotions.

Ah ok, dann sieht das tatsächlich anders aus. Ist aber auch etwas 
missverständlich. Ich hätt's doch mal ganz lesen sollen.

> Die Integer-Promotion macht aus uint_least8_t ein int. Also muss
> UINT8_C() ein int liefern. Das tut es, indem sie die als Argument
> übergebene Integer-Konstante unverändert stehen lässt.

Dann muss ich Kaj Recht geben. Die Verwendung des Makros UINT8_C ist 
dann eigentlich komplett sinnlos, da es auf keinem konformen Compiler 
irgendwas tut. Es existiert dann wohl nur der Vollständigkeit halber.

> Der Sinn dieser Makros liegt darin, einer Integer-Konstanten den zu den
> in stdint.h definierten Integer-Typen jeweils passenden Suffix (L, LL,
> U, UL, ULL oder auch überhaupt keinen) anzuhängen. Das ist etwas
> Anderes, als einen Ausdruck in einen dieser Integer-Typen zu casten.

Ja, das ergibt Sinn. Ich hatte nicht damit gerechnet, dass die Integer 
Promotion dort wirken soll, sondern dass der Typ wirklich entsprechend 
dem Namen des Makros sein soll. Zugegebenermaßen wüsste ich aber nicht, 
wo das in C überhaupt eine Rolle spielen würde - anders als in C++.

von Reinhard M. (reinhardm)


Bewertung
2 lesenswert
nicht lesenswert
> Reinhard M. schrieb:
>> Wenn die Theoretiker über meine Bibliothek die Nase rümpfen - dann ist
>> das etwas, mit dem ich locker leben kann :)
>
> Hah ja, diese Trotteln!
> Wie diese ganzen Theoretiker, die die standard TEMPLATE library
> nutzen...

Nö, so war das nicht gemeint. Wer C++ machen will und die STL verwenden 
kann, der kann ja schon fast von Schlaraffenland sprechen.

Wenn man z.B. bei einem AVR C++ nehmen will, dann gibt es keine STL - 
und wenn man dann an so ein Verarschungsbuch wie "Real-Time C++" gerät, 
dann kommt es eben zu den hier genannten Phänomenen.
Ich finde es nicht richtig, Kaj dafür abzuwatschen, dass er über den 
Quatsch aus dem Buch frustriert ist.

Ein weiterer Grund, die UINTxx Makros nicht zu verwenden, ist das 
"versteckte" Anhängen des U bzw. UL.

Gerade im Mikrocontroller-Umfeld werden viele Register mit benannten 
Bitwerten beschrieben, die verodert eine Zahl ergeben. Jeder kennt die 
Configurationsanweisungen.

Packt man diese in so ein UINTxx-Makro, dann erhält man die schönsten 
Compiler-Fehlermeldungen. Genau. Wegen dem plöden Anhängen von U oder 
UL.
Verwendet man an dieser Stelle statt dem UINTxx-Makro einen static_cast, 
dann sieht man sehr schön, welcher Typ verwendet werden soll und der 
Inhalt der Klammern bei static_cast wird auch noch richtig ausgewertet.
Das wäre also wieder ein Punkt für "echtes" C++ :D

Klar ist Strousoup kein Freund des Präprozessors - nur wer eine 
Bibliothek für mehrere Zielsysteme entwickelt, kommt nicht ohne aus.
Jedoch kann man den Präprozessor auch sinnvoll einsetzen und nicht so 
willkürlich, wie in besagtem Buch.

Meine Bibliothek habe ich für AVR und stm32 geschrieben. Da wird es dann 
schnell haarig, weil man jede Funktion, die man beim stm32 aus der STL 
verwenden könnte, beim AVR nach implementieren muss. Selbst der 
C-Standard ist nicht einheitlich für beide Plattformen, sodass man ohne 
Präprozessor viel unnötige Schreibarbeit leisten muss. In so einer 
Situation muss man sehr sorgfältig abwägen, ob man die STL verwendet, 
oder gleich die Funktionen selbst schreibt.

von Rolf M. (rmagnus)


Bewertung
0 lesenswert
nicht lesenswert
Reinhard M. schrieb:
> Ein weiterer Grund, die UINTxx Makros nicht zu verwenden, ist das
> "versteckte" Anhängen des U bzw. UL.

Was heißt "versteckt"? Die Makros dienen dem Umgang mit den Typen 
uint*_t bzw. uint_least*_t. Bei denen hat man ja das Problem, dass es im 
Gegensatz zu Typen wie int und long keine plattformunabhängige Zuordnung 
zu den Suffixen gibt. Muss ich für einen uint32_t jetzt z.B. UL anhängen 
oder bekomme ich dann einen 64-Bit-Wert? Hänge ich nur ein U an, bekomme 
ich vielleicht nur 16 Bit. Das ist compiler- und plattformabhängig. Das 
ist ähnlich wie bei den PRi*/PRu*-Makros für die Formatstrings.

> Packt man diese in so ein UINTxx-Makro, dann erhält man die schönsten
> Compiler-Fehlermeldungen. Genau. Wegen dem plöden Anhängen von U oder
> UL.

Die Makros sind genau dazu da, das anzuhängen. Es gibt also eigentlich 
keinen Grund, sie dort zu verwenden, wo man das nicht will.

: Bearbeitet durch User
von Karl Käfer (Gast)


Bewertung
3 lesenswert
nicht lesenswert
Kaj G. schrieb:
> Guten Morgen, ich wünsche allen einen angenehmen dritten Advent :)

Danke, Dir auch.

> 1. Beispiel
>
1
> // C++ register access
2
> *reinterpret_cast<volatile std::uint8_t*>(port) ^= value;
3
> 
4
> // Equivalent C-style
5
> *((volatile uint8_t*) port) ^= value;
6
>
>
> 2. Beispiel
>
1
> constexpr std::array<std::uint8_t, 9U> data =
2
> {{
3
>     UINT8_C(0x31), UINT8_C(0x32), UINT8_C(0x33),
4
>     UINT8_C(0x34), UINT8_C(0x35), UINT8_C(0x36),
5
>     UINT8_C(0x37), UINT8_C(0x38), UINT8_C(0x39),
6
> }};
7
>
> Entschuldigung, aber sowas ist weder lesbarer noch irgendwas. Im
> Gegenteil, es ist sogar sehr viel unleserlicher.

Das ist reine Übungssache. Neue Syntax sieht am Anfang immer verwirrend 
aus, aber wenn man sich einmal daran gewöhnt hat, verschwindet das.

> Ein 3. Beispiel: auto
> Da wird dann als erstes dieses Beispiel gebracht:
>
1
> int  x1;        // potenziell nicht initialisiert
2
> auto x2;        // Fehler! Initialisierung notwenig
3
> auto x3 = 0;    // Gut, wohl definierter Wert
4
>
> Okay, auto mag Vorteile habe, aber bei einem primitiven Datentyp sehe
> ich die nicht.

Das ist ja auch nicht der designierte Anwendungsfall von "auto", sondern 
nur ein Beispiel aus einem Lehrbuch. Aber bei
1
std::vector<int> liste;
2
// ein paar Elemente hinzufügen
3
for(auto elem: liste { 
4
  std::cout << elem << std::endl;
5
}

ist das schon viel schicker als
1
for(int i = 0; i < liste.size(); ++i) {
2
  std::cout << liste[i] << std::endl;
3
}

oder gar
1
for(std::vector<int>::iterator it=liste.begin; it != liste.end(); ++it) {
2
  std::cout << *it << std::endl;
3
}

> Ich geb mir wirklich Mühe C++ besser zu verstehenund und zu nutzen. Wenn
> mir aber sowas als "effektiv und modern", womöglich sogar als hip,
> verkauft werden soll,...
> [...]
> Ich werde mit C++ einfach nicht warm...

Du gibst ihm aber auch keine Chance, sondern beurteilst es anhand von 
ein paar popeligigen Lehrbuch-Beispielen - deren Ziel es nicht ist, Dir 
die Eleganz und Lesbarkeit von C++ zu demonstrieren, sondern die Dir nur 
ein Verständnis für spezielle Besonderheiten der Sprache vermitteln 
sollen.

Außerdem ist es wahrscheinlich nicht die beste Idee, sich zum Lernen 
ausgerechnet zwei Bücher auszusuchen, von denen eines die Spezialitäten 
der Sprache in limitierten Umgebungen erklärt, während das andere an 
erfahrene C++-Entwickler gerichtet ist, die ihre Verwendung der Sprache 
optimieren und effizienter gestalten wollen. Für den Einstieg wäre ein 
allgemein gehaltenes Lehrbuch sicher besser geeignet als Bücher, die 
sich auf randständige Spezialitäten konzentrieren.

von (prx) A. K. (prx)


Bewertung
0 lesenswert
nicht lesenswert
Rolf M. schrieb:
> Die Makros sind genau dazu da, das anzuhängen. Es gibt also eigentlich
> keinen Grund, sie dort zu verwenden, wo man das nicht will.

Besser: ... wo man das nicht unbedingt muss. Es gibt nicht so arg viele 
Stellen, an denen diese Makros wirklich notwendig sind. Das Preproc-#if 
ist eine davon, nicht zuletzt, weil man in dessen Rechnerei aufgrund der 
eingeschränkten und von C weitgehend losgelösten Syntax mit Casts keinen 
Stich macht.

von (prx) A. K. (prx)


Bewertung
1 lesenswert
nicht lesenswert
Karl Käfer schrieb:
> Das ist reine Übungssache. Neue Syntax sieht am Anfang immer verwirrend
> aus, aber wenn man sich einmal daran gewöhnt hat, verschwindet das.

Worin liegt der Vorteil, Initialisierungslisten irgendwelcher 
Integer-Typen mit diesen Makros zuzuscheissen? Kein C oder C++ Compiler 
dieser Welt stört sich daran, wenn du ein uint8_t mit 1 statt UINT8_C(1) 
beglückst.

Im stdint.h von Linux steht überdies drin, das diese UINTn_C Dinger 
eigentlich bloss für C gedacht sind, nicht aber für C++:
/* The ISO C99 standard specifies that in C++ implementations these
   should only be defined if explicitly requested.  */
#if !defined __cplusplus || defined __STDC_CONSTANT_MACROS

: Bearbeitet durch User
von F. F. (foldi)


Bewertung
0 lesenswert
nicht lesenswert
Yalu X. schrieb:
> A1: Es soll ein hohes Abstraktionsniveau bieten.
>
> A2: Trotzdem soll es maschinennahe und effiziente Programmierung
>     ermöglichen.
>
> A3: Zudem soll es abwärtskompatibel zu C sein.

A4:  C++ soll die Zusammenarbeit an einem Projekt vereinfachen.

von Karl Käfer (Gast)


Bewertung
1 lesenswert
nicht lesenswert
Piet schrieb:
> Ob sich der ganze C(++) Zirkus nämlich wirklich lohnt hängt ganz
> empfindlich von Hardware-Architektur und der zu erstellenden
> Software-Projektgrößen/typen ab. Es soll ja nicht bloß darauf
> hinauslaufen, daß man die Zeit, die man mit den vielen 'modernen'
> Sprachmitteln einzusparen imstande ist in gleichem Umfang vorab und in
> steter Weiterbildung in die Beherrschung des ganzen
> Sprach-Instrumentariums stecken muß. Da lohnt sich schon ein wenig
> Hinterfragen nach Sinn und Zweck, zumindest für den, der sich das
> leisten kann :)

Das Hinterfragen "lohnt" sich nur für den, der nicht verstanden hat, daß 
Weiterbildung eine Investition in die Zukunft ist. Wer ignoriert, daß er 
von einmal Gelerntem nicht nur in einem, sondern in allen zukünftigen 
Projekten profitiert, ist nur der Sachwalter seines einmal erworbenen 
Wissens und wird niemals ein guter Entwickler.

von Guido G. (nugglix)


Bewertung
0 lesenswert
nicht lesenswert
Reinhard M. schrieb:
> Von meyers gibt es "effektives c++ in embedded systems" auch als pdf.

ISBN?
Danke!

von Torsten R. (Firma: Torrox.de) (torstenrobitzki)


Bewertung
0 lesenswert
nicht lesenswert
Guido G. schrieb:
> Reinhard M. schrieb:
>> Von meyers gibt es "effektives c++ in embedded systems" auch als pdf.

Das würde ich aber auch mit Vorsicht genießen. Da stand auch einiges an 
Blödsinn drinnen (z.B. inplace Konstruktionen in Register, auf die man 
dann per Referenz verweist).

von Karl Käfer (Gast)


Bewertung
1 lesenswert
nicht lesenswert
A. K. schrieb:
> Karl Käfer schrieb:
>> Das ist reine Übungssache. Neue Syntax sieht am Anfang immer verwirrend
>> aus, aber wenn man sich einmal daran gewöhnt hat, verschwindet das.
>
> Worin liegt der Vorteil, Initialisierungslisten irgendwelcher
> Integer-Typen mit diesen Makros zuzuscheissen?

Habe ich denn behauptet, daß das Vorteile habe? Ich glaube nicht, daher 
ist der Autor dieses Lehrbuches der richtige Ansprechpartner für Deine 
Frage.

von Inschenör (Gast)


Bewertung
-2 lesenswert
nicht lesenswert
Ist doch egal, es muss nicht leserlich sein, sondern funktionieren z.B.:
1
if(*((gpio+GPLEV0_ADDR_OFFSET/4)+ (unsigned int)(pinNumber/32)) & (1 << pinNumber % 32))
 (Quelle: http://engsta.com/raspberry-pi-arduino-like-io-access/)

Wenn was nicht funktioniert, kann man sich mit dem Code 
auseinandersetzen und doch nachvollziehen, was es macht.

von Nop (Gast)


Bewertung
-2 lesenswert
nicht lesenswert
Kaj G. schrieb:
> Aber ich habe das Gefühl, dass C++ einfach nur noch aufgeblasen
> und überladen bis zum geht nicht mehr ist. :(

Der Kaiser ist nackt.

von Rolf Magnus (Gast)


Bewertung
3 lesenswert
nicht lesenswert
A. K. schrieb:
> Im stdint.h von Linux steht überdies drin, das diese UINTn_C Dinger
> eigentlich bloss für C gedacht sind, nicht aber für C++:

In C++11 sind die anscheinend von C übernommen worden:
http://en.cppreference.com/w/cpp/types/integer

Karl Käfer schrieb:
> oder gar
> for(std::vector<int>::iterator it=liste.begin; it != liste.end(); ++it)
> {

Ich nehme als Beispiel immer sowas wie:
1
for (std::unordered_map<std::string, std::string>::const_iterator it = mymap.begin(); it != mymap.end(); ++it)
gegen:
1
for (const auto& elem : mymap)

Ich würde sagen, range-bsaed for und auto gehören mit zu den Features 
von C++11, die einem gegenüber früheren Versionen am meisten Tipparbeit 
abnehmen.

von Chris D. (myfairtux) (Moderator) Benutzerseite


Bewertung
2 lesenswert
nicht lesenswert
Rolf Magnus schrieb:
>
1
> for (std::unordered_map<std::string, std::string>::const_iterator it = 
2
> mymap.begin(); it != mymap.end(); ++it)
3
>
> gegen:
>
1
> for (const auto& elem : mymap)
2
>
>
> Ich würde sagen, range-bsaed for und auto gehören mit zu den Features
> von C++11, die einem gegenüber früheren Versionen am meisten Tipparbeit
> abnehmen.

Da merke ich dann doch, wie lange ich schon aus C++ raus bin (mal 
einfache Klassenstrukturen auf der GUI-Wiese ausgenommen) ;-)

Aber der Thread ist sehr interessant: Kompetenz gepaart mit vernünftigem 
Umgang ergibt immer noch die besten Threads. Dafür liebe ich dieses 
Forum.

von Wilhelm M. (wimalopaan)


Bewertung
0 lesenswert
nicht lesenswert
Vielleicht noch mal zu dem Problem des const/constexpr Arrays vom 
Anfang.

Ich habe mal ein Beispiel konstruiert:

Wir haben ein 10-elementiges non-const uint8_t-Array und möchten daraus 
bestimmte, zur Compile-Zeit festliegende Elemente, deren Indizes bekannt 
sind, summieren. Die Indizes für die Summation kann man berechnen aus 
den Indizes des Arrays (hier einfach Addition von +1).

Beides soll so flexibel wie möglich sein!

Einmal die C-Variante:
1
#define Size 10
2
#define NumberOfIndices 3
3
4
uint8_t foo(size_t v) {
5
    return v + 1;
6
}
7
8
int main()
9
{
10
    uint8_t values[Size] = {1, 1, 1};
11
12
    int indices[NumberOfIndices] = {foo(0), foo(1), foo(2)};
13
    
14
    uint8_t sum = 0;
15
    for(uint8_t l = 0; l < NumberOfIndices; ++l) {
16
        sum += values[indices[l]];
17
    }
18
    return sum;    
19
20
}

Hier haben wir das Problem der statische Array-Länge, die als 
integer-constant-expression angegeben werden muss (deswegen mit Cpp). Da 
Cpp-Makros nicht scoped sind, ist das nicht schön. Ein const-int geht 
auch nicht. Die Auswertung des Index-Lookup geschieht zur Laufzeit, 
wobei der Compiler das schon optimiert. Das Konstrukt 
sizeof(array)/sizeof(item) wurd extra nicht betrachtet, weil nur 
eingeschränkt nutzbar.

Bei einer anderen Variante wird das sichtbarer:
1
uint8_t foo(size_t v) {
2
    return v + 1;
3
}
4
5
int main()
6
{
7
    const int Size = 10;
8
    uint8_t values[Size];
9
    values[0] = 1;
10
    values[1] = 1;
11
    values[2] = 1;
12
    
13
    const int NumberOfIndices = 3;
14
    int indices[NumberOfIndices];
15
    for(uint8_t l = 0; l < NumberOfIndices; ++l) {
16
        indices[l] = foo(l);
17
    }
18
    
19
    uint8_t sum = 0;
20
    for(uint8_t l = 0; l < NumberOfIndices; ++l) {
21
        sum += values[indices[l]];
22
    }
23
    return sum;    
24
25
}
Hier sind die Dimensionierungen scoped, so wie es sich gehören sollte. 
Die Initialisierung bzw. Zuweisung des Arrays ist unvollständig -> 
Warnung.

Und hier die C++ Variante:
1
template<typename ItemType, typename F, size_t... II>
2
constexpr auto createArray(F f, std::index_sequence<II...>) {
3
    return std::array<ItemType, sizeof...(II)> {{f(II)...}};
4
}
5
6
int main()
7
{
8
    constexpr int Size = 10;
9
    std::array<uint8_t, Size> values = {1, 1, 1};
10
    
11
    constexpr auto indices = createArray<uint8_t>([](size_t v){return v + 1;}, std::make_index_sequence<3>{});
12
13
    uint8_t sum = 0;
14
    for(const auto& i : indices) {
15
        sum += values[i];
16
    }
17
    return sum;    
18
}

Hier ist die Dimensionierung scoped und alle Initialisierungen 
vollständig. Über die Lambda-Expression recht flexibel. Alles zur 
Compilezeit, da constexpr.

Man möge urteilen ;-)

: Bearbeitet durch User
von Vincent H. (vinci)


Bewertung
-2 lesenswert
nicht lesenswert
Und nur um das nochmal zu unterstreichen, das C Beispiel benötigt für 
die Indizes Ram. Das C++ Beispiel hingegen legt diese im Flash ab.

von Wilhelm M. (wimalopaan)


Bewertung
0 lesenswert
nicht lesenswert
Vincent H. schrieb:
> Und nur um das nochmal zu unterstreichen, das C Beispiel benötigt für
> die Indizes Ram. Das C++ Beispiel hingegen legt diese im Flash ab.

Nein, weder Stack noch Flash.

von Wilhelm M. (wimalopaan)


Bewertung
1 lesenswert
nicht lesenswert
Die C++ Variante ist faktisch identisch zu:
1
int main()
2
{
3
    constexpr int Size = 10;
4
    std::array<uint8_t, Size> values = {1, 1, 1};
5
    
6
    uint8_t sum = 0;
7
    sum += values[1];
8
    sum += values[2];
9
    sum += values[3];
10
    return sum;    
11
}

von Vincent H. (vinci)


Bewertung
0 lesenswert
nicht lesenswert
Ah pardon. Da hab ich dein Beispiel zu schnell überflogen. Ich dachte 
kurz die Indizes landen in der Summe, nicht die Werte.

/edit
Manchmal bin ich selbst überrascht, was mit constexpr alles möglich ist. 
=)

: Bearbeitet durch User
von Chris F. (chfreund) Benutzerseite


Bewertung
1 lesenswert
nicht lesenswert
Ich habe immer lieber mit C++ und ordentlichen OOP-Strukturen gearbeitet 
als nur mit C. Da helfen auch solche Stänkerer und 
Substandardprogrammierer wie Torwalds nicht ;-) Toll ist auch, dass ich 
immmer direkt nativ arbeiten konnte und zwar nur da wo es sinnig ist mit 
inline-asm.
Allerdings habe ich auch am laufenden Band Programmcode gesehen, den man 
in einer "nicht-nativen Runtime/VM-Umgebung" so machen kann, der aber 
bei C++ ein riesiges Problem ist.
Es wäre echt schade, wenn sowas auf die Programmiersprache geschoben 
wird. Schuldzuweisungen sind keine Lösungen für Unkenntnis oder fehlende 
Sorgfalt.

Kaj G. schrieb:
> Ich geb mir wirklich Mühe C++ besser zu verstehenund und zu nutzen. Wenn
> mir aber sowas als "effektiv und modern", womöglich sogar als hip,
> verkauft werden soll,...

Das C++ "hip" sein soll ist mir neu. Für hardwarenahe SW-Entwicklung ist 
es aber das effektivste und modernste Verfügbare.

von Dampf T. (ouuneii)


Bewertung
1 lesenswert
nicht lesenswert
Zum originalen Post. Weshalb sollte man sich mit etwas rumquaelen, das 
keinen Spass macht. Das Leben ist zu kurz um sich mit C den Tag zu 
verderben. Es gibt ja Alternativen.
Dabei ist anzumerken, dass es Leute gibt, die Code verkaufen, resp Geld 
fuer Code nehmen. Und wenn der Bezahler C will, ist es eben C.
Ich hingegen verkaufe keinen Code, sondern Loesungen. Dann ist die 
Sprache auch kein Thema mehr. Ebensowenig, ob ich um etwas 
anzuschrauben, eine Kreuz- oder eine Torx Schraube verwende. Der Winkel 
ist angeschraubt und haelt.

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.