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):
1
intmain(){
2
std::array<I*,4>p{&x1,&x2,&x3,nullptr};
3
p[0]=&x1;
4
while(true){
5
std::bytex{};
6
for(auto&&i:p){
7
if(i){
8
i->store();
9
x|=i->get();
10
}
11
}
12
x1.set(x);
13
}
14
}
Zweiter Versuch mit std::variant<> und Zeigern (bm90.cc):
Der generierte Code könnte besser nicht sein (bm92.s):
1
main:
2
.L2:
3
ldsr24,MCU::P2::r2;r2.1_47,r2
4
ldsr18,MCU::P2::r2;r2.1_64,r2
5
ldsr25,MCU::P1::r2;r2.2_76,r2
6
orr24,r18;tmp48,r2.1_64
7
orr24,r25;_79,r2.2_76
8
stsMCU::P1::r1,r24;r1,_79
9
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.
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.
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.
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.
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.
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?
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.
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?
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.
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.
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.
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.
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.
1
#ifdef __cplusplus
2
# define register
3
#endif
4
//#pragma GCC diagnostic push
5
//#pragma GCC diagnostic ignored "-Wregister"
6
extern"C"{
7
registerintfoo(registerint);
8
}
9
//#pragma GCC diagnostic pop
> Die CMSIS wurde da auch schon aktualisiert.
Jaja, die HAL ...
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.
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).
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.