mikrocontroller.net

Forum: Projekte & Code Cool Kids and OOP


Announcement: there is an English version of this forum on EmbDev.net. Posts you create there will be displayed on Mikrocontroller.net and EmbDev.net.
von Wilhelm M. (wimalopaan)


Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Frei nach dem Motto "Cool Kids don't like OOP" habe ich mal ein simples 
Code-Beispiel mit klassischer Laufzeit-Polymorphie durch einen 
std::variant<>-Ansatz ersetzt (Damit es nicht so ganz abstrakt bleibt, 
hat das Beispiel eine fiktive MCU).

Ausgangscode ist eine Iteration über fiktive HW-Abstraktionen klassisch 
mit virtuellen Funktionen (bm91.cc):
int main() {
    std::array<I*, 4> p{&x1, &x2, &x3, nullptr};
    p[0] = &x1;
    while(true) {
        std::byte x{};
        for(auto&& i : p) {
            if (i) {
                i->store();
                x |= i->get();
            }
        }
        x1.set(x);
    }
}

Zweiter Versuch mit std::variant<> und Zeigern (bm90.cc):
int main() {
    std::array<std::variant<B*, A*, nullptr_t>, 4> p{&x1, &x2, &x3, nullptr};
    p[0] = &x4;
    while(true) {
        std::byte x{};
        for(auto&& i : p) {
            i.visit([&]<typename P>(P& v){
                        if constexpr(!std::is_same_v<P, nullptr_t>) {
                            v->store();
                            x |= v->get();
                        }
            });
        }
       x1.set(x);
    }
}

Dritter Versuch mit std::variant<> ohne Zeiger (bm92.cc):
int main() {
    std::array<std::variant<B, A, nullptr_t>, 4> p{x1, x2, x3, nullptr};
    p[0] = x4;
    while(true) {
        std::byte x{};
        for(auto&& i : p) {
            i.visit([&]<typename P>(P& v){
                        if constexpr(!std::is_same_v<P, nullptr_t>) {
                            v.store();
                            x |= v.get();
                        }
            });
        }
        x1.set(x);
    }
}

Für einen attiny1614 bekommt man:
212       4       4     220      dc bm90.elf
400      10       4     414     19e bm91.elf
190       0       4     194      c2 bm92.elf

Der generierte Code könnte besser nicht sein (bm92.s):
main:
.L2:
lds r24,MCU::P2::r2      ;  r2.1_47, r2
lds r18,MCU::P2::r2      ;  r2.1_64, r2
lds r25,MCU::P1::r2      ;  r2.2_76, r2
or r24,r18               ;  tmp48, r2.1_64
or r24,r25               ;  _79, r2.2_76
sts MCU::P1::r1,r24      ;  r1, _79
rjmp .L2                 ;

Der Code mit virtuellen Funktionen (bm91.s) ist wesentlich länger und 
umständlicher.

Interessant finde ich, dass der Compiler die virtuellen Funktionsaufrufe 
nicht komplett devirtualisieren kann.

Hier auch der Link in den Compiler-Explorer:

https://gcc.godbolt.org/z/5C5mMh

(etwas angepasst, damit es komplett Standard-konform ist).

Die AVR-Variante verwendet eine eigene, straight-forward realisiertes 
Klassentemplate std::variant<> als diskriminierte,rekursive Union (daher 
kein UB hier). Die Realisierung davon hänge ich auch mal an, damit die 
Idee von std::variant<> klar wird. Wobei die nicht out-of-the-box zu 
verwenden ist, da die Meta-Funktionsbibliothek fehlt, was sich aber 
recht leicht anpassen lässt).

Ist nicht nur Spielerei, denn ich habe damit schon mal ein Menu-System 
(ja tatsächlich: AVR) erfolgreich umgebaut (std::variant<> mit Zeigern): 
kleiner, schneller.

von Vincent H. (vinci)


Bewertung
1 lesenswert
nicht lesenswert
optional & variant ♥

Mittlerweile frag ich mich wie ich eigentlich jemals ohne einem der 
beiden hab Leben können ;)

Das einzige was mich bis heute fuchst is der fehlende Referenz-Support.

von Wilhelm M. (wimalopaan)


Bewertung
0 lesenswert
nicht lesenswert
Vincent H. schrieb:
> optional & variant ♥
>
> Mittlerweile frag ich mich wie ich eigentlich jemals ohne einem der
> beiden hab Leben können ;)

Also std::optional<> habe ich schon gaaaanz lange im Einsatz, also 
simple, eigene Variante mit C++11.

std::variant<> eher weniger. Bislang habe ich es auf µC durch einen 
komplett statischen Ansatz ersetzt, bei non-µC für diesen Einsatz eher 
selten.

von Johannes S. (jojos)


Bewertung
0 lesenswert
nicht lesenswert
ich versuche es zu verstehen, habe variants noch nicht benutzt.
In bm91 mit einem Array kann ich aber das Array zur Laufzeit 
bauen/ändern, bei Variants nicht, richtig?
Das Menu System wäre demnach auch statisch, was aber sicher in den 
meisten Fällen auch so sein sollte.

von Wilhelm M. (wimalopaan)


Bewertung
0 lesenswert
nicht lesenswert
Johannes S. schrieb:
> In bm91 mit einem Array kann ich aber das Array zur Laufzeit
> bauen/ändern, bei Variants nicht, richtig?

Vielleicht übersehen
    p[0] = x4;

Oder meinst Du was anderes?

von Vincent H. (vinci)


Bewertung
0 lesenswert
nicht lesenswert
Johannes S. schrieb:
> In bm91 mit einem Array kann ich aber das Array zur Laufzeit
> bauen/ändern, bei Variants nicht, richtig?

Ein variant ist nur ein fancy union. Wie bei einem union auch kannst du 
den aktiven Typen ändern.
std::variant<double, char> v{42.0};  // variant enthält double
v = 'x';  // variant enthält char

von Theor (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Bezieht sich auf bm91.cc

Compiliert mit https://www.onlinegdb.com/online_c++_compiler
und C++17 eingestellt.

Öhm. Ist das C++20?

Fehlt da nicht ein #include <cstddef>?

Und:
Virtual und constexpr gleichzeitig?
inline virtual constexpr std::byte get() const = 0;

von Wilhelm M. (wimalopaan)


Bewertung
1 lesenswert
nicht lesenswert
Theor schrieb:
> Bezieht sich auf bm91.cc
>
> Compiliert mit https://www.onlinegdb.com/online_c++_compiler
> und C++17 eingestellt.

Ich hatte extra den Link zu godbolt gepostet: da sind die Änderungen 
drin, wie oben schon geschrieben)

> Öhm. Ist das C++20?

Ja, mindestens generic lambdas mit explizitem Typparameter

>
> Fehlt da nicht ein #include <cstddef>?

s.o.

> Und:
> Virtual und constexpr gleichzeitig?

Ja, im Beispiel aber nicht wichtig.

von Johannes S. (jojos)


Bewertung
0 lesenswert
nicht lesenswert
Wilhelm M. schrieb:
> Oder meinst Du was anderes?

nein, danke. Übersehen das es ja trotzdem ein Array ist, ein Array von 
Variants. Und der Variant darf die Typen A*, B* und nullptr annehmen, 
und das darf wie Vincent schrieb auch zur Laufzeit verändert werden. 
Solange es ein Typ ist der im Variant definiert ist, jetzt richtig?
Also eher ein enum Ersatz? Mit welchen Vorteilen?

von Wilhelm M. (wimalopaan)


Bewertung
0 lesenswert
nicht lesenswert
Johannes S. schrieb:
> und das darf wie Vincent schrieb auch zur Laufzeit verändert werden.
> Solange es ein Typ ist der im Variant definiert ist, jetzt richtig?

Ja.

Johannes S. schrieb:
> Also eher ein enum Ersatz? Mit welchen Vorteilen?

Polymorphe ohne vtable.

von Johannes S. (jojos)


Bewertung
0 lesenswert
nicht lesenswert
ok.
Auf dem µC mit STM Target mal eine Zeile mit std::variant kompiliert, da 
explodiert aber das logfile wenn ich mit c++17/c++2a kompiliere, die HAL 
ist voll von register storage classes die nun obsolet werden sollen. Das 
ist schon ein krasser Kompatibilitätsbruch.

: Bearbeitet durch User
von Wilhelm M. (wimalopaan)


Bewertung
0 lesenswert
nicht lesenswert
Johannes S. schrieb:
> ok.
> Auf dem µC mit STM Target mal eine Zeile mit std::variant kompiliert, da
> explodiert aber das logfile wenn ich mit c++17/c++2a kompiliere, die HAL
> ist voll von register storage classes die nun obsolet werden sollen. Das
> ist schon ein krasser Kompatibilitätsbruch.

Ich denke nicht: Du solltest die HAL-Header korrekt als extern "C" 
inkludieren.

von Johannes S. (jojos)


Bewertung
0 lesenswert
nicht lesenswert
Wilhelm M. schrieb:
> Ich denke nicht: Du solltest die HAL-Header korrekt als extern "C"
> inkludieren.

sind sie. Aber der gcc ignoriert diese register Empfehlung wohl schon 
lange und ST sollte das rauswerfen. Codesize beim Kompilieren ist auch 
gleich.
Die CMSIS wurde da auch schon aktualisiert.

von Wilhelm M. (wimalopaan)


Bewertung
2 lesenswert
nicht lesenswert
Johannes S. schrieb:
> Wilhelm M. schrieb:
>> Ich denke nicht: Du solltest die HAL-Header korrekt als extern "C"
>> inkludieren.
>
> sind sie. Aber der gcc ignoriert diese register Empfehlung wohl schon
> lange und ST sollte das rauswerfen. Codesize beim Kompilieren ist auch
> gleich.

Ja, mindestens 15 Jahre ;-)

Hier hast Du zwei Möglichkeiten.
#ifdef __cplusplus
# define register 
#endif
//#pragma GCC diagnostic push
//#pragma GCC diagnostic ignored "-Wregister"
extern "C" {
register int foo(register int);
}
//#pragma GCC diagnostic pop

> Die CMSIS wurde da auch schon aktualisiert.

Jaja, die HAL ...

von Johannes S. (jojos)


Bewertung
0 lesenswert
nicht lesenswert
ein einfaches '-Wno-register' beim gcc oder '-Wno-deprecated-register' 
beim ARMC6 stellt den Compiler auch ruhig.
Den Quellcode selber soll ST aktualisieren, das wurde da auch schon 
gemeldet.
Es zeigt nur das die Anwender selbst bei Neuerungen im 3-Jahres Rhytmus 
nicht sofort mitziehen können. Ich finde die Features aber schon 
spannend, bringt ruhig weiter solche Beispiele.

: Bearbeitet durch User
von Wilhelm M. (wimalopaan)


Bewertung
0 lesenswert
nicht lesenswert
Johannes S. schrieb:
> Es zeigt nur das die Anwender selbst bei Neuerungen im 3-Jahres Rhytmus
> nicht sofort mitziehen können.

Deprecated wurde es 2009 für C++11.

In C++17 ist die Bedeutung des keyword nun ganz verschwunden, aber noch 
reserviert.

Die Hersteller hatten 8-Jahre Zeit. Ich denke, das reicht locker (c++11, 
c++14, c++17).

: Bearbeitet durch User
von Johannes S. (jojos)


Bewertung
0 lesenswert
nicht lesenswert
c++14 hatte aber noch nicht gemeckert, wenn kein Wecker klingelt schläft 
man weiter :) Es wird auch immer noch als Warning, nicht als Error 
gemeldet.
Wenn viele Tests durchlaufen werden müssen und die neuen Features nicht 
benutzt werden, dann ziehen die Hersteller nicht mit.

Antwort schreiben

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

Wichtige Regeln - erst lesen, dann posten!

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

Formatierung (mehr Informationen...)

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




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.

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