Forum: PC-Programmierung c++ 11/14/17?


von Klaus (Gast)


Lesenswert?

Hi
ich arbeite zwar lange mit C, musste hier und da auch kleinere Projekte 
mit C++ angehen.
sind diese ganzen Erweiterungen von c++11/14/17 weltbewegend und 
verbreitet?
Auch im embedded Bereich?

Klaus

von Programmierer (Gast)


Lesenswert?

Klaus schrieb:
> sind diese ganzen Erweiterungen von c++11/14/17 weltbewegend und
> verbreitet? Auch im embedded Bereich?

Ja, ja, ja. Viele der Neuerungen sind für embedded sehr nützlich, wie 
z.B. Vorberechnung via constexpr. Es gibt kaum einen Grund sich auf den 
18 Jahre alten Sprachstandard C++03 zu beschränken.

von dunky (Gast)


Lesenswert?

Den Schritt zu c++11 würde ich schon weltbewegend bezeichnen.

Aber bei so einer unspezifischen Frage kann man auch einfach 
ja/nein/vielleicht antworten ;)

von Rolf M. (rmagnus)


Lesenswert?

Klaus schrieb:
> Hi
> ich arbeite zwar lange mit C, musste hier und da auch kleinere Projekte
> mit C++ angehen.
> sind diese ganzen Erweiterungen von c++11/14/17 weltbewegend und
> verbreitet?

Definitiv.

> Auch im embedded Bereich?

Hmm, schwer zu sagen. Ich nutze sie dort. Kommt aber auch drauf an, was 
der verfügbare Compiler kann. Manche sind bei solchen Änderungen ja 
nicht gerade rasend schnell unterwegs, vor allem, wenn sie zertifiziert 
sein müssen.

von Yalu X. (yalu) (Moderator)


Lesenswert?

Klaus schrieb:
> c++11/14/17

Inzwischen kannst du auch noch C++20 mit hinzunehmen.

Einige der Neuerungen sind sehr praktisch und erleichtern das 
Programmieren, weswegen sich die Beschäftigung damit auf jeden Fall 
lohnt.

Andere blähen IHMO die Sprache nur weiter auf und hätten genauso gut 
weggelassen werden können. Aber das ist ein allgemeines Problem bei 
Kommiteesprachen (nicht nur C++): Da will jeder Beteiligte auf Biegen 
und Brechen seine supertolle Idee in den Standard einbringen, und wenn 
der Vorschlag nicht völlig destruktiv ist, stimmen die anderen halt 
irgendwann zu, damit der Standardisierungsprozess nicht zu sehr ins 
Stocken gerät.

von mh (Gast)


Lesenswert?

Yalu X. schrieb:
> Andere blähen IHMO die Sprache nur weiter auf und hätten genauso gut
> weggelassen werden können.

Was würdest du sagen bläht die Sprache nur auf?

von Hau wech den Scheiss (Gast)


Lesenswert?

Rolf M. schrieb:
> Manche sind bei solchen Änderungen ja
> nicht gerade rasend schnell unterwegs, vor allem, wenn sie zertifiziert
> sein müssen.

Eben. Sicherheitsregel: "Never change a running system. Never run a 
changed system" !

von Programmierer (Gast)


Lesenswert?

Hau wech den Scheiss schrieb:
> Eben. Sicherheitsregel: "Never change a running system. Never run a
> changed system" !

Nach dem Motto tut sich aber nie was, oder man müsste ständig komplett 
neue Sprachen erfinden um Neuerungen umzusetzen. Es gibt auf der ganzen 
Welt nicht genug Programmierer, um ständig alles in neue Sprachen neu zu 
implementieren, wenn man die neuen Features nutzen will.

Yalu X. schrieb:
> Andere blähen IHMO die Sprache nur weiter auf und hätten genauso gut
> weggelassen werden können.

Andere finden diese Features gut. Um nicht völlig von anderen Sprachen 
abgehängt zu werden, kann man dort etablierte Features übernehmen. C++ 
hat auch eine Daseinsberechtigung für neue Projekte, abseits der Pflege 
existierender Systeme, und hier möchte man vielleicht von neuen Features 
profitieren. Wer hat jetzt Recht?

von Hau wech den Scheiss (Gast)


Lesenswert?

Programmierer schrieb:
> Hau wech den Scheiss schrieb:
>> Sicherheitsregel: "Never change a running system. Never run a
>> changed system" !
>
> Es gibt auf der ganzen
> Welt nicht genug Programmierer, um ständig alles in neue Sprachen neu zu
> implementieren, wenn man die neuen Features nutzen will.

Eben. Warum irgendwelche modischen Neuerungen durchdrücken, wenn es 
weder die Manpower dafür gibt, noch irgendwelche Verbesserungen an der 
Sicherheit aka Kundenzufriedenheit zu erwarten sind.

von mh (Gast)


Lesenswert?

Hau wech den Scheiss schrieb:
> Eben. Warum irgendwelche modischen Neuerungen durchdrücken, wenn es
> weder die Manpower dafür gibt, noch irgendwelche Verbesserungen an der
> Sicherheit aka Kundenzufriedenheit zu erwarten sind.

Dann, gib mal wenigstens ein Beispiel aus dem c++20 Standard
- das eine modische Neuerung und
- zuwenig Manpower (was auch immer du damit meinen magst) hat und
- keine Verbesserung an Sicherheit oder Kundenzufriedenheit erwarten 
lässt.

von Programmierer (Gast)


Lesenswert?

Hau wech den Scheiss schrieb:
> Eben. Warum irgendwelche modischen Neuerungen durchdrücken, wenn es
> weder die Manpower dafür gibt, noch irgendwelche Verbesserungen an der
> Sicherheit aka Kundenzufriedenheit zu erwarten sind.

Worauf bezieht sich das jetzt? Neue Sprachfeatures wie z.B. Lambdas 
bedeuten einfachere und schnellere Entwicklung und Wartung, und dadurch 
geringere Entwicklungskosten und billigere Endprodukte, und billig macht 
Kunden zufrieden.

von Hau wech den Scheiss (Gast)


Lesenswert?

Programmierer schrieb:
> Hau wech den Scheiss schrieb:
>> Eben. Warum irgendwelche modischen Neuerungen durchdrücken, wenn es
>> weder die Manpower dafür gibt, noch irgendwelche Verbesserungen an der
>> Sicherheit aka Kundenzufriedenheit zu erwarten sind.
>
> Worauf bezieht sich das jetzt? Neue Sprachfeatures wie z.B. Lambdas
> bedeuten einfachere und schnellere Entwicklung und Wartung, und dadurch
> geringere Entwicklungskosten und billigere Endprodukte, und billig macht
> Kunden zufrieden.

Es geht um Software-Qualifizierung also Sicherheitskritischer Bereich. 
Das kann schon Automotive sein, und da will der Kunde sicher und 
unbeschadet angekommen und zahlt dafür auch einen Aufschlag. Siehe 
Volvo.

Die Software ist eben nicht nach der Entwicklung fertig, da schliesst 
sich mindestens noch die verifikatiopn an.Und da bleibt man lieber bei 
gut bewährter Software .

Und die billigste Software ist immer noch die, die man schon hatund 
deren Schwachstellen man kennt.

von Programmierer (Gast)


Lesenswert?

Hau wech den Scheiss schrieb:
> Es geht um Software-Qualifizierung also Sicherheitskritischer Bereich.

Seit wann?

Hau wech den Scheiss schrieb:
> Und da bleibt man lieber bei
> gut bewährter Software .

Warum?

Neue C++-Versionen bieten bessere Möglichkeiten zur typsicheren 
(Meta-)Programmierung, damit kann man mehr Fehler direkt beim 
Kompilieren erkennen, wodurch sich die Fehlerwahrscheinlichkeit wieder 
reduziert.

Hau wech den Scheiss schrieb:
> da schliesst
> sich mindestens noch die verifikatiopn an.

Echte Verifikation wird in der Automobilindustrie nicht gemacht, da 
praktisch unmöglich.

von P. S. (namnyef)


Lesenswert?

Leider sind die Toolchain-Anbieter im Embedded-Bereich seeeehr träge, 
was das Implementieren der "neuen" C++-Standards angeht. C++11 ist bei 
vielen schon das höchste der Gefühle.
Aber ja: C++ hat viele Features, die auch im Embedded-Bereich sehr 
sinnvoll wären oder sind.

: Bearbeitet durch User
von Jan K. (jan_k)


Lesenswert?

In Automotive und Co (alles was mit FuSi zu tun hat), braucht man 
zertifizierte Komponenten, das schließt den Compiler, die Hardware, die 
Toolchain mit ein. Ein zertifizierter C++11 (wenn man überhaupt das 
Glück hat, C++ benutzen zu dürfen (!)) wird nicht mal eben durch einen 
C++17 (falls es den überhaupt "schon" gibt) ersetzt. Das ist zu teuer 
(denn man muss häufig dann mehrere Varianten parallel haben, um alte 
builds reproduzieren zu können) und dabei ist die Entwicklungszeit 
(Lambdas, constexpr etc) leider fast vollkommen egal - zumindest war es 
das in den letzten Firmen, die ich von innen gesehen habe.

edit: Von Keil gibts beispielsweise gerade mal C++11: 
https://developer.arm.com/tools-and-software/embedded/arm-compiler/safety

: Bearbeitet durch User
von Hau wech den Scheiss (Gast)


Lesenswert?

Programmierer schrieb:
> Hau wech den Scheiss schrieb:
>> Es geht um Software-Qualifizierung also Sicherheitskritischer Bereich.
>
> Seit wann?

Seit der Aspekt der Software-Zertifizierung in den Thread geworfen 
wurde:

Beitrag "Re: c++ 11/14/17?"

Hau wech den Scheiss schrieb:
>> Und da bleibt man lieber bei
>> gut bewährter Software .

>Warum?

Naja, weil man davon ausgeht das sich Software die sich in der 
Vergangenheit als Fehlerarm gezeigt hat, auch in Zukunft das selbe 
Fehlerarme Verhalten zeigen wird. Software unterliegt keinem Verschleiß 
und muss daher nicht permanent erneuert werden.

>Echte Verifikation wird in der Automobilindustrie nicht gemacht, da
>praktisch unmöglich.

Klar, und ich bin der Kaiser von China.

von Jan K. (jan_k)


Lesenswert?

Hau wech den Scheiss schrieb:
>>Echte Verifikation wird in der Automobilindustrie nicht gemacht, da
>>praktisch unmöglich.
>
> Klar, und ich bin der Kaiser von China.

Ich denke, da hat er Recht. Echte aka formale Verifikation ist bei 
komplexen Zusammenhängen extremst aufwendig oder nahezu unmöglich.

von Rolf M. (rmagnus)


Lesenswert?

mh schrieb:
> Dann, gib mal wenigstens ein Beispiel aus dem c++20 Standard
...
> - zuwenig Manpower (was auch immer du damit meinen magst) hat

Es geht dabei nicht um den Standard selbst, sondern um die 
Compiler-Entwickler, die das dann in ihren Produkten umsetzen müssen.

Programmierer schrieb:
> Neue C++-Versionen bieten bessere Möglichkeiten zur typsicheren
> (Meta-)Programmierung, damit kann man mehr Fehler direkt beim
> Kompilieren erkennen, wodurch sich die Fehlerwahrscheinlichkeit wieder
> reduziert.

Das basiert aber auf der Annahme, dass der Compiler selbst generell 
keine Fehler hat. Als Entwickler der Software hat man allerdings nur auf 
den Quellcode Einfluss, nicht aber auf den Compiler selbst.

Hau wech den Scheiss schrieb:
> Hau wech den Scheiss schrieb:
>>> Und da bleibt man lieber bei
>>> gut bewährter Software .
>
>>Warum?
>
> Naja, weil man davon ausgeht das sich Software die sich in der
> Vergangenheit als Fehlerarm gezeigt hat, auch in Zukunft das selbe
> Fehlerarme Verhalten zeigen wird. Software unterliegt keinem Verschleiß
> und muss daher nicht permanent erneuert werden.

Darüber hinaus kennt man bei länger verwendeter Software die bestehenden 
Fehler, und ein Wechsel auf eine neue Software würde die Fehler durch 
neue ersetzen, die man noch nicht kennt. Und bekannte Fehler sind immer 
besser als unbekannte. Deshalb wird auch in der Raumfahrt gerne "gut 
abgehangene" Hardware verwendet, also z.B. Prozessor-Typen, die es schon 
sehr lange gibt. Neben der geringen Komplexität sind dort auch mehr von 
den Fehlern bereits gefunden worden als bei neuerer Hardware.

>>Echte Verifikation wird in der Automobilindustrie nicht gemacht, da
>>praktisch unmöglich.
>
> Klar, und ich bin der Kaiser von China.

Er meint mit "Verfikiation" vermutlich die Erbringung eines formalen 
Beweises für die Korrektheit.

von Hau wech den Scheiss (Gast)


Lesenswert?

Jan K. schrieb:
> Hau wech den Scheiss schrieb:
>>>Echte Verifikation wird in der Automobilindustrie nicht gemacht, da
>>>praktisch unmöglich.
>>
>> Klar, und ich bin der Kaiser von China.
>
> Ich denke, da hat er Recht. Echte aka formale Verifikation ist bei
> komplexen Zusammenhängen extremst aufwendig oder nahezu unmöglich.

Nur wenn man das Design und den Entwicklungsprozess nicht auf 
Verifikation hin auslegt. Schon mal was von Design for Testability 
gehört? Oder Test Automatisition Equipment? Fehlerinjektion?

Also ich kenn da Automotive-Buden die haben mehrere Bladeracks und 
HIL-Aufbauten im Serverraum stehen, die nichts anderes machen als die 
Firmware Tagelang mit testpattern zu stressen. Hinzukommen die Tausenden 
Testfahrzeuge mit Protokollrecordern, die mehrere MannJahrzehnte an 
realen Szemarien aufzeichnen bevor der Automobilhersteller seine 
Freigabe erteilt.

https://www.pcwelt.de/news/BMWs-autonome-Autos-1.500-TB-neue-Daten-pro-Tag-10564439.html

Ntürlich wird in der Automobilindustire verifiziert -so gut und 
Vollständig es eben geht. Deshalb nimmt man ja auch unnötige Komplexität 
aus dem Produkt/Entwicklungsprozess. beispielsweise die, durch Zulassung 
neuer Code-Standards zwangsläufig entsteht. - "Never change a running 
system"

von Programmierer (Gast)


Lesenswert?

Hau wech den Scheiss schrieb:
> Nur wenn man das Design und den Entwicklungsprozess nicht auf
> Verifikation hin auslegt. Schon mal was von Design for Testability
> gehört? Oder Test Automatisition Equipment? Fehlerinjektion?

Testen hat nichts mit Verifikation zu tun. Verifikation bedeutet, einen 
formalen mathematischen Beweis zu erbringen, dass die Software exakt der 
Spezifikation entspricht. Und das ist bei komplexer Software ein 
gewaltiger Aufwand. Um diesen zu vermeiden, testet man halt einfach 
möglichst viele (trotzdem nur ein verschwindend geringer Anteil) Fälle 
in der Hoffnung, dass die verbleibenden Fälle auch korrekt sind.

C++20 Software kann man genau so testen wie C89-Software.

von Oliver S. (oliverso)


Lesenswert?

Jan K. schrieb:
> In Automotive und Co (alles was mit FuSi zu tun hat), braucht man
> zertifizierte Komponenten, das schließt den Compiler, die Hardware, die
> Toolchain mit ein.

Nur braucht das der größte Teil der Software in einem Auto gar nicht.

Und da zur Zeit jeder Kommentar über die deutsche Automobilindustrie in 
etwas lautet "können alles, ausser Software", würde ich auf die 
bisherigen Gepflogenheiten dort auch nicht allzuviel geben.

Ich bin mir ziemlich sicher, daß weder Google, noch Apple oder Tesla 
sich mit Misratenen Normentwürfen herumschlagen. Die sitzen stattdessen 
in den C++-Normungsgremien, und gehen damit vorwärts.

Oliver

von Jan K. (jan_k)


Lesenswert?

Oliver S. schrieb:
> Ich bin mir ziemlich sicher, daß weder Google, noch Apple oder Tesla
> sich mit Misratenen Normentwürfen herumschlagen. Die sitzen stattdessen
> in den C++-Normungsgremien, und gehen damit vorwärts.

Das stimmt vermutlich. Und glaub mir, ich bin über den langsamen 
Fortschritt auch angenervt, bekomme es tagtäglich auf der Arbeit mit.

Möchte man aber Hardware/Module in einem Auto oder in der Industrie 
unterbringen, muss häufig nach den erwarteten Normen entwickelt werden. 
Macht man das nicht, bekommt man das Projekt nicht - ganz einfach. Und 
ISO26262 und co kommen gerade erst richtig und schwappen von purem 
Automotive in Richtung Transportation, Industrie, Baumaschinen etc.

@Hau wech den Scheiss (Gast), du kennst leider den Begriff formale 
Verifikation nicht. Das hat nichts mit Testen zu tun. Natürlich wird 
viel getestet und wer seine Software samt Drumherum nicht testbar 
gestaltet, hat irgendwann grundsätzlich die A-Karte.

von Wilhelm M. (wimalopaan)


Lesenswert?

Klaus schrieb:
> sind diese ganzen Erweiterungen von c++11/14/17 weltbewegend

ja

> und
> verbreitet?

In diesem Forum stoßen sie regelmäßig auf Kritik ;-) Das scheint ein 
gutes Zeichen zu sein.

> Auch im embedded Bereich?

Eigentlich gerade da.

Meine persönliche Hitliste im µC-Bereich (inkl. c++20) sind:

1) concepts: der Schlüssel zu compiletime-polymorphie und TMP
2) constexpr everywhere
3) lambda-expr. Verbesserungen (e.g. explicit template-lambda parameter)
4) stdlib: tuple<>, variant<>, bit_cast<>, etc. zusammen mit 2)
5) template-alias (using) und viele Kleinigkeiten vereinfachen die TMP

: Bearbeitet durch User
von Yalu X. (yalu) (Moderator)


Lesenswert?

mh schrieb:
> Yalu X. schrieb:
>> Andere blähen IHMO die Sprache nur weiter auf und hätten genauso gut
>> weggelassen werden können.
>
> Was würdest du sagen bläht die Sprache nur auf?

Zwei Beispiele, die mich besonders stören:


1. Die Uniform Initialization:

   Zu den bereits bestehenden mehreren Möglichkeiten der Initialisierug
   kommt nun noch eine weitere hinzu

     https://xkcd.com/927/

   beschreibt die Situation ganz treffend, wenn man dort "standards"
   durch "initialization methods" ersetzt.

   Nicht ganz so schlimm fände ich die Uniform Initialization, wenn sie
   wenigstens die klassischen Initialisierungsmethoden komplett ersetzen
   könnte, aber das tut sie nicht.

   Dazu kommt noch, dass nun einige meinen, deren Verwendung auf die
   Spitze treiben zu müssen, so dass dann Stilblüten wie diese
   entstehen:

1
     for (int i{}; i<3; i++)
2
       ...

   Das ist zwar legaler Code, aber was soll der Unfug?

   Anfangs habe ich die Uniform Initialization selber verwendet, u.a. um
   in meinem Code zu dokumentieren, dass ich mit der Zeit gehe und nicht
   an alten Zöpfen hänge ;-) Mittlerweile ist mir das egal, weswegen ich
   wieder zu den klassischen Initialisierungsmethoden zurückgekehrt bin.
   Mich spricht auch deren Syntax mehr an.


2. Typsynonyme mit using als Alternative zu typedef:

   Ich frage mich, was an der typedef-Syntax schlecht ist. Den einzigen
   Mangel sehe ich darin, dass typedef nicht getemplatet werden kann,
   ein Problem, das seit der Einführung der Templates in C++ (also schon
   lange vor C++11) diskutiert wird, aber nie behoben wurde. Statt
   einfach auch typedef-Templates zu erlauben, wurde die völlig neue
   using-Syntax eingeführt. Wozu? Habe ich irgendwelche schwerwiegende
   Gründe übersehen, warum das für typedef nicht möglich ist?


Es gibt noch ein paar weitere IHMO unsinnige neue Features, die ich aber
aus meinem Gesichtsfeld entschwunden sind, da sie auch von anderen kaum
genutzt werden. Sie machen damit lediglich das Standarddokument und die
Lehrbücher fett, stören aber wenigstens bei der täglichen Arbeit nicht.


Bitte nicht missverstehen: Meine Kritik bedeutet keineswegs, dass ich
zurück zu C++03 möchte, ganz im Gegenteil. C++11 und Nachfolger bringen
insgesamt mehr positive als negative Erweiterungen mit. Die für mich
wichtigsten positiven Punkte sind:

1. Range based Loops:

   Endlich kann man über die Elemente eines Containers ohne dieses
   lästige begin() und end() iterieren.

2. Lambda-Ausdrücke:

   C++ war eine der letzten Sprachen, die das noch nicht hatte, deswegen
   wurde es höchste Zeit, das nachzuliefern.

3. Neue Datentypen in Standardbibliothek

   Tuple, Optional, verschiedene Sorten von Smart Pointers usw.

4: π

   Ich hätte nicht geglaubt, dass das bisher GCC-spezifische M_PI noch
   irgendwann in den Standard aufgenommen werden wird. Seit letzten
   Dezember (C++20) ist es aber tatsächlich so weit: Es gibt jetzt u.a.
   std::numbers::pi in <numbers>. Ich frage mich nur, warum das fast ein
   halbes Jahrhundert gedauert hat.

5. Compilezeitberechnungen mit constexpr:

   Wenn ich sehe, wie man solche Dinge früher mit Template-Hacks
   hingedoktert hat (einige haben daraus eine regelrechte Wissenschaft
   gemacht), ist diese Erweiterung ein wirklicher Segen.

6. Contraints und Concepts:

   Endlich wird das leidige Problem mit dem Duck-Typing in Templates und
   den damit verbundenen schwer interpretierbaren Fehlermeldungen etwas
   entschärft. Diese Lösung ist zwar nicht besonders schön und bedarf
   zusätzlicher Zuarbeit vonseiten des Template-Entwicklers, aber mehr
   war unter Beibehaltung der Abwärtskompatibilität wohl nicht möglich.

7. Stark typisierte Enums

   Ein weitere Schritt zu mehr Fehlererkennung durch den Compiler.

8. Type Inference:

   Implementiert wurde zwar nur eine schwache Form davon, aber auch
   diese ist oft sehr hilfreich, bspw. bei der Deklaration eines
   Iterators zu einem gegebenen Container-Objekt.

9. Korrekte Unterscheidung zwischen doppelten Temnplate-Klammern und dem
   >>-Operator

   Früher musste bspw. bei std::vector<std::vector<int>> zwischen den
   beiden ">" ein Leerzeichen eingefügt werden.

: Bearbeitet durch Moderator
von S. R. (svenska)


Lesenswert?

Das aus meiner Sicht größte Problem an C++ ist die Komplexität. Es gibt 
alles und dann noch die kleine Schwester davon.

Im Ergebnis übersteigt die Komplexität der Sprache die Hirne der 
Entwickler, die dann nur einen kleinen Teil der Möglichkeiten überhaupt 
nutzen. Ohne einen übergeordneten Standard nutzt dann jeder noch einen 
anderen Teil, was die Sprache für Menschen schlecht lesbar machen kann.

Kompatiblität ist schön und gut, aber im Drei-Jahres-Takt ausschließlich 
neue Dinge hinzufügen macht das nicht gerade besser.

Das "es gibt mehr als eine gute Lösung" ist es, was Perl so nervig macht 
und C++ geht aus meiner Sicht den gleichen Weg.

von Carl D. (jcw2)


Lesenswert?

S. R. schrieb:
>
> Kompatiblität ist schön und gut, aber im Drei-Jahres-Takt ausschließlich
> neue Dinge hinzufügen macht das nicht gerade besser.
>
> Das "es gibt mehr als eine gute Lösung" ist es, was Perl so nervig macht
> und C++ geht aus meiner Sicht den gleichen Weg.

Es gibt aus gutem Grund mehrere Lösungen. Denn wenn wegen neuer Ideen 
die alten Projekte nicht mehr übersetzbar sind, dann wird das wie bei 
Python 2 vs 3, wo auch heute noch beides parallel installiert sein muß.

Wenn nun etwas, was bisher umständlich war, durch ein neues, eleganteres 
Verfahren ersetzt werden kann, dann sollte da ein "endlich" zu hören 
sein. Falls es es keine übergeordneten Gründe gibt, das komplizierte zu 
behalten (Zertifizierung).
"Immer schon so gemacht" haben wir eben "auf den Bäumen sitzen".

Ausnahmen würde ich bei so Dingen machen wie "ich habe 'volatile' nicht 
verstanden und plädiere dafür es abzuschaffen". Wenn sowas von Firmen 
kommt, die eigentlich lieber ihre homebrew Sprachen verwenden, da kann 
man über die wirklichen Gründe mutmaßen. Da ist Apple nicht anders als 
MS.

von S. R. (svenska)


Lesenswert?

Carl D. schrieb:
> Es gibt aus gutem Grund mehrere Lösungen.

Das ist mir schon klar und ich kann auch das C++-Konsortium verstehen. 
Ändert aber nichts daran, dass der enorme Sprachumfang allein schon 
dafür sorgt, dass niemand die Sprache mehr vollständig überblicken kann.

Fällt z.B. immer dann auf, wenn bei den Vorträgen mal in die Runde 
gefragt wird, welchen Datentyp (oder welche Fehlermeldung) ein 
bestimmter Ausdruck jetzt erzeugt - und das selbst den Experten, die den 
Standard schreiben, nicht klar ist.

von Wilhelm M. (wimalopaan)


Lesenswert?

Mir kommen echt die Tränen, dass Ihr Euch in so viele neue Dinge 
einarbeiten müsst.

von Mark B. (markbrandis)


Lesenswert?

Wilhelm M. schrieb:
> Mir kommen echt die Tränen, dass Ihr Euch in so viele neue Dinge
> einarbeiten müsst.

Du beherrschst sämtliche Features von C++20? :-)

von Programmierer (Gast)


Lesenswert?

Mark B. schrieb:
> Du beherrschst sämtliche Features von C++20? :-)

Der schlaue Programmierer lernt sie bei Bedarf anstatt sich jedes Mal 
über den bösen Fortschritt zu beklagen...

von mh (Gast)


Lesenswert?

Mark B. schrieb:
> Wilhelm M. schrieb:
>> Mir kommen echt die Tränen, dass Ihr Euch in so viele neue Dinge
>> einarbeiten müsst.
>
> Du beherrschst sämtliche Features von C++20? :-)

Die 2h, um einmal alle Änderungen zu überfliegen, sollte man haben. Den 
Rest kann man machen, wenn man es braucht oder die Zeit hat.

von S. R. (svenska)


Lesenswert?

Wilhelm M. schrieb:
> Mir kommen echt die Tränen, dass Ihr Euch in so viele neue Dinge
> einarbeiten müsst.

Hängt schlicht davon ab, ob man für das "sich in viele neue Dinge 
einarbeiten" oder etwas anderes bezahlt wird, und ob man dafür eher eine 
Stunde oder drei Monate braucht.

mh schrieb:
> Die 2h, um einmal alle Änderungen zu überfliegen, sollte man haben.

Sicherlich, aber die Konsequenzen daraus sind nochmal eine ganz andere 
Hausnummer.

von mh (Gast)


Lesenswert?

S. R. schrieb:
> Sicherlich, aber die Konsequenzen daraus sind nochmal eine ganz andere
> Hausnummer.

Was sind denn die Konsequenzen "daraus"?

Ich sehe Nichts, das große Konsequenzen hat, wenn es nicht einsetzt.

von Oliver S. (oliverso)


Lesenswert?

mh schrieb:
> Was sind denn die Konsequenzen "daraus"?
>
> Ich sehe Nichts, das große Konsequenzen hat, wenn es nicht einsetzt.

Nun ja, mit concepts, auto überall, constexpr lambdas, ranges, und was 
da alles noch so an Kleinigkeiten dabei ist, sieht der Code doch schon 
sehr anders aus als das, was man mit C++11 so gebastelt hat. Bis man das 
in voller Konsequenz mal mental umgesetzt hat, dauerts schon länger als 
nur zwei Stunden.

Aber new und delete gibts auch immer noch, wer will, kannn nach wie vor 
"C mit Klassen"-Code schreiben.

Oliver

: Bearbeitet durch User
von Hans-Georg L. (h-g-l)


Lesenswert?

mh schrieb:
> Ich sehe Nichts, das große Konsequenzen hat, wenn es nicht einsetzt.

Die Konsequenz daraus ist, das du die Chance verschenkst deinen Code 
(typ) sicherer zu machen ;-)

von mh (Gast)


Lesenswert?

Oliver S. schrieb:
> mh schrieb:
>> Was sind denn die Konsequenzen "daraus"?
>>
>> Ich sehe Nichts, das große Konsequenzen hat, wenn es nicht einsetzt.
>
> Nun ja, mit concepts, auto überall, constexpr lambdas, ranges, und was
> da alles noch so an Kleinigkeiten dabei ist, sieht der Code doch schon
> sehr anders aus als das, was man mit C++11 so gebastelt hat. Bis man das
> in voller Konsequenz mal mental umgesetzt hat, dauerts schon länger als
> nur zwei Stunden.

Es ging um C++20, also sind nur die Änderungen seit C++17 relevant. Und 
wie gesagt:
mh schrieb:
> Die 2h, um einmal alle Änderungen zu überfliegen, sollte man haben. Den
> Rest kann man machen, wenn man es braucht oder die Zeit hat.

Niemand erwartet, dass du das alle C++20 Änderungen von heute auf morgen 
perfekt benutzen kannst.

Hans-Georg L. schrieb:
> mh schrieb:
>> Ich sehe Nichts, das große Konsequenzen hat, wenn es nicht einsetzt.
>
> Die Konsequenz daraus ist, das du die Chance verschenkst deinen Code
> (typ) sicherer zu machen ;-)
Kannst du genauer erklären, wann ICH eine Chance verschenkt habe? Oder 
was wolltest du mit dem Beitrag zum Ausdruck bringen?

von Rolf M. (rmagnus)


Lesenswert?

mh schrieb:
> Ich sehe Nichts, das große Konsequenzen hat, wenn es nicht einsetzt.

Wenn man es nicht einsetzt, dann bleibt man aber bei C++03 stehen. Also: 
Entweder lernt man die neuen Features, oder man benutzt sie halt nicht.

mh schrieb:
> Es ging um C++20, also sind nur die Änderungen seit C++17 relevant.

Es ging um C++20 im Vergleich zu dem Stand vor C++11.

von S. R. (svenska)


Lesenswert?

mh schrieb:
> Ich sehe Nichts, das große Konsequenzen hat, wenn es nicht einsetzt.

Es geht nicht darum, ob ich es einsetze, sondern ob jemand anderes das 
in der gleichen Codebasis einsetzt, mit der ich arbeiten muss. Und 
selbst wenn ich einsetze heißt das noch lange nicht, dass der Rest des 
Teams damit arbeiten kann.

Oliver S. schrieb:
> Nun ja, mit concepts, auto überall, constexpr lambdas, ranges, und was
> da alles noch so an Kleinigkeiten dabei ist, sieht der Code doch schon
> sehr anders aus als das, was man mit C++11 so gebastelt hat.

Schöne Zusammenfassung.

Genau das macht modernes C++20 effektiv zu einer völlig anderen 
Programmiersprache als der, die ich vor garnicht so vielen Jahren mal 
als "C++" kennengelernt habe.

von mh (Gast)


Lesenswert?

Rolf M. schrieb:
> mh schrieb:
>> Ich sehe Nichts, das große Konsequenzen hat, wenn es nicht einsetzt.
>
> Wenn man es nicht einsetzt, dann bleibt man aber bei C++03 stehen. Also:
> Entweder lernt man die neuen Features, oder man benutzt sie halt nicht.
Warum erwähnst du jetzt C++03? Willst du wirklich aussagen, dass man 
entweder alle neuen Feauters einsetzt, oder sich auf einen fast 20 Jahre 
alten Standard beschränken muss? Es gibt nichts was da zwischen liegt? 
Ich kann mir nicht vorstellen, dass du das wirklich sagen willst.

> mh schrieb:
>> Es ging um C++20, also sind nur die Änderungen seit C++17 relevant.
>
> Es ging um C++20 im Vergleich zu dem Stand vor C++11.
Dann verfolge die Kette mal bis zu dem Betrag, aus dem dieser Satz von 
mir stammt. Um es einfacher zu machen:

mh schrieb:
> Mark B. schrieb:
>> Wilhelm M. schrieb:
>>> Mir kommen echt die Tränen, dass Ihr Euch in so viele neue Dinge
>>> einarbeiten müsst.
>>
>> Du beherrschst sämtliche Features von C++20? :-)
>
> Die 2h, um einmal alle Änderungen zu überfliegen, sollte man haben. Den
> Rest kann man machen, wenn man es braucht oder die Zeit hat.

Es ging also sehr wohl um C++20.

von mh (Gast)


Lesenswert?

S. R. schrieb:
> mh schrieb:
>> Ich sehe Nichts, das große Konsequenzen hat, wenn es nicht einsetzt.
>
> Es geht nicht darum, ob ich es einsetze, sondern ob jemand anderes das
> in der gleichen Codebasis einsetzt, mit der ich arbeiten muss. Und
> selbst wenn ich einsetze heißt das noch lange nicht, dass der Rest des
> Teams damit arbeiten kann.

Es ist jetzt also die Schuld des Standardkomitees, dass ihr euch im Team 
nicht auf ein gemeinsames Werkzeug einigen könnt?

> Oliver S. schrieb:
>> Nun ja, mit concepts, auto überall, constexpr lambdas, ranges, und was
>> da alles noch so an Kleinigkeiten dabei ist, sieht der Code doch schon
>> sehr anders aus als das, was man mit C++11 so gebastelt hat.
>
> Schöne Zusammenfassung.
>
> Genau das macht modernes C++20 effektiv zu einer völlig anderen
> Programmiersprache als der, die ich vor garnicht so vielen Jahren mal
> als "C++" kennengelernt habe.
C++11 oder C++20. Wo sind plötzlich C++14 und C++17 geblieben? Habt ihr 
euch wirklich seit 6 Jahren nicht mehr weitergebildet? Dann kann es ja 
kein besonders wichtiges Werkzeug sein.

von S. R. (svenska)


Lesenswert?

mh schrieb:
> Dann verfolge die Kette mal bis zu dem Betrag,
> aus dem dieser Satz von mir stammt.

Der Thread handelt von den "ganzen Erweiterungen von C++11/14/17".

Die Beschränkung auf "die Unterschiede zwischen C++17 und C++20" kam 
tatsächlich von dir, hat aber sonst niemanden interessiert. ;-)

von mh (Gast)


Lesenswert?

S. R. schrieb:
> mh schrieb:
>> Dann verfolge die Kette mal bis zu dem Betrag,
>> aus dem dieser Satz von mir stammt.
>
> Der Thread handelt von den "ganzen Erweiterungen von C++11/14/17".
>
> Die Beschränkung auf "die Unterschiede zwischen C++17 und C++20" kam
> tatsächlich von dir, hat aber sonst niemanden interessiert. ;-)

Wenn Mark B. schreibt "Du beherrschst sämtliche Features von C++20",
kommt das also plötzlich von mir?

von Hans-Georg L. (h-g-l)


Lesenswert?

mh schrieb:
> S. R. schrieb:
>> mh schrieb:
>>> Dann verfolge die Kette mal bis zu dem Betrag,
>>> aus dem dieser Satz von mir stammt.
>>
>> Der Thread handelt von den "ganzen Erweiterungen von C++11/14/17".
>>

So sehe ich das auch.

>> Die Beschränkung auf "die Unterschiede zwischen C++17 und C++20" kam
>> tatsächlich von dir, hat aber sonst niemanden interessiert. ;-)
>
> Wenn Mark B. schreibt "Du beherrschst sämtliche Features von C++20",
> kommt das also plötzlich von mir?

Es kam nicht von dir, aber du hast dich auf die Reduktion darauf 
eingelassen.

Wenn man generisch programmiert ist es auf jeden Fall sinnvoll die C++20 
Constraints und concepts sich anzuschauen.

von Wilhelm M. (wimalopaan)


Lesenswert?

Was wollen wir?

[] Wir wollen eine Programmiersprache, die sich nicht ändert, damit wir 
unser Denken nicht ändern müssen.

[] Wir wollen eine Programmiersprache, die vollständig abwärtskompatibel 
ist, damit der alte Code immmer noch compiliert werden kann, und ohne 
neue Bugs läuft. Alte Bugs sollen erhalten bleiben.

[] Wir wollen ein Programmiersprache, die top-modern ist, und die 
allerneuesten Features, die diskutiert werden, inkludiert.

[] Wir wollen eine Programmiersprache, die nicht aufgebläht ist, weil 
regelmäßig alte Features entfernt werden.

[] Wir wollen eine Programmiersprache, die möglichst wenig Features hat, 
damit sie leicht zu lernen ist.

[] Wir wollen eine Programmiersprache, die immer den schnellsten 
Maschinen-Code und auch sonstwie den effizientesten Ressourcengebrauch 
liefert.

[] Wir wollen ein Programmiersprache, dessen Syntax uns persönlich am 
besten passt.

[] Wir wollen eine Programmiersprache, die sich nicht ändert, damit wir 
nichts neues lernen müssen.

[] Wir wollen eine Programmiersprache, die nicht von einem Gremium 
definiert wird, damit wir uns nicht engagieren müssen.

[] Wir wollen eine Programmiersprache, die wir erlernen können, ohne zu 
lernen bzw. ihr Historie oder ihre grundlegenden Ideen zu verstehen.

[] ...

[] ...

Schaut aus, wie im richtigen Leben ;-)

von Programmierer (Gast)


Lesenswert?

[x] Wir wollen C89 ("ANSI-C") weil wir das im Studium lernen mussten und 
zu faul sind irgendwas anderes zu lernen. Außerdem Simulink weil das so 
schön bunt ist.

von Wilhelm M. (wimalopaan)


Lesenswert?

Im übrigen ist das hier Diskutierte eher ein Problem der "alten Säcke" 
als der "jungen Hüpfer". Die Jungen kommen oft aus eine Script-Welt o.ä. 
und sind "überglücklich" über die Möglichkeiten des "modernen C++". Die 
Umständlichkeiten des "alten C++" erzählt man einmal, und dann lassen 
sie die auch sofort gerne links liegen.

von Eumel (Gast)


Lesenswert?

Also entweder gefällt mir ausnahmslos alles an C++, oder ich bin 
lernfaul, alt oder sonst wie minderbemittelt. Ist das in etwa richtig 
zusammengefasst?

von Oliver S. (oliverso)


Lesenswert?

Wenn’s für dich so passt, dann ja.

Oliver

von Wilhelm M. (wimalopaan)


Lesenswert?

Eumel schrieb:
> Also entweder gefällt mir ausnahmslos alles an C++, oder ich bin
> lernfaul, alt oder sonst wie minderbemittelt. Ist das in etwa richtig
> zusammengefasst?

Wenn Du das darin liest, dann sicherlich ja ;-)

von Hans-Georg L. (h-g-l)


Lesenswert?

Wilhelm M. schrieb:
> Im übrigen ist das hier Diskutierte eher ein Problem der "alten Säcke"
> als der "jungen Hüpfer". Die Jungen kommen oft aus eine Script-Welt o.ä.
> und sind "überglücklich" über die Möglichkeiten des "modernen C++". Die
> Umständlichkeiten des "alten C++" erzählt man einmal, und dann lassen
> sie die auch sofort gerne links liegen.
Ich bin so ein alter Sack, der mit programmieren angefangen hat als der 
8080 Assembler noch vom Lochstreifen eingelesen wurde, mit "klassischem" 
C++ später sein Geld verdient hat und schon ein Weilchen Rentner ist 
;-).
Wenn du keine Erfahrung hast wie man ein System in wieder verwendbare 
Klassen aufteilt nützen dir die tollsten Features einer Sprache nichts. 
Ob Erfahrungen aus der Script Welt dabei helfen sei mal dahin gestellt.

Im Moment versuche ich meine C Programme für die STM32 MC auf C++ um zu 
stellen und da sind Klassen eher unwichtig und die neuen Features sehr 
willkommen. Allerdings unterstützt die STM Cube-IDE nur den gcc 9.3.1
und somit C++17 aber es lässt sich nur -std=c++14 einstellen. Diese 
blöden Eclipse IDEs sind alle so vermurkst, die Build Systeme und die 
Debugger Unterstützung sind untereinander nicht kompatibel. Den gcc 
scheint es aber nicht zu stören wenn man zusätzlich noch -std=c++17 
hinzu fügt.

Ich bin immer noch dabei zu lernen, auch wenn es langsamer geht ;-)

von Mark B. (markbrandis)


Lesenswert?

Hans-Georg L. schrieb:
> Im Moment versuche ich meine C Programme für die STM32 MC auf C++ um zu
> stellen und da sind Klassen eher unwichtig und die neuen Features sehr
> willkommen.

So sehe ich es auch: OOP auf Mikrocontrollern in Embedded Systemen 
braucht man oft nicht, bzw. bringt für viele typische Anwedungsfälle 
(z.B. Abarbeiten eines Schnittstellenprotokolls) oft keine nennenswerten 
Vorteile gegenüber prozeduraler Programmierung. Aber C++ ist eben nicht 
nur OOP. Man sagt ja auch, es sei eine "multi-paradigm" 
Programmiersprache.

von Wilhelm M. (wimalopaan)


Lesenswert?

Hans-Georg L. schrieb:
> Im Moment versuche ich meine C Programme für die STM32 MC auf C++ um zu
> stellen und da sind Klassen eher unwichtig und die neuen Features sehr
> willkommen.

Klassen sind an der Stelle sehr(!) wichtig. Und zwar als die Basis der 
Meta-Programmierung. Denn Meta-Funktionen F: type -> type sind im 
allgemeinen als Klassentemplates bzw. Spezialisierungen derselben 
realisiert.

Was Du allerdings meinst mit Deiner Aussage ist ein Teilbereich der OOP, 
nämlich Laufzeitpolymorphie. Das mag für den µC Bereich eher weniger 
wichtig sein (in meinen Augen ist es das). Wobei es auch da Ersatz gibt, 
der auf µC besser geeignet ist.

von Wilhelm M. (wimalopaan)


Lesenswert?

Mark B. schrieb:
> Man sagt ja auch, es sei eine "multi-paradigm"
> Programmiersprache.

Genau: und klassische OOP ist nur ein Teil davon!

Aber selbst von man klassische OOP nicht einsetzt, heißt dass nicht, 
dass Klassen (in C++) unnütz sind ...

von Hans-Georg L. (h-g-l)


Angehängte Dateien:

Lesenswert?

Wilhelm M. schrieb:
> Hans-Georg L. schrieb:
>> Im Moment versuche ich meine C Programme für die STM32 MC auf C++ um zu
>> stellen und da sind Klassen eher unwichtig und die neuen Features sehr
>> willkommen.
>
> Klassen sind an der Stelle sehr(!) wichtig. Und zwar als die Basis der
> Meta-Programmierung. Denn Meta-Funktionen F: type -> type sind im
> allgemeinen als Klassentemplates bzw. Spezialisierungen derselben
> realisiert.
>
> Was Du allerdings meinst mit Deiner Aussage ist ein Teilbereich der OOP,
> nämlich Laufzeitpolymorphie. Das mag für den µC Bereich eher weniger
> wichtig sein (in meinen Augen ist es das). Wobei es auch da Ersatz gibt,
> der auf µC besser geeignet ist.

Wenn ich mich undeutlich ausdrücke darfst du mich gerne korrigieren ;-)
Ich lerne gerne dazu ...

Mein Bestreben war es erst einmal das C Konstrukt :
RCC->AHB1ENR = GPIOA | 1<<GPIOB | 1<<GPIOC /*usw...*/ ;
durch eine typsichere und einfach verständliche C++ Version zu ersetzen.

Meine Version:
AHB1_EnableClock(GPIOA_CLK,GPIOB_CLK,GPIOC_CLK);

Meine Datentypen sind enums und die kann man in diesem Zusammenhang auch 
als Klassen sehen.

Anbei noch ein Code Ausschnitt dazu.

ps .. die enums lassen sich auch noch nach read/write only erweitern

: Bearbeitet durch User
von Hau wech den Scheiss (Gast)


Lesenswert?

Hans-Georg L. schrieb:

> Anbei noch ein Code Ausschnitt dazu.

Zeile 40 enthält einen Fehler.

von Hau wech den Scheiss (Gast)


Lesenswert?

Hans-Georg L. schrieb:
> Anbei noch ein Code Ausschnitt dazu.

In Zeile 53 ist auch ein Fehler.

von Hans-Georg L. (h-g-l)


Lesenswert?

Hau wech den Scheiss schrieb:
> Hans-Georg L. schrieb:
>
>> Anbei noch ein Code Ausschnitt dazu.
>
> Zeile 40 enthält einen Fehler.

Danke.

Das ist nur ein erster Versuch im Endeffekt möchte ich diese 
Declarationen wenn ich mir über die Strukturierung der namespaces klar 
bin, automatisch aus dem svd File erzeugen aber auch diese sind nicht 
unbedingt Fehlerfrei.

von Hau wech den Scheiss (Gast)


Lesenswert?

Hans-Georg L. schrieb:
>  automatisch aus dem svd File erzeugen aber auch diese sind nicht
> unbedingt Fehlerfrei.

Vielleicht einen Plausibilitätscheck drüber laufen lassen, der auf 
doppelte vergebenen Addressen schaut.

Misaligment für dword-Adressen wäre auch so ein Check der sich anbietet, 
aber letzteres sollte doch auch der Assembler finden und als Warning 
bemeckern. Vielleicht auch aus den pdf-docs mit tesseract rauspulen wenn 
man den mitgelieferten Headern nicht trauen kann.

von Hans-Georg L. (h-g-l)


Lesenswert?

Hau wech den Scheiss schrieb:
> Hans-Georg L. schrieb:
>>  automatisch aus dem svd File erzeugen aber auch diese sind nicht
>> unbedingt Fehlerfrei.
>
> Vielleicht einen Plausibilitätscheck drüber laufen lassen, der auf
> doppelte vergebenen Addressen schaut.
>
> Misaligment für dword-Adressen wäre auch so ein Check der sich anbietet,
> aber letzteres sollte doch auch der Assembler finden und als Warning
> bemeckern. Vielleicht auch aus den pdf-docs mit tesseract rauspulen wenn
> man den mitgelieferten Headern nicht trauen kann.
 Assembler hat nichts gefunden

nochmal mit datenblatt verglichen, das müsst jetzt stimmen
Es geht übrigends um den STM32F411 (Blackpill)

<code>
    namespace Regs {
        constexpr reg32_t RCC_BASE         AHB1PERIPH_BASE + 0x3800UL;
        // RCC register
        constexpr reg32_t RCC_CR                = RCC_BASE;
        constexpr reg32_t RCC_PLLCFGCR          = RCC_BASE + 0x04U;
        constexpr reg32_t RCC_CFGCR             = RCC_BASE + 0x08U;
        constexpr reg32_t RCC_CIR               = RCC_BASE + 0x0CU;
        constexpr reg32_t RCC_AHB1RSTR          = RCC_BASE + 0x10U;
        constexpr reg32_t RCC_AHB2RSTR          = RCC_BASE + 0x14U;
        constexpr reg32_t RCC_APB1RSTR          = RCC_BASE + 0x20U;
        constexpr reg32_t RCC_APB2RSTR          = RCC_BASE + 0x24U;
        constexpr reg32_t RCC_AHB1ENR           = RCC_BASE + 0x30U;
        constexpr reg32_t RCC_AHB2ENR           = RCC_BASE + 0x34U;
        constexpr reg32_t RCC_APB1ENR           = RCC_BASE + 0x40U;
        constexpr reg32_t RCC_APB2ENR           = RCC_BASE + 0x44U;
        constexpr reg32_t RCC_AHB1LPENR         = RCC_BASE + 0x50U;
        constexpr reg32_t RCC_AHB2LPENR         = RCC_BASE + 0x54U;
        constexpr reg32_t RCC_APB1LPENR         = RCC_BASE + 0x60U;
        constexpr reg32_t RCC_APB2LPENR         = RCC_BASE + 0x64U;
        constexpr reg32_t RCC_BDCR              = RCC_BASE + 0x70U;
        constexpr reg32_t RCC_CSR               = RCC_BASE + 0x74U;
        constexpr reg32_t RCC_SSCGR             = RCC_BASE + 0x80U;
        constexpr reg32_t RCC_PLLI2SCFGR        = RCC_BASE + 0x84U;
  }

</code>

: Bearbeitet durch User
von Hans-Georg L. (h-g-l)


Lesenswert?

Hau wech den Scheiss schrieb:
> Hans-Georg L. schrieb:
>>  automatisch aus dem svd File erzeugen aber auch diese sind nicht
>> unbedingt Fehlerfrei.
>
> Vielleicht einen Plausibilitätscheck drüber laufen lassen, der auf
> doppelte vergebenen Addressen schaut.
>
> Misaligment für dword-Adressen wäre auch so ein Check der sich anbietet,
> aber letzteres sollte doch auch der Assembler finden und als Warning
> bemeckern. Vielleicht auch aus den pdf-docs mit tesseract rauspulen wenn
> man den mitgelieferten Headern nicht trauen kann.

Guter Tip, das könnte ich dann alles in mein Programm einbauen das die 
svd Files einliest.

von Wilhelm M. (wimalopaan)


Lesenswert?

Hans-Georg L. schrieb:
> Wenn ich mich undeutlich ausdrücke darfst du mich gerne korrigieren ;-)
> Ich lerne gerne dazu ...
>
> Mein Bestreben war es erst einmal das C Konstrukt :
> RCC->AHB1ENR = GPIOA | 1<<GPIOB | 1<<GPIOC /*usw...*/ ;
> durch eine typsichere und einfach verständliche C++ Version zu ersetzen.

Wunderbar. Das ist der erste Schritt.
1
    template<reg32_t ra,typename T, typename ... Args>
2
    requires((... && std::is_same<Args, T>{}))
3
    constexpr void set_bit(Args ... args ) {
4
//        static_assert((... && std::is_same<Args, T>()));   // parameter test
5
        reg32_t mask =(... | args);                       // folding OR
6
        *reinterpret_cast<volatile reg32_t*>(ra) |= mask; // set bitmask
7
    };

Ich habe Dein static_assert() mal durch ein adhoc-requirement ersetzt. 
DAs macht die Fehlermeldung ggf. intuitiver.

Desweiteren ist es eher unüblich, die traits zu instanziieren und dann 
implizit nach bool zu koinvertieren.
1
    template<reg32_t ra,typename T, typename ... Args>
2
    constexpr void clear_bit(Args ... args ) {
3
        static_assert((... && std::is_same_v<Args, T>));   // parameter test
4
        reg32_t mask =(... | args);                       // folding OR
5
        *reinterpret_cast<volatile reg32_t*>(ra) &= ~mask; // clear bitmask
6
    };

Habe das mal kurz hier geändert, nur als Idee.

Du könntest dann auch noch scoped-enums einsetzen.

Statt extra Funktionen für set/clear einzuführen, kannst Du nach dem 
gleichen Muster ggf. die Op-|| und Op-&& für die Typen überladen. Ggf. 
sogar als template und per enable-if/concept ganz gezielt aktivieren für 
die Typen, für die Du das haben möchtest.

von Hau wech den Scheiss (Gast)


Lesenswert?

Hans-Georg L. schrieb:
>> Misaligment für dword-Adressen wäre auch so ein Check der sich anbietet,
>> aber letzteres sollte doch auch der Assembler finden und als Warning
>> bemeckern. Vielleicht auch aus den pdf-docs mit tesseract rauspulen wenn
>> man den mitgelieferten Headern nicht trauen kann.
>
> Guter Tip, das könnte ich dann alles in mein Programm einbauen das die
> svd Files einliest.

Da könnte aber auch eine IMHO sinnvolle Erweiterung der 
Programmiersprache sein, das man Typen nicht nur entsprechend Bitbreite 
und Vorzeichen definiert, sondern auch nach dem 'Alignment'. Also Hier 
einen Typen für die Registeraddressen vorsehen der nur Konstanten endend 
auf 0x0, 0x4, 0x8 oder 0xC zulässt.

Scripte zur Fehlersuche, -vermeidung über den Code einzusetzen scheint 
mir aber ein universeller Ansatz, der auch für andere Sprachen 
einsetzbar ist. Also generische Prüfscripte für jede 
Prozessorarchitektur (auch FPGA eigene Prozessoren) die den C/C++/? Code 
auf 'offensichtliche Merkwürdigkeiten' durchsucht.

Die 'merkwürdigen Stellen' hatte ich schon in einer Minute drüberfliegen 
gefunden, das Raussuchen von Datenblatt und Vergleich hat dann ein 
Mehrfaches an Zeit gekostet.

von Programmierer (Gast)


Lesenswert?

Hau wech den Scheiss schrieb:
> Da könnte aber auch eine IMHO sinnvolle Erweiterung der
> Programmiersprache sein, das man Typen nicht nur entsprechend Bitbreite
> und Vorzeichen definiert, sondern auch nach dem 'Alignment'.

Das haben C und C++ schon lange. Kann man seit C++11 (na sowas) auch 
explizit festlegen mit dem "alignas" Keyword. Allerdings ist der 
manuelle Zugriff auf beliebige Speicherstellen durch Umcasten von 
Adressen sowieso nicht vorgesehen und ein Missbrauch der Sprache, 
weshalb da auch die Adresse nicht geprüft wird.

Hau wech den Scheiss schrieb:
> Scripte zur Fehlersuche, -vermeidung über den Code einzusetzen scheint
> mir aber ein universeller Ansatz, der auch für andere Sprachen
> einsetzbar ist.

Diese sind allerdings schwierig in komplexe Sprachen zu integrieren, 
weil man hier alle Sprachkonstrukte korrekt verarbeiten muss. 
Glücklicherweise kann man in C++ dank Metaprogrammierung und statischer 
Typisierung eine Menge Prüfungen direkt in der Sprache vornehmen, die 
dann bei jedem kompilier-Vorgang direkt vom Compiler geprüft werden, 
ganz ohne externe Skripte. Hier z.B. mit:
1
static_assert ((ra % 4) == 0, "Address not aligned");

Hier zeigt sich der Nachteil der klassischen C-Programmierung: Diverse 
Funktionalität muss man durch externe Tools nachbilden (Fehlersuche, 
Code-Generierung), während man in mächtigeren Sprachen wie C++ diese 
Dinge einfach direkt schon integriert hat.

von temp (Gast)


Lesenswert?

Was mich immer wieder nervt ist, dass es praktisch unmöglich ist 
einfache Ausdrücke wie:
GPIOA->BSS = 1<<5;
So z.b. in Templates zu verpacken dass am Ende ein asm Zweizeiler dabei 
rauskommt auch wenn die Optimierung des Kompilers zum Debuggen 
ausgeschaltet ist. Mit eingeschalteter Optimierung ist das kein Problem. 
Oder anders gefragt, wie baut man z.B. eine Gpio Klasse so, dass sie 
nicht während des Debuggens 5 mal langsamer wird? Leider ist es bis 
heute so, will man beim Debuggen und im Release annähernd gleiche 
Resultate beim erzeugten Code, geht um ein C—Macro auch 2021 immer noch 
kein Weg vorbei. Ich lasse mich aber gern eines besseren Belehren.

von Wilhelm M. (wimalopaan)


Lesenswert?

temp schrieb:
> Leider ist es bis
> heute so, will man beim Debuggen und im Release annähernd gleiche
> Resultate beim erzeugten Code, geht um ein C—Macro auch 2021 immer noch
> kein Weg vorbei. Ich lasse mich aber gern eines besseren Belehren.

Du kannst doch sicher -Og benutzen. Das reicht aus, um die 
template-calls (inline) zu optimieren.

Die obige Lösung kannst Du natürlich verbessern, und möglichst viel zur 
Compilezeit erledigen:
1
        template<reg32_t adr, auto ... Args>
2
        constexpr void enable() {
3
            constexpr reg32_t mask = (... | Args);            
4
            *reinterpret_cast<volatile reg32_t*>(adr) |= mask;
5
        }

(Oder Du schreibst noch eine Meta-Funktion für den |-Op, ist wegen 
constexpr aber unnötig, etwa static_or<...>).

Mit dem static_or dann:
1
   *reinterpret_cast<volatile reg32_t*>(Regs::RCC_AHB1ENR) = static_or_v<CRC_CLK,GPIOA_CLK,GPIOB_CLK,GPIOC_CLK,GPIOD_CLK>;

bzw:
1
GPIOA->BSS = static_or_v<...>;

Auf die Typ-Prüfungen habe ich jetzt verzichtet, kann man aber ja noch 
obigem Muster ebenfalls einbauen.

von Hans-Georg L. (h-g-l)


Lesenswert?

Programmierer schrieb:
...
>
1
static_assert ((ra % 4) == 0, "Address not aligned");
>
 Werde ich einbauen Danke.

von Hans-Georg L. (h-g-l)


Lesenswert?

Wilhelm M. schrieb:
> temp schrieb:
>> Leider ist es bis
>> heute so, will man beim Debuggen und im Release annähernd gleiche
>> Resultate beim erzeugten Code, geht um ein C—Macro auch 2021 immer noch
>> kein Weg vorbei. Ich lasse mich aber gern eines besseren Belehren.
>
> Du kannst doch sicher -Og benutzen. Das reicht aus, um die
> template-calls (inline) zu optimieren.
>
> Die obige Lösung kannst Du natürlich verbessern, und möglichst viel zur
> Compilezeit erledigen:
>
> [c]
>         template<reg32_t adr, auto ... Args>
>         constexpr void enable() {
>             constexpr reg32_t mask = (... | Args);
>             *reinterpret_cast<volatile reg32_t*>(adr) |= mask;
>         }
Wilhelm M. schrieb:
>     template<reg32_t ra,typename T, typename ... Args>
>     requires((... && std::is_same<Args, T>{}))
>     constexpr void set_bit(Args ... args ) {
> //        static_assert((... && std::is_same<Args, T>()));   //
> parameter test
>         reg32_t mask =(... | args);                       // folding OR
>         *reinterpret_cast<volatile reg32_t*>(ra) |= mask; // set bitmask
>     };
>
> Ich habe Dein static_assert() mal durch ein adhoc-requirement ersetzt.
> DAs macht die Fehlermeldung ggf. intuitiver.
>
requires ist c++20 und wie schon geschrieben ist mein compiler auf c++17 
begrenzt aber eine sinnvolle Fehlermeldung könnte ich noch ausgeben

> Desweiteren ist es eher unüblich, die traits zu instanziieren und dann
> implizit nach bool zu koinvertieren.
>     template<reg32_t ra,typename T, typename ... Args>
>     constexpr void clear_bit(Args ... args ) {
>         static_assert((... && std::is_same_v<Args, T>));   // parameter
> test
>         reg32_t mask =(... | args);                       // folding OR
>         *reinterpret_cast<volatile reg32_t*>(ra) &= ~mask; // clear
> bitmask
>     };
>
Finde den Unterschied nicht so leicht für ältere Augen ;-)

> Du könntest dann auch noch scoped-enums einsetzen.

Das wollte ich sowieso und vorsichtshalber den underlying typ auf 
unsigned ändern. Nicht das der Compiler, durch Bit-Schiebereien, 
irgendwann auf die Idee kommt Bit31 als Vorzeichen aufzufassen.

> Statt extra Funktionen für set/clear einzuführen, kannst Du nach dem
> gleichen Muster ggf. die Op-|| und Op-&& für die Typen überladen. Ggf.
> sogar als template und per enable-if/concept ganz gezielt aktivieren für
> die Typen, für die Du das haben möchtest.

Ich bin ehrlich gesagt kein großer Freund davon erstmal alles zusammen 
zu fassen und dann wieder mit if zu zerlegen. Mal sehen.. es gibt ja 
noch andere Registertypen.

von Wilhelm M. (wimalopaan)


Lesenswert?

Hans-Georg L. schrieb:
> Finde den Unterschied nicht so leicht für ältere Augen ;-)
1
std::is_same_v<Args, T>

statt
1
std::is_same<Args, T>()

von Wilhelm M. (wimalopaan)


Lesenswert?

Hans-Georg L. schrieb:
> Ich bin ehrlich gesagt kein großer Freund davon erstmal alles zusammen
> zu fassen und dann wieder mit if zu zerlegen

Nicht if, sondern enable-if.

von temp (Gast)


Lesenswert?

Mit -Og hab ich auch schon gespielt. Kennen aber auch nicht alle 
Compiler. Das kann aber auch nicht die Lösung sein. Mit #pragma kann man 
auch für bestimmte Teile im Code eine gezielt anderen Optimierung 
verwenden. Allerdings brachte das bisher keinen Erfolg. Ich mache jetzt 
schon nur bei den Dateien die ich Debugge den Schalter auch an, sonst 
passt ein Release Code von 20k schon nicht mehr in 32k Flash. Trotzdem 
nervt mich das einfach nur und hindert mich auch daran Lobeshymnen auf 
C++ im embedded Bereich zu singen. Was nützt das alles, wenn man zwar 
schöne Programme schreiben kann, die am Ende aber nicht vernünftig 
debugbar sind oder beim Debuggen 3 mal so groß. Bei der PC 
Programmierung stört mich das nicht.

von Wilhelm M. (wimalopaan)


Lesenswert?

Hans-Georg L. schrieb:
> requires ist c++20 und wie schon geschrieben ist mein compiler auf c++17
> begrenzt

Ist das ein gcc? Und c++2a kann der noch nicht?

Das requires dannst Du ggf. auch durch ein enable-if ersetzen, um die 
Instantiierung aus dem overload-set zu entfernen.

von Hans-Georg L. (h-g-l)


Lesenswert?

Sorry, ich wollte diesen Thread nicht für mich kapern aber ich denke es 
geht immer noch um allgemeines C++ Wissen.

von Hans-Georg L. (h-g-l)


Lesenswert?

Wilhelm M. schrieb:
> Hans-Georg L. schrieb:
>> requires ist c++20 und wie schon geschrieben ist mein compiler auf c++17
>> begrenzt
>
> Ist das ein gcc? Und c++2a kann der noch nicht?
>
> Das requires dannst Du ggf. auch durch ein enable-if ersetzen, um die
> Instantiierung aus dem overload-set zu entfernen.

Es ist wie geschrieben ein gcc 9.3.1 der sich mit STM CUBE-IDE (Eclipse) 
maximal auf c++14 stellen lässt. Aber man kann zusätzlich c++17 
einfügen, was er dann auch benutzt. Das funktioniert auch mit 
-std=c++2a. :-)

gibt es auch eine Option zum Removen einer anderen Option ? Also um das 
-std=c++14 los zu werden.

: Bearbeitet durch User
von Wilhelm M. (wimalopaan)


Lesenswert?

Hans-Georg L. schrieb:
> Es ist wie geschrieben ein gcc 9.3.1

Sorry, hatte ich nicht gelesen.

Im übrigen ist das Compilieren des Gcc aus dem git-repo kein Problem.

von Programmierer (Gast)


Lesenswert?

temp schrieb:
> Was nützt das alles, wenn man zwar
> schöne Programme schreiben kann, die am Ende aber nicht vernünftig
> debugbar sind oder beim Debuggen 3 mal so groß.

Du könntest zur Entwicklung/Debugging einen größeren Controller mit mehr 
Flash nehmen und dann für die Serienproduktion wo es auf's Geld ankommt 
den kleinstmöglichen. Mit __attribute__((always_inline)) kann man sich 
auch schon gut behelfen, oder indem man nur einzelne Dateien ohne 
Optimierung kompiliert. IMO ist es aber auch gar nicht so schlimm 
optimierten Code zu debuggen. Der Cursor springt halt ein bisschen 
lustig herum.

von Oliver S. (oliverso)


Lesenswert?

Hans-Georg L. schrieb:
> gibt es auch eine Option zum Removen einer anderen Option ? Also um das
> -std=c++14 los zu werden.

Das ist ein Problem des Cube-Eclipse-Plugins, nicht des Compilers. Da 
musst du dich an die dazu zuständigen Programmierer wenden.

Andere Plugins bieten in dem Auswahlfeld zumindest ein leeres Feld an.

Oliver

von Hans-Georg L. (h-g-l)


Lesenswert?

Oliver S. schrieb:
> Hans-Georg L. schrieb:
>> gibt es auch eine Option zum Removen einer anderen Option ? Also um das
>> -std=c++14 los zu werden.
>
> Das ist ein Problem des Cube-Eclipse-Plugins, nicht des Compilers. Da
> musst du dich an die dazu zuständigen Programmierer wenden.
>
> Andere Plugins bieten in dem Auswahlfeld zumindest ein leeres Feld an.
>
> Oliver

Unter Window->Preferences->StmCube->Build gibt es dieses Leerfeld.

Unter den Project->settings aber dann nicht mehr.

Dieses Plugin hat mich schon Nerven gekostet. Ich benutze die Cube IDE 
nur weil man damit problemlos zwischen ST-Link und Segger J-Link 
umschalten kann  und der Build mit dem CubeMX Code kompatibel ist was 
bei anderen, wo man seinen eigenen Compiler einbinden kann leider nicht 
funktioniert. Und anpassen kann man bei allen selbst nichts, sondern 
muss irgendwo betteln das es einer macht.

von Programmierer (Gast)


Lesenswert?

Hans-Georg L. schrieb:
> Ich benutze die Cube IDE
> nur weil man damit problemlos zwischen ST-Link und Segger J-Link
> umschalten kann

Das geht mit dem GNU-MCU-Eclipse Plugin auch.

Hans-Georg L. schrieb:
> und der Build mit dem CubeMX Code kompatibel ist was
> bei anderen, wo man seinen eigenen Compiler einbinden kann leider nicht
> funktioniert.

Wieso das? Die CubeIDE nutzt auch nur den normalen GCC-ARM-Embedded. 
Wenn man den separat herunterlädt (und ggf. in eine beliebige IDE 
einbindet) kann man damit auch den CubeMX-generierten Code kompilieren. 
Wenn das bei dir nicht klappt hast du irgendwas falsch gemacht (Makros 
vergessen oder so).

von Hans-Georg L. (h-g-l)


Lesenswert?

Programmierer schrieb:
> Hans-Georg L. schrieb:
>> Ich benutze die Cube IDE
>> nur weil man damit problemlos zwischen ST-Link und Segger J-Link
>> umschalten kann
>
> Das geht mit dem GNU-MCU-Eclipse Plugin auch.
>
> Hans-Georg L. schrieb:
>> und der Build mit dem CubeMX Code kompatibel ist was
>> bei anderen, wo man seinen eigenen Compiler einbinden kann leider nicht
>> funktioniert.
>
> Wieso das? Die CubeIDE nutzt auch nur den normalen GCC-ARM-Embedded.
> Wenn man den separat herunterlädt (und ggf. in eine beliebige IDE
> einbindet) kann man damit auch den CubeMX-generierten Code kompilieren.
> Wenn das bei dir nicht klappt hast du irgendwas falsch gemacht (Makros
> vergessen oder so).

Es geht nicht um den Code sondern die .(c)project files. Irgendwann hast 
du eine Sammlung von .project files die untereinander nicht kompatibel 
sind.

Diese Baustelle möchte ich nicht mehr öffnen ;-)

von Programmierer (Gast)


Lesenswert?

Hans-Georg L. schrieb:
> Es geht nicht um den Code sondern die .(c)project files. Irgendwann hast
> du eine Sammlung von .project files die untereinander nicht kompatibel
> sind.

Achso. Die sind aber bei Verwendung des GNU-MCU-Plugin flugs angelegt. 
Sich deswegen auf die STM32CubeIDE festzulegen ist Jammern auf hohem 
Niveau.

von temp (Gast)


Lesenswert?

Programmierer schrieb:
> IMO ist es aber auch gar nicht so schlimm
> optimierten Code zu debuggen. Der Cursor springt halt ein bisschen
> lustig herum.

Ne, das ist absoluter Mist. Dann nehme ich lieber printf und überhaupt 
keinen Debugger.
Was nützt es mir einen Breakpoint zu setzen, der sich nicht an die 
Regeln im Code hält weil er halt durch die Optimierung entweder garnicht 
mehr da ist oder nichts mehr mit dem Quellcode zu tun hat. Sollten die 
Werkzeugen nicht dazu da sein mir die Arbeit zu erleichtern? Aber nein 
jetzt muss man sich wieder Gedanken darum machen wie und wann der 
Compiler optimiert? Schöne neue constexpr Welt. Das kann der 
Präprozessor seit 50 Jahren aber besser...
Und das ganze Geschwafel von Typesicherheit ist überflüssig wie ein 
Kropf. Jedenfalls im embedded Bereich. Wenn jemand solche Klötze wie 
boost anbietet, mag das seine Berechtigung haben. Das paradoxe an C++ 
ist hat, mit jeder Erweiterung (c++11/14/17) wurde die Tipparbeit immer 
mehr. Endlos lange Zeilen die mehr verwirren als nützen. Jetzt macht man 
gerade wieder die Rolle Rückwärts und macht neue Erweiterungen damit es 
wieder kürzer wird. Aber schön, es wollen ja schließlich Leute dafür 
bezahlt werden sich immer wieder neue Formen für den Kopf einer for(;;) 
einfallen zu lassen. Am Ende entscheidet sich jeder Entwickler für seine 
persönliche Vorliebe, kriegt er dann mal fremden Code vorgeschmissen ist 
das dann erst mal wie eine neue Sprache. Ich bin beeindruckt.

von Programmierer (Gast)


Lesenswert?

Jaja, früher war alles besser. Hab gar keine Lust den ganzen Blödsinn zu 
widerlegen. Nur für andere Mitleser: Ein strategisch platziertes 
'__BKPT();' bzw. '__asm__ volatile ("bkpt" : : : "memory");' kann im 
Notfall, wenn normale Breakpoints gar nicht funktionieren wollen, 
helfen.

von Wilhelm M. (wimalopaan)


Lesenswert?

temp schrieb:
> Und das ganze Geschwafel von Typesicherheit ist überflüssig wie ein
> Kropf.

Selten so gelacht.

von Yalu X. (yalu) (Moderator)


Lesenswert?

temp schrieb:
> Das paradoxe an C++
> ist hat, mit jeder Erweiterung (c++11/14/17) wurde die Tipparbeit immer
> mehr.

Das stimmt nicht, zumindest nicht in dieser Pauschalität. 7 der 9 von
mir oben genannte Vorzüge von C++≥11

Yalu X. schrieb:
> Die für mich wichtigsten positiven Punkte sind:

führen zu kompakterem und übersichtlicherem Code. Die verbleibenden 2
(Punkte 6 und 7) erfordern zwar etwas mehr Tipparbeit, helfen aber bei
der Aufdeckung von Programmierfehlern, deren Suche sonst i.Allg. mehr
Zeit beansprucht als die paar zusätzlich zu tippenden Zeichen.

: Bearbeitet durch Moderator
von temp (Gast)


Lesenswert?

Programmierer schrieb:
> Jaja, früher war alles besser. Hab gar keine Lust den ganzen Blödsinn zu
> widerlegen. Nur für andere Mitleser: Ein strategisch platziertes
> '__BKPT();' bzw. '__asm__ volatile ("bkpt" : : : "memory");' kann im
> Notfall, wenn normale Breakpoints gar nicht funktionieren wollen,
> helfen.
Das hat nichts mit früher war alles besser zu tun. Solange das "heute 
ist alles viel besser" solche Klimmzüge erfordert die vorher nicht nötig 
waren ist das Neue eben nicht besser. Und wenn das ganze schon bei der 
Implementierung einer einfachen Gpio-Klasse dazu führt, dass ich 
schlechter Debuggen kann, ist und bleibt es Mist. Wie gesagt, vielleicht 
bin ich ja zu blöd und ihr wisst es alle besser. Dann aber bitte ein 
Beispiel liefern wie man dem Compiler beibringen kann seine Berechnungen 
zur Compilierzeit zu machen ohne in der Datei wo es verwendet wird 
expliziet einen Optimierungslevel angeben zu muessen. Das hab ich mal 
kurz mit dem Simulator vom Segger Embedded Studio probiert. Bitte nur 
überprüfte Vorschläge machen.
1
enum eGpioPort
2
{
3
  PA = GPIOA_BASE,
4
  PB = GPIOB_BASE,
5
  PC = GPIOC_BASE,
6
};  
7
8
template<eGpioPort ePort, uint8_t bitPosi>
9
class GpioPin
10
{
11
  constexpr GPIO_TypeDef * GetPort() { return reinterpret_cast<GPIO_TypeDef*>(ePort)); }
12
public:
13
  constexpr void Set() { SET_BIT(GetPort()->BSRR, (1<<bitPosi)); } 
14
  constexpr void Clr() { SET_BIT(GetPort()->BSRR, (1<<(bitPosi+16))); }
15
};
16
17
GpioPin<PA, 5> aLed;
18
19
int main(void) 
20
{
21
  while (1)
22
    {
23
    aLed.Set();
24
    aLed.Clr();
25
    }
26
  return 0;
27
}

Sowas sollte dabei rauskommen.
1
int main(void)
2
{
3
    4804        ldr r0, =0x48000018
4
--- main.cpp -- 127 ----------------------------------------
5
constexpr void Set() { SET_BIT(GetPort()->BSRR, (1<<bitPosi)); }
6
    6801        ldr r1, [r0]
7
    F0410120    orr r1, r1, #32
8
    6001        str r1, [r0]
9
--- main.cpp -- 128 ----------------------------------------
10
constexpr void Clr() { SET_BIT(GetPort()->BSRR, (1<<(bitPosi+16))); }
11
    6801        ldr r1, [r0]
12
    F4411100    orr r1, r1, #0x200000
13
    6001        str r1, [r0]

und nicht sowas bei Optimierung kleiner 2
1
    F2400400    movw r4, #0
2
    F2C20400    movt r4, #0x2000
3
--- main.cpp -- 135 ----------------------------------------
4
while (1)
5
{
6
aLed.Set();
7
    4620        mov r0, r4
8
    F000F804    bl 0x0800042E <_ZN7GpioPinIL9eGpioPort1207959552ELh5EE3SetEv>
9
--- main.cpp -- 138 ----------------------------------------
10
aLed.Clr();
11
    4620        mov r0, r4
12
    F000F80B    bl 0x08000442 <_ZN7GpioPinIL9eGpioPort1207959552ELh5EE3ClrEv>
13
--- main.cpp -- 135 ----------------------------------------
14
while (1)
15
    E7F8        b 0x08000420

Wilhelm M. schrieb:
> Mit dem static_or dann:
>    *reinterpret_cast<volatile reg32_t*>(Regs::RCC_AHB1ENR) =
> static_or_v<CRC_CLK,GPIOA_CLK,GPIOB_CLK,GPIOC_CLK,GPIOD_CLK>;
>
> bzw:
> GPIOA->BSS = static_or_v<...>;
>
> Auf die Typ-Prüfungen habe ich jetzt verzichtet, kann man aber ja noch
> obigem Muster ebenfalls einbauen.

Jetzt muss mir einmal jemand verraten welchen Vorteil es bring so eine 
Konstruktion als Ersatz von
1
SET_BIT(GPIOA->BSRR, GPIO_BSRR_BS0 | GPIO_BSRR_BS4 | GPIO_BSRR_BS5);
zu verwenden. Und sowas wie "Regs::RCC_AHB1ENR" setzt voraus, dass alle 
Registerdefinitionen der CPU neu geschrieben werden müssen. Einen 
Vorteil hat das dann, man lernt seinen Controller gut kennen...

von temp (Gast)


Lesenswert?

muss mein Beispiel nochmal korrigieren:
1
enum eGpioPort
2
{
3
  PA = GPIOA_BASE,
4
  PB = GPIOB_BASE,
5
  PC = GPIOC_BASE,
6
};  
7
8
template<eGpioPort ePort, uint32_t bitPosi>
9
class GpioPin
10
{
11
  constexpr GPIO_TypeDef * GetPort() { return (reinterpret_cast<GPIO_TypeDef*>(ePort)); }
12
public:
13
  constexpr void Set() { GetPort()->BSRR=(1<<bitPosi); } 
14
  constexpr void Clr() { GetPort()->BSRR=(1<<(bitPosi+16)); }
15
};
16
17
GpioPin<PA, 5> aLed;
18
int main(void) 
19
{
20
  while (1)
21
    {
22
    aLed.Set();
23
    aLed.Clr();
24
    }
25
  return 0;
26
}
der erzeugt Code sieht so aus:
1
int main(void)
2
{
3
    4803        ldr r0, =0x48000018
4
    2120        movs r1, #32
5
    F44F1200    mov.w r2, #0x200000
6
--- main.cpp -- 128 ----------------------------------------
7
constexpr void Set() { GetPort()->BSRR=(1<<bitPosi); }
8
    6001        str r1, [r0]
9
--- main.cpp -- 129 ----------------------------------------
10
__attribute__((always_inline))
11
constexpr void Clr() { GetPort()->BSRR=(1<<(bitPosi+16)); }
12
    6002        str r2, [r0]
13
--- main.cpp -- 134 ----------------------------------------
14
GpioPin<PA, 5> aLed;
15
int main(void)
16
{
17
while (1)
18
    E7FC        b 0x08000418

ohne Optimierungslevel werden auch wieder richtige Funktionscalls 
erzeugt.
das "gute Alte" würde so aussehen:
1
  while (1)
2
    {
3
    GPIOA->BSRR=GPIO_BSRR_BS_5; 
4
    GPIOA->BSRR=GPIO_BSRR_BR_5; 
5
    }

und erzeugt unabhängig von der Optimierung den gleichen kurzen 
Assemblercode. Quintessenz: Man erkauft sich mit den Vorteilen des 
"guten Neuen" neue Hürden die beim "verschmäten Alten" garnicht erst 
auftreten.

von Gerade Ast (Gast)


Lesenswert?

Was mir nicht so gut gefällt, ist daß die "uniform initialisation" nicht 
der alten Initialisierung gleichwertig ist:
Das ist OK:
1
    char c('a');
2
    std::string s( 1, c );
3
    assert(s == "a");

Das nicht:
1
    char c{'a'};
2
    std::string s{ 1, c };  // wird als initializer-list betrachtet
3
    assert(s == "a");  // schlägt fehl

Siehe auch:
https://www.youtube.com/watch?v=-AQfQFcXac8  :-)

von Wilhelm M. (wimalopaan)


Lesenswert?

Gerade Ast schrieb:
> Was mir nicht so gut gefällt, ist daß die "uniform initialisation" nicht
> der alten Initialisierung gleichwertig ist:

Und das ist m.E. gut so!

Gerade Ast schrieb:
> Siehe auch:
> https://www.youtube.com/watch?v=-AQfQFcXac8  :-)

Da wird einem ja schlecht: new / delete ;-)

von Yalu X. (yalu) (Moderator)


Lesenswert?

Wilhelm M. schrieb:
> Gerade Ast schrieb:
>> Was mir nicht so gut gefällt, ist daß die "uniform initialisation" nicht
>> der alten Initialisierung gleichwertig ist:
>
> Und das ist m.E. gut so!

Das kann man so sehen oder auch nicht. "Gerade Ast", meine Wenigkeit und
viele andere sehen das offensichtlich anders als du.

Wie ich oben schon schrieb und das Beispiel von "Gerade Ast" deutlich
zeigt, ist die "uniform initialization" alles andere als "uniform".

Rätselfrage: Was gibt folgendes Programm aus?

1
#include <iostream>
2
#include <vector>
3
#include "myclasses.h"
4
5
int main() {
6
7
  // use uniform initialization
8
9
  std::vector<MyClass1> v1{2};
10
  std::vector<MyClass2> v2{2};
11
12
  std::cout << v1.size() << '\n';
13
  std::cout << v2.size() << '\n';
14
15
  // use classic initialization
16
17
  std::vector<MyClass3> v3={2};
18
  std::vector<MyClass4> v4(2);
19
20
  std::cout << v3.size() << '\n';
21
  std::cout << v4.size() << '\n';
22
}

von Oliver S. (oliverso)


Lesenswert?

Das kommt u.a, darauf an, ob MyClassX ein int ist...

Oliver

von Wilhelm M. (wimalopaan)


Lesenswert?

Yalu X. schrieb:
> std::vector<MyClass1> v1{2};

och, da können wir doch gleich weitermachen:
1
std::vector<MyClass1> v1(2, 3);

von Yalu X. (yalu) (Moderator)


Lesenswert?

Oliver S. schrieb:
> Das kommt u.a, darauf an, ob MyClassX ein int ist...

Richtig, oder ob MyClassX einen Konstruktor mit int-Argument hat oder
ob int in MyClassX konvertierbar ist ...

Kurz: Verwendet man die uniform Initialization (v1 und v2), muss man
sich in myclasses.h die Typ- bzw. Klassendefinition von MyClassX
anschauen. Mit der klassischen Initialisierung (v3 und v4) ist das
Verhalten sofort ohne weitere Nachforschungen erkennbar.

Wilhelm M. schrieb:
> std::vector<MyClass1> v1(2, 3);

Was ist daran Besonderes? Bei zwei int-Argumenten ist das erste die
Anzahl der Elemente, und damit ist v1.size() auch ohne Kenntnis der
Klassendefinition eindeutig bestimmt, oder nicht?

von Wilhelm M. (wimalopaan)


Lesenswert?

Yalu X. schrieb:
> Wilhelm M. schrieb:
>> std::vector<MyClass1> v1(2, 3);
>
> Was ist daran Besonderes?

Ich finde die "uniform initialization" konsistenter:
1
    {
2
        std::vector<int> v1(); // ups
3
        std::vector<int> v2(3);
4
        std::vector<int> v3(3, 2); // ist das [2, 2, 2] oder [3, 3] ?
5
        std::vector<int> v4(2, 3);    
6
//        std::vector<int> v5(1, 2, 3);  // warum geht das nicht? 
7
    }
8
    {
9
        std::vector<int> v1{};
10
        std::vector<int> v2{3};
11
        std::vector<int> v3{3, 2};
12
        std::vector<int> v4{2, 3};    
13
        std::vector<int> v5{1, 2, 3};    
14
    }
15
    {
16
        std::array<int, 10> a1; // warum uninitialisierte Elemente?
17
//        std::array<int, 10> a2(3); // warum geht das nicht?
18
    }
19
    {
20
        std::array<int, 10> a1{};
21
        std::array<int, 10> a2{3};
22
        std::array<int, 10> a3{3, 2};
23
        std::array<int, 10> a4{1, 2, 3};
24
    }
25
    {
26
        int i0;   // ?
27
        int i1(); // ups
28
        int i2(4); // s.o. Konsistenz
29
    }
30
    {
31
        int i0{}; // num. Standardwert
32
        int i1{}; 
33
        int i2{4};
34
    }

Yalu X. schrieb:
> Kurz: Verwendet man die uniform Initialization (v1 und v2), muss man
> sich in myclasses.h die Typ- bzw. Klassendefinition von MyClassX
> anschauen. Mit der klassischen Initialisierung (v3 und v4) ist das
> Verhalten sofort ohne weitere Nachforschungen erkennbar.

Was Du hier zitierst, ist nicht ein Thema der uniform-initialization, 
sondern es ist Design-Defekt von std::vector<>. Dieser entsteht durch 
Abwärtskompatibilität, was m.E. auch ein sehr hohes Gut ist.
Der ctor std::vector<T>(int) bzw. std::vector<T>(int, T) gehören nicht 
in die Schnittstelle (zumindest wenn T=int; das sieht man auch daran, 
dass dann die Bedeutung der beiden int nicht klar ist -> Strong-Types). 
Da die jedoch vorher da waren, war es ein Fehler, den 
std::vector<T>(std::initializer_list<T>) hinzuzunehmen (Dies ist ja auch 
schon oft diskutiert worden, aber wegen Abwärtskompatibiliät ... nun 
ja).
Eine Variante wäre es gewesen, in einem anderen Namensraum ein korrekte 
Variante von vector<> anzubieten.

: Bearbeitet durch User
von Yalu X. (yalu) (Moderator)


Lesenswert?

Wilhelm M. schrieb:
> Was Du hier zitierst, ist nicht ein Thema der uniform-initialization,
> sondern es ist Design-Defekt von std::vector<>.

Ob man den Design-Defekt dem std::vector oder der uniform Initialization
zuschreibt, ist IMHO Ansichtssache. Fest steht, dass dieser "Defekt" vor
der Einführung der uniform Initialization nicht in Erscheinung trat.

> Der ctor std::vector<T>(int) bzw. std::vector<T>(int, T) gehören nicht
> in die Schnittstelle (zumindest wenn T=int;

Ich finde den Konstruktor zur Initialisierung des Vektors mit n gleichen
Elementen ganz praktisch.

> das sieht man auch daran, dass dann die Bedeutung der beiden int nicht
> klar ist -> Strong-Types).

Dann wäre also jede Funktion mit zwei oder mehr Argumenten gleichen Typs
defekt? Dann müssten deiner Ansicht nach auch die pow- und die
atan2-Funktion und sogar die Operatoren für Subtraktion und Division
abgeschafft werden? Alternativ könnte man spezielle Datentypen für
Basis, Exponent, y-Koordinate, x-Koordinate, Minuend, Subtrahend,
Dividend und Divisor einführen. Ich bin mir aber sicher: Wenn dies
geschehen würde, würde C++ 90% seiner Gefolgschaft verlieren.

> Da die jedoch vorher da waren, war es ein Fehler, den
> std::vector<T>(std::initializer_list<T>) hinzuzunehmen

Diese ist aber eine der wirklich wertvollen Neuerungen von C++, auf die
ich nicht verzichten wollte. Sie beißt sich auch überhaupt nicht mit
bestehenden Features der Sprache. Der Konflikt entsteht erst im
Zusammenhang mit der – IMHO weitgehend nutzlosen – uniform
Initialization. Letztere wurde nicht vor sondern zusammen mit der
initializer_list (beides C++11) eingeführt, deswegen lag hier der große
Fehler. Diesen Fehler umschiffe ich, indem ich die uniform
Initialization einfach nicht nutze. Es wäre schön, wenn jeder, mit
dessen Quellcode ich arbeiten muss, ebenso handeln würde.

Wilhelm M. schrieb:
> Eine Variante wäre es gewesen, in einem anderen Namensraum ein korrekte
> Variante von vector<> anzubieten.

Eine sehr viel einfachere und elegantere Variante wäre es gewesen, die
uniform Initialization komplett wegzulassen. Für mich scheint das ein
Feature zu sein, dass einfach nicht zu Ende gedacht wurde.

von Rolf M. (rmagnus)


Lesenswert?

Wilhelm M. schrieb:
> Ich finde die "uniform initialization" konsistenter:
>     {
>         std::vector<int> v1(); // ups
>         std::vector<int> v2(3);
>         std::vector<int> v3(3, 2); // ist das [2, 2, 2] oder [3, 3] ?
>         std::vector<int> v4(2, 3);
> //        std::vector<int> v5(1, 2, 3);  // warum geht das nicht?

Ist doch einfach. Der erste Parameter ist hier immer die Größe. Das ist 
auch ganz unabhängig vom Elementtyp des vectors.

>     }
>     {
>         std::vector<int> v1{};
>         std::vector<int> v2{3};
>         std::vector<int> v3{3, 2};
>         std::vector<int> v4{2, 3};
>         std::vector<int> v5{1, 2, 3};
>     }

Wie mache ich einen vector, der 100 default-initialisierte Elemente hat?

Wilhelm M. schrieb:
> Was Du hier zitierst, ist nicht ein Thema der uniform-initialization,
> sondern es ist Design-Defekt von std::vector<>. Dieser entsteht durch
> Abwärtskompatibilität, was m.E. auch ein sehr hohes Gut ist.

Das ist eben die Crux. Ja, Abwärtskompatibilität ist wichtig, und ja, 
viele der neuen Features haben durchaus ihre Berechtigung, aber es macht 
die Sprache auch sehr unübersichtlich.

von Wilhelm M. (wimalopaan)


Lesenswert?

Yalu X. schrieb:
> Wilhelm M. schrieb:
>> Was Du hier zitierst, ist nicht ein Thema der uniform-initialization,
>> sondern es ist Design-Defekt von std::vector<>.
>
> Ob man den Design-Defekt dem std::vector oder der uniform Initialization
> zuschreibt, ist IMHO Ansichtssache.

Ja. Offensichtlich sind wir da schon lange unterschiedlicher Ansicht. 
Wir hatten in anderen Threads schon desöfteren darüber geredet.

> Fest steht, dass dieser "Defekt" vor
> der Einführung der uniform Initialization nicht in Erscheinung trat.

Ja. Für den Grund: s.o.

Dafür gab es andere: most-vexing-parse, keine list-initialisierung, ...

>> Der ctor std::vector<T>(int) bzw. std::vector<T>(int, T) gehören nicht
>> in die Schnittstelle (zumindest wenn T=int;
>
> Ich finde den Konstruktor zur Initialisierung des Vektors mit n gleichen
> Elementen ganz praktisch.

Ich auch ;-) Das kann ja auch so bleiben. Falls Du das anders verstanden 
hast, dann hast Du mich oben falsch verstanden. Es ging um den 
mehrstelligen ctor.

>> das sieht man auch daran, dass dann die Bedeutung der beiden int nicht
>> klar ist -> Strong-Types).
>
> Dann wäre also jede Funktion mit zwei oder mehr Argumenten gleichen Typs
> defekt?

Grundsätzlich: ja. Mehrstellige Funktionen mit denselben, unspezifischen 
Datentypen sind m.E. zu vermeiden.

> Dann müssten deiner Ansicht nach auch die pow- und die
> atan2-Funktion und sogar die Operatoren für Subtraktion und Division
> abgeschafft werden?

Nein. Du musst nicht gleich das Kinde mit dem Bade ausschütten.

>> Da die jedoch vorher da waren, war es ein Fehler, den
>> std::vector<T>(std::initializer_list<T>) hinzuzunehmen
>
> Diese ist aber eine der wirklich wertvollen Neuerungen von C++, auf die
> ich nicht verzichten wollte. Sie beißt sich auch überhaupt nicht mit
> bestehenden Features der Sprache.

Genau.

> Der Konflikt entsteht erst im
> Zusammenhang mit der – IMHO weitgehend nutzlosen – uniform
> Initialization.

Und da bin ich eben anderer Meinung: s.o.. Die beiden ctoren (s.o.) 
hätte man eben abschaffen müssen, was natürlich nicht geht. Da die Sache 
aber seit Urzeiten verbockt ist, hätte man es anders lösen müssen.

> Diesen Fehler umschiffe ich, indem ich die uniform
> Initialization einfach nicht nutze. Es wäre schön, wenn jeder, mit
> dessen Quellcode ich arbeiten muss, ebenso handeln würde.

Ich mache es anders herum wie Du weißt: ich / wir arbeiten nur mit 
uniform-initialisation.

> Wilhelm M. schrieb:
>> Eine Variante wäre es gewesen, in einem anderen Namensraum ein korrekte
>> Variante von vector<> anzubieten.
>
> Eine sehr viel einfachere und elegantere Variante wäre es gewesen, die
> uniform Initialization komplett wegzulassen. Für mich scheint das ein
> Feature zu sein, dass einfach nicht zu Ende gedacht wurde.

Der Fehler ist m.E. gewesen, dass man eben die Konsequenzen speziell im 
std::vector<> u.a. nicht bedacht hat. Das kann passieren. Da das Problem 
aber schon sehr früh entdeckt und bemängelt wurde. hätte man es auf 
verschiedene Weise korrigieren können. Das war der fatale Folgefehler.
Bei den Concepts etwa hat man ja wegen ähnlicher Probleme aus dem TS die 
function-style-concepts wieder entfernt - und zwar kurz vor knapp!

von Wilhelm M. (wimalopaan)


Lesenswert?

Rolf M. schrieb:
> Wilhelm M. schrieb:
>> Ich finde die "uniform initialization" konsistenter:
>>     {
>>         std::vector<int> v1(); // ups
>>         std::vector<int> v2(3);
>>         std::vector<int> v3(3, 2); // ist das [2, 2, 2] oder [3, 3] ?
>>         std::vector<int> v4(2, 3);
>> //        std::vector<int> v5(1, 2, 3);  // warum geht das nicht?
>
> Ist doch einfach. Der erste Parameter ist hier immer die Größe. Das ist
> auch ganz unabhängig vom Elementtyp des vectors.
>
>>     }
>>     {
>>         std::vector<int> v1{};
>>         std::vector<int> v2{3};
>>         std::vector<int> v3{3, 2};
>>         std::vector<int> v4{2, 3};
>>         std::vector<int> v5{1, 2, 3};
>>     }
>
> Wie mache ich einen vector, der 100 default-initialisierte Elemente hat?

Vorschlag:
1
auto v1 = MyVector<MyClass2>{}.fill(10).with(MyClass2{3});
2
auto v2 = MyVector<int>{}.fill(10);
3
auto v3 = fill<MyVector<MyClass2>>(10).with(MyClass2{3});

> Wilhelm M. schrieb:
>> Was Du hier zitierst, ist nicht ein Thema der uniform-initialization,
>> sondern es ist Design-Defekt von std::vector<>. Dieser entsteht durch
>> Abwärtskompatibilität, was m.E. auch ein sehr hohes Gut ist.
>
> Das ist eben die Crux. Ja, Abwärtskompatibilität ist wichtig, und ja,
> viele der neuen Features haben durchaus ihre Berechtigung, aber es macht
> die Sprache auch sehr unübersichtlich.

s.o. meine "Wunschliste" ;-)

von Tom K. (xpac)


Lesenswert?

Also ich bleibe bei C, im embedded Bereich.

von temp (Gast)


Lesenswert?

Tom K. schrieb:
> Also ich bleibe bei C, im embedded Bereich.

Da spricht auch nichts dagegen. Allerdings war das hier nicht das Thema.

Ich nutze C++, allerdings nicht um jeden Preis. Gerade wenn so ein 
kleiner Controller nur 20k RAM hat dürfte die excessive Verwendung der 
STL sowieso ausgeschlossen sein. Eigentlich alles was mit dynamischen 
Speicher arbeitet. Und wenn man sich schon darauf einlässt, sollte man 
auch genau wissen wie die STL intern arbeit um die Grenze ausloten zu 
können.

Nach oben gibt es sicher Controller die über genug Resource verfügen. 
Wenn es in diese Größenordnung geht nehme ich aber lieber gleich einen 
Raspi o.ä. mit ordentlichem Betriebssystem. Da kann man dann auch im C++ 
Baukasten in die Vollen greifen.

Ups, eben sehe ich dass der ganze Thread unter PC-Programmierung 
eingeordnet ist. Da stellt sich die gesamte Frage nicht. Auch die 
Debugproblematik bzw. gezielte Optimierung ist da kein Thema. Auf einem 
kleinen Controller aber schon.

von Wilhelm M. (wimalopaan)


Lesenswert?

temp schrieb:
> Ich nutze C++, allerdings nicht um jeden Preis. Gerade wenn so ein
> kleiner Controller nur 20k RAM hat dürfte die excessive Verwendung der
> STL sowieso ausgeschlossen sein.

Gerade für kleine µC nutze nur noch C++, natürlich dann ohne stdlib. 
Dadurch werden viel mehr Fehler schon zur Compilezeit aufgedeckt bzw. 
verhindert.

von interessant (Gast)


Lesenswert?

temp schrieb:
> template<eGpioPort ePort, uint32_t bitPosi>
> class GpioPin {}

leider ist das kacke, weil dann alle Interfaces die GpioPin entgegen 
nehmen auch templates sein müssen.
1
struct Gpio {
2
  GPIO_HandleTypedef &gpio;
3
  uint16_t pin;
4
  void write(bool level) { HAL_GPIO_WritePin(&gpio, pin, level);
5
}

Das erzeugt bei GCC mit Optimierung (inkl. LTO) optimalen Code, wenn 
das Pin Objekt als const angelegt wird. Dies klappt auch, wenn das 
Objekt in andere Objekte via Konstruktor injiziert wird.

Dummerweise failed der GCC, sobald die Klasse einen Konstruktor hat. 
Deshalb mache ich bei Gpio eine Ausnahme und benutze kein übergeordnetes 
Interface.

von Wilhelm M. (wimalopaan)


Lesenswert?

interessant schrieb:
> leider ist das kacke, weil dann alle Interfaces die GpioPin entgegen
> nehmen auch templates sein müssen.

Der sog. CodeBloat, den viele befürchten, ist in der Praxis (bei realen 
Programmen und nicht nur Mini-Beispielen) viel geringen - wenn überhaupt 
vorhanden, und nicht das Gegenteil eintritt - als man denkt. Verzichtet 
man komplett auf dyn. Polymorphie und bei der Modellierung von HW auf 
die Erzeugung von Instanzen, macht stattdessen nur stat. Polymorphie und 
header-only hat man m.E. nur Vorteile.

von temp (Gast)


Lesenswert?

interessant schrieb:
> temp schrieb:
>> template<eGpioPort ePort, uint32_t bitPosi>
>> class GpioPin {}
>
> leider ist das kacke, weil dann alle Interfaces die GpioPin entgegen
> nehmen auch templates sein müssen.

Das war hier nicht die Frage, das Beispiel sollte kurz bleiben.
Es ging in dem Beispiel darum, dass es Kacke ist, dass es keinen (mir 
bekannten Weg) gibt, solchen einfachen Templates (Einzeiler) mitzuteile, 
dass sie bitte auch beim Debuggen inline in ein paar wenige Assembler 
Statements übersetzt werden. Also genau das machen was mit einem C-Macro 
schon immer ging. Leider konnte mir noch keiner das Gegenteil beweisen.

interessant schrieb:
> Das erzeugt bei GCC mit Optimierung (inkl. LTO) optimalen Code, wenn
> das Pin Objekt als const angelegt wird.

Das ist jetzt aber nicht dein Ernst? Und dann HAL_xx Aufrufe? Noch 
schlimmer gehts nicht.

Ich greife dein Beispiel aber mal auf:
1
class GpioPin2
2
{
3
public: 
4
  GPIO_TypeDef *pPort;
5
  uint8_t       pos;
6
  
7
             GpioPin2(  GPIO_TypeDef * pP, uint8_t p) { pPort=pP; pos=p; }
8
 constexpr inline void Set() const { pPort->BSRR=1<<pos; }  
9
 constexpr inline void Clr() const { pPort->BSRR=1<<(pos+16); }
10
};
11
12
#define SET(a) (a).pPort->BSRR=1<<(a).pos;   
13
#define CLR(a) (a).pPort->BSRR=1<<(a).pos;   
14
15
const GpioPin2 aLed2(GPIOA, 5);
16
17
int main(void) 
18
{
19
  while (1)
20
    {
21
    SET(aLed2);
22
    CLR(aLed2);
23
24
    aLed2.Set();
25
    aLed2.Clr();
26
    }
27
  return 0;
28
}

Auch hier gibt es keinen Weg bei ausgeschalteter Optimierung das gleiche 
Ergebnis wie mit dem Macro zu erzeugen.
Das Problem ist ja, vieles wird heute in C++ in die Header verlagert. 
Viel Funktionalität von der man davon ausgeht es ist getesteter Code. 
Die Innereien davon will man meistens nicht debuggen sondern nur seinen 
eigenen Code der das benutzt. Und genau dafür um das zu steuern gibt es 
irgendwie keine Lösung. Es ist ja genauso als wenn ich zum Debuggen 
unter Windows bzw. Linux immer mit den Debug-Versionen alles Systemlibs 
arbeiten würde. Auch auf dem PC komme ich immer ins Fluchen, da die 
Einzelschrittabarbeitung von Code im Debugger fast unmöglich ist. 
Ständig landet man da in den Untiefen der STL, die man ja eigentlich 
überhaupt nicht debuggen will. In dieser Hinsicht hat das jetzte 
Jahrzehnt jedenfalls keinen Fortschritt gebracht.

von Hans-Georg L. (h-g-l)


Lesenswert?

Wilhelm M. schrieb:
> interessant schrieb:
>> leider ist das kacke, weil dann alle Interfaces die GpioPin entgegen
>> nehmen auch templates sein müssen.
>
> Der sog. CodeBloat, den viele befürchten, ist in der Praxis (bei realen
> Programmen und nicht nur Mini-Beispielen) viel geringen - wenn überhaupt
> vorhanden, und nicht das Gegenteil eintritt - als man denkt. Verzichtet
> man komplett auf dyn. Polymorphie und bei der Modellierung von HW auf
> die Erzeugung von Instanzen, macht stattdessen nur stat. Polymorphie und
> header-only hat man m.E. nur Vorteile.

Da bin ich der gleichen Meinung und Bloat kann man auch mit C erzeugen.
z.B. die STM-HAL erzeugt nicht so knapp Bloat im RAM.

von Al Fine (Gast)


Lesenswert?

temp schrieb:
> Ständig landet man da in den Untiefen der STL, die man ja eigentlich
> überhaupt nicht debuggen will.

Also da musst du den Debugger richtig konfigurieren. Mit lldb und gdb 
geht das solala - ich habe den Eindruck da trotzdem öfter mal in einer 
zu skippenden Datei zu landen, muss aber meine Konfig nochmal checken.

https://stackoverflow.com/questions/5676241/tell-gdb-to-skip-standard-files
https://stackoverflow.com/questions/13211452/tell-lldb-to-ignore-files

von Hans-Georg L. (h-g-l)


Angehängte Dateien:

Lesenswert?

temp schrieb:

snip ...

> Auch hier gibt es keinen Weg bei ausgeschalteter Optimierung das gleiche
> Ergebnis wie mit dem Macro zu erzeugen.
> Das Problem ist ja, vieles wird heute in C++ in die Header verlagert.
> Viel Funktionalität von der man davon ausgeht es ist getesteter Code.
> Die Innereien davon will man meistens nicht debuggen sondern nur seinen
> eigenen Code der das benutzt. Und genau dafür um das zu steuern gibt es
> irgendwie keine Lösung. Es ist ja genauso als wenn ich zum Debuggen
> unter Windows bzw. Linux immer mit den Debug-Versionen alles Systemlibs
> arbeiten würde. Auch auf dem PC komme ich immer ins Fluchen, da die
> Einzelschrittabarbeitung von Code im Debugger fast unmöglich ist.
> Ständig landet man da in den Untiefen der STL, die man ja eigentlich
> überhaupt nicht debuggen will. In dieser Hinsicht hat das jetzte
> Jahrzehnt jedenfalls keinen Fortschritt gebracht.

Ich habe das gerade mit meinem geposteten Code und mit dem gcc 9.3.1 
-std=c++17 -Og gestestet. Beim debugger single step landet er direct in 
meinem "set_bit" mit der Einschränkung das ich mir nicht den parameter 
pack angucken kann. Damit kann ich aber leben. Kommt halt darauf an was 
man will. Ich will z.b. meine selbst geschriebene lib debuggen , aber 
die std libs nicht.

@Wilhelm M
requires kennt der gcc erst ab version 10.

ps. ich habe den Code nochmal angehängt damit man nicht lange suchen 
muss.

von temp (Gast)


Lesenswert?

Hans-Georg L. schrieb:
> Damit kann ich aber leben. Kommt halt darauf an was
> man will. Ich will z.b. meine selbst geschriebene lib debuggen , aber
> die std libs nicht.

Die selbst geschriebenen Libs debuggt man aber nur solange man sie 
schreibt. Irgendwann wirst du die ja auch mal benutzen.
Deinen Code oben würde ich nicht mal ansatzweise in Erwägung ziehen. 
Alles was nicht mit den Definitionen aus den Headern des Controllers 
auskommt, kommt für mich nicht in Frage. Ebensowenig wie irgendwelche 
HAL Libs wie bei STM. Aber das sind perönliche Vorlieben.

Al Fine schrieb:
> Also da musst du den Debugger richtig konfigurieren.

Danke, das kannte ich noch nicht. Ist aber trotzdem eine ganz schöne 
Orgie.
Sowas zu steuern gehört aber meines Erachtens nach in den C++ Standard 
und nicht in die Konfiguration des gdb oder wer auch immer.

von Wilhelm M. (wimalopaan)


Lesenswert?

Hans-Georg L. schrieb:
> @Wilhelm M
> requires kennt der gcc erst ab version 10.

Bis Du sicher? Auch mit -fconcepts-ts oder -fconcepts? Aber ich weiß es 
nicht mehr genau, ab welcher Version, ist schon sooo lange her.

Trotzdem gebe ich Dir den guten Rat, auf eine aktuelle Version des gcc 
zu gehen, wenn Du den Weg - der super sinnvoll ist - weiter verfolgen 
willst. Denn Concepts sind DAS feature für stat. Polymorphie.

Begreife Klassentemplates als Meta-Funktionen, die Du mit Hilfe von 
Concepts durch partielle Spezialisierung "überladen" kannst: Du kannst 
also eine Meta-Funktion (partielle Spezialisierung) anhand eines 
Concepts für einen template-typ-parameter (oder nttp) aktivieren. Das 
ist die statische Analogie zu single-dispatch bzw. zu tag-dispatching.

von Wilhelm M. (wimalopaan)


Lesenswert?

Hans-Georg L. schrieb:
> Beim debugger single step landet er direct in
> meinem "set_bit" mit der Einschränkung das ich mir nicht den parameter
> pack angucken kann.

Sollte im mangled-name stehen ...

von Hans-Georg L. (h-g-l)


Lesenswert?

temp schrieb:
> Deinen Code oben würde ich nicht mal ansatzweise in Erwägung ziehen.
> Alles was nicht mit den Definitionen aus den Headern des Controllers
jeder hat seine Vorlieben ;-)
1
#define RCC_BASE              (AHB1PERIPH_BASE + 0x3800UL)
2
// wird zu
3
constexpr reg32_t RCC_BASE              = AHB1PERIPH_BASE + 0x3800UL;
4
//---------------------
5
typedef struct
6
{
7
  ...
8
   __IO uint32_t AHB1ENR;       /*!< RCC AHB1 peripheral clock register, Address offset: 0x30 */
9
  ...
10
} RCC_TypeDef;
11
// wird zu
12
constexpr reg32_t RCC_AHB1ENR           = RCC_BASE + 0x30U;
13
//------------------------  
14
#define RCC_AHB1ENR_GPIOAEN_Pos            (0U)                                
15
#define RCC_AHB1ENR_GPIOAEN_Msk            (0x1UL << RCC_AHB1ENR_GPIOAEN_Pos)   /*!< 0x00000001 */
16
#define RCC_AHB1ENR_GPIOAEN                RCC_AHB1ENR_GPIOAEN_Msk             
17
// wird zu
18
enum class AHB1_Clock : reg32_t {    
19
   GPIOA = 1<<0,
20
}
21
22
// sollte ich vielleicht besser umbenennen, dann passen alle Namen
23
// genau zum Datenblatt
24
25
enum class RCC_AHB1ENR : reg32_t {    
26
   GPIOAEN = 1<<0,
27
}

Das mit der Namen-Differenz Header zum Datenblatt stört mich bei der HAL 
und der LL.

von Hans-Georg L. (h-g-l)


Lesenswert?

Wilhelm M. schrieb:
> Trotzdem gebe ich Dir den guten Rat, auf eine aktuelle Version des gcc
> zu gehen, wenn Du den Weg - der super sinnvoll ist - weiter verfolgen
> willst. Denn Concepts sind DAS feature für stat. Polymorphie.
>
> Begreife Klassentemplates als Meta-Funktionen, die Du mit Hilfe von
> Concepts durch partielle Spezialisierung "überladen" kannst: Du kannst
> also eine Meta-Funktion (partielle Spezialisierung) anhand eines
> Concepts für einen template-typ-parameter (oder nttp) aktivieren. Das
> ist die statische Analogie zu single-dispatch bzw. zu tag-dispatching.
Ich habe dem gcc 10.2 von der ARM-Seite auf der Platte und in Eclipse 
als User defined Toolchain in eine Liste eintragen können aber es gibt 
keine Möglichkeit das zu aktivieren. Und im Moment reicht mir das auch 
so wie es ist, die Lib wird auch nicht so bleiben wie sie ist. Was mir 
von den neusten Errungenschaften von C++ das Leben leichter macht nehme 
ich gerne.

von temp (Gast)


Lesenswert?

Hans-Georg L. schrieb:
> Das mit der Namen-Differenz Header zum Datenblatt stört mich bei der HAL
> und der LL.

Wenn es am Ende darauf hinausläuft, alle Registerdefinitionen des 
Controllers neu in eine andere Syntax zu pressen, kann ich mir kaum 
vorstellen, dass da in überschaubarer Zeit eine halbwegs fehlerfreie Lib 
rauskommt. Und wozu? Damit du dann die Fehler in deiner Lib suchst und 
nicht mehr im Anwendungsprogramm. Typischer Fall von C++ um jeden Preis.
Und dann noch zusätzlich HAL oder LL. Ich kann nur vermuten, du bist im 
akademischen Bereich tätig. Geld verdienen geht damit nicht und wenn 
Hobby, dann muss das Hobby darin bestehen LIBs zu bauen.

von Rolf M. (rmagnus)


Lesenswert?

temp schrieb:
> Hans-Georg L. schrieb:
>> Das mit der Namen-Differenz Header zum Datenblatt stört mich bei der HAL
>> und der LL.
>
> Wenn es am Ende darauf hinausläuft, alle Registerdefinitionen des
> Controllers neu in eine andere Syntax zu pressen, kann ich mir kaum
> vorstellen, dass da in überschaubarer Zeit eine halbwegs fehlerfreie Lib
> rauskommt.

Das kommt darauf an, woher die ursprünglichen Definitionen kommen. Beim 
AVR gibt's z.B. XML-Dateien, die die Register und andere Dinge 
beschreiben, aus denen die Header generiert werden. Daraus könnte man 
auch entsprechende C++-Header automatisiert generieren.

von temp (Gast)


Lesenswert?

Rolf M. schrieb:
> Das kommt darauf an, woher die ursprünglichen Definitionen kommen.

Vom Hersteller natürlich.

Rolf M. schrieb:
> Daraus könnte man
> auch entsprechende C++-Header automatisiert generieren.

Auch das macht sich nicht von allein. Ich würde an sowas micht mal einen 
Gedanken verschwenden.

von Carl D. (jcw2)


Lesenswert?

Rolf M. schrieb:
> temp schrieb:
>> Hans-Georg L. schrieb:
>>> Das mit der Namen-Differenz Header zum Datenblatt stört mich bei der HAL
>>> und der LL.
>>
>> Wenn es am Ende darauf hinausläuft, alle Registerdefinitionen des
>> Controllers neu in eine andere Syntax zu pressen, kann ich mir kaum
>> vorstellen, dass da in überschaubarer Zeit eine halbwegs fehlerfreie Lib
>> rauskommt.
>
> Das kommt darauf an, woher die ursprünglichen Definitionen kommen. Beim
> AVR gibt's z.B. XML-Dateien, die die Register und andere Dinge
> beschreiben, aus denen die Header generiert werden. Daraus könnte man
> auch entsprechende C++-Header automatisiert generieren.

Für ARM gibt's dafür sogar Doku:
https://www.keil.com/pack/doc/CMSIS/SVD/html/index.html

von Hans-Georg L. (h-g-l)


Lesenswert?

temp schrieb:
> Rolf M. schrieb:
>> Das kommt darauf an, woher die ursprünglichen Definitionen kommen.
>
> Vom Hersteller natürlich.
>
> Rolf M. schrieb:
>> Daraus könnte man
>> auch entsprechende C++-Header automatisiert generieren.
Gibt es beim den ARM MC auch nennt sich svd Files und die werden auch 
vom Debugger benutzt um SFR Register an zu zeigen.
>
> Auch das macht sich nicht von allein. Ich würde an sowas micht mal einen
> Gedanken verschwenden.

Kein Hexenwerk so was ähnliches habe ich schon öfter gemacht. Das macht 
aber erst Sinn wenn ich meine Zielstruktur gefunden habe und da bin ich 
gerade dabei.

von Hans-Georg L. (h-g-l)


Lesenswert?

temp schrieb:
> Hans-Georg L. schrieb:
>> Das mit der Namen-Differenz Header zum Datenblatt stört mich bei der HAL
>> und der LL.
>
> Wenn es am Ende darauf hinausläuft, alle Registerdefinitionen des
> Controllers neu in eine andere Syntax zu pressen, kann ich mir kaum
> vorstellen, dass da in überschaubarer Zeit eine halbwegs fehlerfreie Lib
> rauskommt. Und wozu? Damit du dann die Fehler in deiner Lib suchst und
> nicht mehr im Anwendungsprogramm. Typischer Fall von C++ um jeden Preis.
> Und dann noch zusätzlich HAL oder LL. Ich kann nur vermuten, du bist im
> akademischen Bereich tätig. Geld verdienen geht damit nicht und wenn
> Hobby, dann muss das Hobby darin bestehen LIBs zu bauen.

Hättest du nicht nur vermutet und den Thread auch gelesen ...

Aber extra für dich noch einmal. Ich habe lange Zeit meine Brötchen mit 
klassischem C++ verdient und bin seit ein paar Jährchen Rentner. Jetzt 
beschäftige ich mich eben mit meta-Programmierung weil mir das einfach 
Spaß macht.

von temp (Gast)


Lesenswert?

Hans-Georg L. schrieb:
> Jetzt
> beschäftige ich mich eben mit meta-Programmierung weil mir das einfach
> Spaß macht.

Ist doch kein Problem, wenn du deine Erfüllung darin siehst.
Damit verfolgst du halt andere Ziele als die meisten hier.

von interessant (Gast)


Lesenswert?

> [C]
> class GpioPin2
> {
> public:
>   GPIO_TypeDef *pPort;
>   uint8_t       pos;
>
>              GpioPin2(  GPIO_TypeDef * pP, uint8_t p) { pPort=pP; pos=p;
> }
>  constexpr inline void Set() const { pPort->BSRR=1<<pos; }
>  constexpr inline void Clr() const { pPort->BSRR=1<<(pos+16); }
> };
> Auch hier gibt es keinen Weg bei ausgeschalteter Optimierung das gleiche
> Ergebnis wie mit dem Macro zu erzeugen.

Ähh doch, siehe: https://godbolt.org/z/KfzexM
Man braucht lediglich always_inline Attribute + lokale O3 Optimierung zu 
verwenden, was ich in einem solchen Fall für legitim halten würde.

Der erzeugte Maschinencode ist für beide Funktionen sowohl für O0 als 
auch O3 identisch.

Aber dann ist der Maschinencode mit O0 immer noch 2.5 mal so lang ggü. 
O3, von daher verstehe ich deine Ansicht nicht so ganz...

Ich werde mal bei ST ein Ticket aufmachen, dass die HAL_GPIO_WritePin 
Implementierung in den Header verlegt werden soll.

von Rolf M. (rmagnus)


Lesenswert?

temp schrieb:
> Rolf M. schrieb:
>> Das kommt darauf an, woher die ursprünglichen Definitionen kommen.
>
> Vom Hersteller natürlich.

Entsprechend dem darauf folgenden Beispiel hätte man eigentlich darauf 
kommen können, dass gemeint war, ob da wirklich einer den ganzen Header 
von Hand geschrieben oder vielleicht auch aus etwas generiert hat, aus 
dem man genauso gut den C++-Header generieren kann.

> Rolf M. schrieb:
>> Daraus könnte man
>> auch entsprechende C++-Header automatisiert generieren.
>
> Auch das macht sich nicht von allein.

Doch, wenn man einmal den Generator geschrieben hat (was nun auch nicht 
arg schwer ist), macht sich das von allein. Dafür sind Code-Generatoren 
da. Das machen sie für den C-Code ja auch.

> Ich würde an sowas micht mal einen Gedanken verschwenden.

Ich halte es dagegen für sinnvoll und sehe nichts, was dagegen spricht.

Beitrag #6543903 wurde von einem Moderator gelöscht.
von Wilhelm M. (wimalopaan)


Lesenswert?

Hans-Georg L. schrieb:
> @Wilhelm M
> requires kennt der gcc erst ab version 10.

Ich hatte Dir die notwendigen Optionen genannt. Hast Du die eingesetzt?

Im CompilerExplorer mit gcc-9.3 und -std=c++17 -Og  -fconcepts
geht das. Sogar ab Version 7.1.

: Bearbeitet durch User
von temp (Gast)


Lesenswert?

interessant schrieb:
> Ähh doch, siehe: https://godbolt.org/z/KfzexM
> Man braucht lediglich always_inline Attribute + lokale O3 Optimierung zu
> verwenden, was ich in einem solchen Fall für legitim halten würde.
>
> Der erzeugte Maschinencode ist für beide Funktionen sowohl für O0 als
> auch O3 identisch.

Stell deinen Beispielcode als Beweis hier rein oder halt den Mund. Und 
nein, always_inline ist leider nicht die Lösung und locale O3 
Optimierung auch nicht. Solange du nicht über das Zitieren meines Codes 
hinauskommst nehme ich dich nicht für voll.

interessant schrieb:
> Der erzeugte Maschinencode ist für beide Funktionen sowohl für O0 als
> auch O3 identisch.
>
> Aber dann ist der Maschinencode mit O0 immer noch 2.5 mal so lang ggü.
> O3, von daher verstehe ich deine Ansicht nicht so ganz...

Aha, identischer Maschinencode der 2,5mal länger ist. Fällt dir da 
nichts ein?

von temp (Gast)


Lesenswert?

temp schrieb:
>> Ähh doch, siehe: https://godbolt.org/z/KfzexM

Sorry, ich rudere zurück und bitte um Entschuldigung. Den Code im 
Onlinecompiler hatte ich nicht gesehen, da bei mir der Link zerst nicht 
ging.

von temp (Gast)


Lesenswert?

temp schrieb:
> interessant schrieb:
>> Ähh doch, siehe: https://godbolt.org/z/KfzexM
>> Man braucht lediglich always_inline Attribute + lokale O3 Optimierung zu
>> verwenden, was ich in einem solchen Fall für legitim halten würde.

So, ich hab das natürlich probiert. Deine locale O3 Optimierung bring 
leider genau nichts.
Das liegt daran, dass da wo du das notiert hat, kein Code erzeugt wird. 
Macht man die locale Optimierung da wo es verwendet wird (aLed.Set() 
aLed.Clr()) oder in deinem Beispiel für die gesamte Funktion a() oder 
b() geht das. Leider ist das aber keine Option, da ich das Gegenteil 
erreichen will. a() oder b() vernünftig debuggen ohne beim Singelstep in 
die Internas der Klasse Gpio zu springen.
Es wäre wirklich eine riesen Hilfe, wenn die locale Optimierung auch bei 
Headern oder Klassendefinitionen funktionieren würde. Oder noch viel 
besser in wenn der Sprachstandard so etwas hergeben würde.

von Hans-Georg L. (h-g-l)


Lesenswert?

Wilhelm M. schrieb:
> Hans-Georg L. schrieb:
>> @Wilhelm M
>> requires kennt der gcc erst ab version 10.
>
> Ich hatte Dir die notwendigen Optionen genannt. Hast Du die eingesetzt?
>
> Im CompilerExplorer mit gcc-9.3 und -std=c++17 -Og  -fconcepts
> geht das. Sogar ab Version 7.1.

-fconcepts war der Zauberspruch ;-)

-std=c++2a wie zuerst vorgeschlagen, hat nicht funktioniert.

von lalala (Gast)


Lesenswert?

Programmierer schrieb:
> Hau wech den Scheiss schrieb:
>> Eben. Warum irgendwelche modischen Neuerungen durchdrücken, wenn es
>> weder die Manpower dafür gibt, noch irgendwelche Verbesserungen an der
>> Sicherheit aka Kundenzufriedenheit zu erwarten sind.
>
> Worauf bezieht sich das jetzt? Neue Sprachfeatures wie z.B. Lambdas
> bedeuten einfachere und schnellere Entwicklung und Wartung, und dadurch
> geringere Entwicklungskosten und billigere Endprodukte, und billig macht
> Kunden zufrieden.

Ganz ehrlich: was ist der Vorteil von Lambdas. Der Nachteil ist halt 
dass die Lambda Funktion nicht einfach als Funktion getestet werden kann 
(ohne Copy-Paste), sondern nur im Kontext der verwendeten Funktion. 
Minimaler Vorteil ist ein leicht geringerer Schreibaufwand.

von Al Fine (Gast)


Lesenswert?

lalala schrieb:
> Ganz ehrlich: was ist der Vorteil von Lambdas. Der Nachteil ist halt
> dass die Lambda Funktion nicht einfach als Funktion getestet werden kann
> (ohne Copy-Paste), sondern nur im Kontext der verwendeten Funktion.
> Minimaler Vorteil ist ein leicht geringerer Schreibaufwand.

Die Dinger können Variablen aus ihrem Kontext capturen. Wenn man nicht 
nur eine Funktion sondern auch noch eine struct mit 4 Variablen oder so 
dazu braucht relativiert sich das mit dem "minimal".

von Wilhelm M. (wimalopaan)


Lesenswert?

lalala schrieb:
> Ganz ehrlich: was ist der Vorteil von Lambdas. Der Nachteil ist halt
> dass die Lambda Funktion

Lambda-Funktionen gibt es nicht. Was Du meinst, ist eine 
Lambda-Expression, die ein closure generiert, vulgo function-object.

Gerade für µC finde ich lambda-expression in der Form als IIFE 
(immediate invoked function expression) extrem nützlich, um komplexe 
Initialisierungen für read-only Objekte (const) durchzuführen:
1
const auto x = []{
2
     // komplexe Berechnung
3
     return result;
4
}();

Ein einfaches Beispiel das o.g. einzusetzen, ist die Berechung von LUTs.

Oder
1
IrgendeinRegister::on<IrgendeinFlag>([]{
2
    // ...
3
});
(inkl. Rücksetzen, o.ä.).

Ansonsten sind die lambda-expressions natürlich überall dort extrem 
praktisch, wo man sonst mit Funktionszeigern oder allg. 
Funktionsobjekten in Form von expliziten Klassen arbeiten musste (etwa 
stdlib-Algos).

von Wilhelm M. (wimalopaan)


Lesenswert?

Hans-Georg L. schrieb:
> -fconcepts war der Zauberspruch ;-)

Stand auch schon ganz oben drin ;-) Hast wohl übersehen.

von Rolf M. (rmagnus)


Lesenswert?

lalala schrieb:
> Ganz ehrlich: was ist der Vorteil von Lambdas. Der Nachteil ist halt
> dass die Lambda Funktion nicht einfach als Funktion getestet werden kann
> (ohne Copy-Paste), sondern nur im Kontext der verwendeten Funktion.
> Minimaler Vorteil ist ein leicht geringerer Schreibaufwand.

Hauptvorteil ist die deutlich erhöhte Übersichtlichkeit des Code, weil 
ich einen Zweizeiler nicht irgendwo weit weg als separate Funktion 
definieren muss, nur weil es halt eine Funktion sein muss, sondern 
gleich dorthin schreiben, wo er verwendet wird.

von Al Fine (Gast)


Lesenswert?

Rolf M. schrieb:
> Hauptvorteil ist die deutlich erhöhte Übersichtlichkeit des Code, weil
> ich einen Zweizeiler nicht irgendwo weit weg als separate Funktion
> definieren muss, nur weil es halt eine Funktion sein muss, sondern
> gleich dorthin schreiben, wo er verwendet wird.

Das hielt sich eigentlich in Grenzen, weil man ja überall einfach eine 
Klasse/struct definieren konnte (auch lokal). Aber solche 
Funktionsobjekte selbst zu schreiben war halt doch immer Aufwand.

[code]
void fn() {
  // ...

  struct S {
   S(int someValue) { //bla }
   bool operator()(int n) { // bla }
  // blub...
  };

  algorithm(begin, end, S(4));
}

von Carl D. (jcw2)


Lesenswert?

interessant schrieb:
>> [C]
>> class GpioPin2
>> {
>> public:
>>   GPIO_TypeDef *pPort;
>>   uint8_t       pos;
>>
>>              GpioPin2(  GPIO_TypeDef * pP, uint8_t p) { pPort=pP; pos=p;
>> }
>>  constexpr inline void Set() const { pPort->BSRR=1<<pos; }
>>  constexpr inline void Clr() const { pPort->BSRR=1<<(pos+16); }
>> };
>> Auch hier gibt es keinen Weg bei ausgeschalteter Optimierung das gleiche
>> Ergebnis wie mit dem Macro zu erzeugen.
>
> Ähh doch, siehe: https://godbolt.org/z/KfzexM
> Man braucht lediglich always_inline Attribute + lokale O3 Optimierung zu
> verwenden, was ich in einem solchen Fall für legitim halten würde.
>
> Der erzeugte Maschinencode ist für beide Funktionen sowohl für O0 als
> auch O3 identisch.
>
> Aber dann ist der Maschinencode mit O0 immer noch 2.5 mal so lang ggü.
> O3, von daher verstehe ich deine Ansicht nicht so ganz...
>
> Ich werde mal bei ST ein Ticket aufmachen, dass die HAL_GPIO_WritePin
> Implementierung in den Header verlegt werden soll.

Wer aus dem Code-Schnippsel tatsächlich alles rausholen will(*)
, der könnte auf das Schieben verzichten und das für's "Löschen" 
zuständigen Register BRR benutzen. Zumindest "unoptimiert" ist das 
schneller.
Außerdem kann man da auch schön sehen, daß zu wissen, was die HW kann 
(oder welchen Algorithmus man verwendet) wichtiger ist, als den 
Maschinencode direkt zu schreiben.

(*) auf einem CortexM3 bringt das mit >O0 natürlich nicht wirklich was. 
Aber das zu verstehen setzt Kenntnisse des Thumb(2) Befehlssatzes 
voraus.

von interessant (Gast)


Lesenswert?

temp schrieb:
> So, ich hab das natürlich probiert. Deine locale O3 Optimierung bring
> leider genau nichts.Leider ist das aber keine Option, da ich das Gegenteil
> erreichen will. a() oder b() vernünftig debuggen ohne beim Singelstep in
> die Internas der Klasse Gpio zu springen.

Stimmt, die O3 bringt nichts, aber das always_inline schon.
Dein Problem verstehe ich trotzdem nicht.

Wer zum Geier single stepped seinen gesamten Code?
Wenn da eine Funktion "pinA.set()" steht, wendet man step over an, denn 
das ist trivial code den man einmal schreibt und dann nie wieder 
debuggt.

Single stepping macht man erst, wenn man verzweifelt einen Hardfault 
Fehler sucht :D

von Yalu X. (yalu) (Moderator)


Lesenswert?

lalala schrieb:
> Ganz ehrlich: was ist der Vorteil von Lambdas. Der Nachteil ist halt
> dass die Lambda Funktion nicht einfach als Funktion getestet werden kann
> (ohne Copy-Paste), sondern nur im Kontext der verwendeten Funktion.
> Minimaler Vorteil ist ein leicht geringerer Schreibaufwand.

Die Lambda-Ausdrücke kommen ursprünglich aus der funktionalen
Programmierung und finden nach und nach auch Einzug in imperative
Programmiersprachen. In Funktionalsprachen dienen sie primär
dazu,

1. zusammengehörige Dinge im Quellcode näher zusammenzurücken und damit
   die Übersichtlichkeit zu steigern

2. für Funktionen, deren Bedeutung auf einen Blick erfasst werden kann,
   den Funktionsnamen einzusparen

3. als angenehmen Nebeneffekt den Schreibaufwand zu verringern

Diese Dinge sind aber nur bei sehr einfachen Funktionen relevant.
Funktionen, die mehr als einen Einfachen Ausdruck enthalten, werden
praktisch immer ganz normal mit Namen definiert.

Da C++ keine lokalen Funktionsdefinitionen (also Funktionsdefinitionen
innerhalb einer anderen Funktion) in der klassischen Notation
erlaubt¹, kommt hier noch ein weiterer wesentlicher Aspekt hinzu:

4. Funktionen lokal zu definieren

Dabei geht es nicht nur um die Sichtbarkeitsbeschränkung dieser
Funktionen, sondern vor allem darum, in der lokalen Funktion auf lokale
Variablen der umgebenden Funktion zugreifen zu können. Dazu muss die
lokale Funktion nicht notwendigerweise anonym sein. Möchte man die
Funktion benennen, weist man den Lambda-Ausdruck einfach einer Variable
mit passendem Namen zu:

1
void outerFunc(int x) {
2
  const auto localFunc = [x](int y} {
3
    ...
4
    // do something with x and y
5
    ...
6
  }
7
  ...
8
  localFunc(42);          // direct call
9
  ...
10
  otherFunc(localFunc);   // use as callback function
11
  ...
12
}

Aus dem (in C++ local nicht erlaubten) klassischen Funktionskopf

1
  void localFunc(int y) {

wird also

1
  const auto localFunc = [x](int y} {

was ja gar nicht einmal so viel anders aussieht.

Würde man localFunc global definieren, hätte sie keinen Zugriff auf x.
Man müsste deswegen x als zweites Argument (zusätzlich zu y) übergeben,
was aber insbesondere bei der Verwendung als Callback-Funktion nicht
immer möglich ist, weil sich dadurch die Funktionssignatur ändert.

Alternativ kann man die Funktion in ein callable Object packen:

Al Fine schrieb:
> void fn() {
>   // ...
>
>   struct S {
>    S(int someValue) { //bla }
>    bool operator()(int n) { // bla }
>   // blub...
>   };
>
>   algorithm(begin, end, S(4));
> }

So (oder so ähnlich) werden die Lambda-Ausdrücke intern repräsentiert.
Die [...](...)-Schreibweise ist willkommener syntaktischer Zucker dafür.


Witzigerweise sind wegen des o.g. 4. Punkts Lambda-Ausdrücke in C++ weit
weniger verzichtbar als in den funktionalen Sprachen, in denen sie
ursprünglich implementiert wurden, zumal es dort für viele typische
Anwendungen von Lambda-Ausdrücken Ersatzschreibweisen gibt, die noch
einmal kürzer und prägnanter sind.

──────────────
¹) GCC unterstützt lokale Funktionsdefinitionen als Erweiterung in C,
   aber nicht in C++.

von Wilhelm M. (wimalopaan)


Lesenswert?

Yalu X. schrieb:
> So (oder so ähnlich) werden die Lambda-Ausdrücke intern repräsentiert.

Nicht der Lambda-Ausdruck wird so repräsentiert, sondern das Closure, 
also das Funktionsobjekt. Also das Ergebnis des Lambda-Ausdrucks.

von Yalu X. (yalu) (Moderator)


Lesenswert?

Ja, natürlich das Ergebnis. 'tschuldigung für die unpräzise
Ausdrucksweise :)

von temp (Gast)


Lesenswert?

Carl D. schrieb:
> Wer aus dem Code-Schnippsel tatsächlich alles rausholen will(*)
> , der könnte auf das Schieben verzichten und das für's "Löschen"
> zuständigen Register BRR benutzen. Zumindest "unoptimiert" ist das
> schneller.

Beweis das mal.
Das ändert rein garnichts. Das Schieben mit Konstanten erfolgt doch 
nicht zur Laufzeit. Das Berechnet der Compiler zur Laufzeit. Und am Ende 
bleibt eine Zuweisung eines Registers mit einer Konstannten. Aber diese 
Diskussion geht am Ziel vobei. Ich suche nicht nach einer Lösung für 
genau das Problem, sondern kritisieren den aktuellen Zustand von C++ in 
dieser Hinsicht.

interessant schrieb:
> Wer zum Geier single stepped seinen gesamten Code?
> Wenn da eine Funktion "pinA.set()" steht, wendet man step over an, denn
> das ist trivial code den man einmal schreibt und dann nie wieder
> debuggt.
>
> Single stepping macht man erst, wenn man verzweifelt einen Hardfault
> Fehler sucht :D
Wer bestimmt wann man was macht? Du nicht!
Es geht hier ums Prinzip und nicht um dieses konkrete Beispiel. Aber mal 
ein Beispiel.
1
void testfunc(const char *p, size_t len);
2
3
...
4
...
5
const char *p="Mülleimer";
6
testfunc(p, strlen(p));
7
...
8
std::string s("Mülleimer");
9
testfunc(s.c_str(), s.length());
10
...

wenn ich im Singelstep im ersten Aufruf in testfunc() reinspringe lande 
ich auch sofort darin. strlen() ist eine Funktion aus der optimierten 
Lib und ich lande niemals in der Sourcen von strlen().
Anders die Variante mit dem std::string. Hier lande ich erst mal in den 
Headern davon und muss mich da durch hangeln bis ich irgendwann mal in 
der testfunc() lande. Sofort kommen jetzt wieder Schlauschweine die 
messerscharf schließen, man kann doch einen Breakpoint in testfunc() 
setzen. Alles richtig und wird sicher auch gemacht. Schön ist aber was 
anderes. Hinzukommt, eine strlen() bleibt optimiert ob ich das Programm 
debugge oder nicht. Wie std::string in Maschinencode übersetzt wird ist 
aber vom Optimierungslevel abhängig. Je mehr Metaprogrammiererei in den 
C++ Standard-Libs verwendet wird, desto mehr unterscheiden sich die 
Debug- und Releasevarianten eines Programms. Für mich ist und bleibt es 
ein Rückschritt.

von Wilhelm M. (wimalopaan)


Lesenswert?

temp schrieb:
> Für mich ist und bleibt es
> ein Rückschritt.

Du debug-st einfach zuviel ;-) Verwende mehr compiletime-Konstrukte, 
Strong-Types, etc, u.s.w. und Deine "Debugging-Experience" wird sich 
massiv verändern.

von Carl D. (jcw2)


Lesenswert?

temp schrieb:
> Carl D. schrieb:
>> Wer aus dem Code-Schnippsel tatsächlich alles rausholen will(*)
>> , der könnte auf das Schieben verzichten und das für's "Löschen"
>> zuständigen Register BRR benutzen. Zumindest "unoptimiert" ist das
>> schneller.
>
> Beweis das mal.
> Das ändert rein garnichts. Das Schieben mit Konstanten erfolgt doch
> nicht zur Laufzeit. Das Berechnet der Compiler zur Laufzeit.

Wo steht da, daß diese Klasse nur "constexpr" instanziiert werden kann?
Das "constexpr" vor dem Konstruktor sagt nur, daß diese Klasse in 
compiletime Code instanziiert werden kann. BTW, das "constexpr" zu 
erzwingen heißt (ab C++20) "consteval".

Daß man auf einem M3/4/... das Shiften umsonst bekommt, das ändert nicht 
an der Hauptaussage, daß man erst wissen sollte, was man tut und erst 
dann die Frage kommt "mit welchem Werkzeug".

Aber was soll's. Jeder wie er will.

von Hans-Georg L. (h-g-l)


Lesenswert?

Es verbietet niemand, da wo man hin will einen breakpoint zu setzen ;-)

von temp (Gast)


Lesenswert?

Wilhelm M. schrieb:
> Du debug-st einfach zuviel ;-) Verwende mehr compiletime-Konstrukte,
> Strong-Types, etc, u.s.w. und Deine "Debugging-Experience" wird sich
> massiv verändern.

Wenn das alles ist was an Argumenten bleibt hat sich jede weitere 
Diskussion erübrigt.

von Wilhelm M. (wimalopaan)


Lesenswert?

temp schrieb:
> Wilhelm M. schrieb:
>> Du debug-st einfach zuviel ;-) Verwende mehr compiletime-Konstrukte,
>> Strong-Types, etc, u.s.w. und Deine "Debugging-Experience" wird sich
>> massiv verändern.
>
> Wenn das alles ist was an Argumenten bleibt hat sich jede weitere
> Diskussion erübrigt.

Das sind äußerst gewichtige Argumente: Code, der nicht compiliert, den 
braucht man nicht zu debuggen ;-)

von temp (Gast)


Lesenswert?

Carl D. schrieb:
> Wo steht da, daß diese Klasse nur "constexpr" instanziiert werden kann?
> Das "constexpr" vor dem Konstruktor sagt nur, daß diese Klasse in
> compiletime Code instanziiert werden kann. BTW, das "constexpr" zu
> erzwingen heißt (ab C++20) "consteval".
>
> Daß man auf einem M3/4/... das Shiften umsonst bekommt, das ändert nicht
> an der Hauptaussage, daß man erst wissen sollte, was man tut und erst
> dann die Frage kommt "mit welchem Werkzeug".
>
> Aber was soll's. Jeder wie er will.

Hast du es immer noch nicht kapiert? Ich habe das als simples 
nachvollziehbares Beispiel benutzt. Selbst wenn ich das als reales 
Problem hätte würde ich mit Sicherheit nicht hier nach einer Lösung 
fragen.

Hans-Georg L. schrieb:
> Es verbietet niemand, da wo man hin will einen breakpoint zu setzen ;-)

Herr lass Hirn regnen.

So langsam glaube ich, ich bin hier bei selbst ernannten C++ Gurus 
gelandet die von sich selbst glauben die Weisheit mit Löffeln gefressen 
zu haben. Mann kann das ja auch schön als Schwanzverlängerung verwenden 
wenn man sonst irgendwelche Minderwertigkeitskomplexe hat. Und wehe es 
kommt einer daher der sich wagt Kritik an dem heiligen Werkzeug zu 
äussern...
Ich bin jetzt raus hier.

von Carl D. (jcw2)


Lesenswert?

temp schrieb:
> Wilhelm M. schrieb:
>> Du debug-st einfach zuviel ;-) Verwende mehr compiletime-Konstrukte,
>> Strong-Types, etc, u.s.w. und Deine "Debugging-Experience" wird sich
>> massiv verändern.
>
> Wenn das alles ist was an Argumenten bleibt hat sich jede weitere
> Diskussion erübrigt.

Er macht nur, was vorher mit seinen zig anderen Argumenten dafür von der 
Gegenseite gemacht wurde.
Ja, der Gegenstand der Diskussion hat Nachteile zu anderen, aber eben 
auch viele Vorteile, die diese Nachteile überwiegen könnte, um es 
wertfrei zu schreiben.
Und weniger debuggen durch besser Typprüfung ist ein solcher "andere 
Nachteile überwiegender Vorteil".

von Wilhelm M. (wimalopaan)


Lesenswert?

temp schrieb:
> Und wehe es
> kommt einer daher der sich wagt Kritik an dem heiligen Werkzeug zu
> äussern...
> Ich bin jetzt raus hier.

Ja, dann, dann, dann ... dann entziehst Du Dich der Diskussion. Viel 
Spaß beim debuggen ;-)

von Jedzia D. (Firma: Rast und Ruh) (jedzia)


Lesenswert?

temp schrieb:
...
> Beweis das mal.
> Das ändert rein garnichts. Das Schieben mit Konstanten erfolgt doch
> nicht zur Laufzeit. Das Berechnet der Compiler zur Laufzeit. Und am Ende
Eher: Das Berechnet der Compiler zur Übersetzungszeit.

Ihr macht ja schon Sublimationsfehler. Das ist dann, wenn das Gehirn als 
Dampf durch die Ohren austritt.


Hitzige Diskussion, was? Dabei sind doch neue Features was sehr schönes. 
Besonders wenn niemand einen zwingt sie zu benutzen:P

von A. S. (Gast)


Lesenswert?

Jedzia D. schrieb:
> temp schrieb:
> ...
>> Das Berechnet der Compiler zur Laufzeit.
> Eher: Das Berechnet der Compiler zur Übersetzungszeit.

Was das gleiche ist: Du hast den Satz vielleicht nicht aufmerksam 
gelesen.

Und wenn män es klarer schreiben will: Das berechnet der Compiler. 
(Punkt)

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

temp schrieb:
> Und nein, always_inline ist leider nicht die Lösung und locale O3
> Optimierung auch nicht.

Optimierung und always_inline haben Einfluß auf Optimierung, aber nicht 
auf die Erzeugung von Debug-Info.

Was in die Richtung geht wäre Attribut "artificial", vielleicht hilft 
das ja?

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.