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
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
intx1;// potenziell nicht initialisiert
2
autox2;// Fehler! Initialisierung notwenig
3
autox3=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
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.
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.
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
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.
> 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 :)
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.
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?
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 :)
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.
Ich hab ja kein Grundsaetzliches Problem mit C++, aber was mir da als
"besser" verkauft werden soll...
1
constexprstd::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 ;)
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...
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.
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. :)
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.>
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
>intx1;// potenziell nicht initialisiert
2
>autox2;// Fehler! Initialisierung notwenig
3
>autox3=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
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.
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.
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:
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.
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. ;-)
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 :-/
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.
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...
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:
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
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.
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.
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.
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
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.
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"?
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?
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.
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:
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.
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?
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.
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++.
> 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.
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.
> 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
>intx1;// potenziell nicht initialisiert
2
>autox2;// Fehler! Initialisierung notwenig
3
>autox3=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
> 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.
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.
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
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.
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.
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).
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.
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/integerKarl Käfer schrieb:> oder gar> for(std::vector<int>::iterator it=liste.begin; it != liste.end(); ++it)> {
Ich nehme als Beispiel immer sowas wie:
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.
>> 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.
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:
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_tfoo(size_tv){
2
returnv+1;
3
}
4
5
intmain()
6
{
7
constintSize=10;
8
uint8_tvalues[Size];
9
values[0]=1;
10
values[1]=1;
11
values[2]=1;
12
13
constintNumberOfIndices=3;
14
intindices[NumberOfIndices];
15
for(uint8_tl=0;l<NumberOfIndices;++l){
16
indices[l]=foo(l);
17
}
18
19
uint8_tsum=0;
20
for(uint8_tl=0;l<NumberOfIndices;++l){
21
sum+=values[indices[l]];
22
}
23
returnsum;
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:
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 ;-)
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.
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.
=)
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.
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.