Da ich bei meinen Spielereien mit diversen MCUs (STM32, ESP32) bezüglich
der Programmierung immer wieder schnell an meine Grenzen stoße, da ich
kaum über C++ Erfahrung verfüge und diese Sprache ganz allgemein nicht
so prickelnd finde (um nicht zu sagen, dass ich mittlerweile ein
erklärter C++-Hasser bin), bin ich auf der Suche nach einem guten Buch,
das sich vor allem C++ widmet, idealerweise auch auf die Einschränkungen
in Mikrocontrollerumgebungen eingeht.
Hat jemand von euch hier einen Tipp ? Mir geht es wie gesagt in erster
Linie um C++ - eine Einführung in Mikrocontroller ist nicht so
notwendig.
Der Autor dieses Buches macht hier gerne dafür Werbung, damit er es
nicht immer selber machen muss übernehme ich es diesmal:
http://www.ruediger-asche.de/
(obwohl weniger C++)
Dann gibt es hier einiges:
https://www.grimm-jaud.de/
und zu modernen C++ Features:
https://github.com/AnthonyCalandra/modern-cpp-features
Zu OO selber finde ich auch das alte Standardwerk von Stroustrup gut, er
selber hat auch eine große Linkliste:
https://www.stroustrup.com/C++.html
Und praktischerweise kann man auch gleich ein C++ orientiertes OS
benutzen, namentlich Mbed-OS. Da sind reichlich Tricks und Konzepte drin
die man auch erstmal verstehen muss, auf jedenfall hat man eine gute
Basis für Komponenten.
C++ ist aber nur sehr eingeschränkt auf µCs zu nutzen. Auf alle Fälle
ist es besser als das veralterte C.
Die interessanten Dinge wie polymorphe Objekte mit virtuellen Methoden
sind aufgrund mangelnder Resourcen nicht nutzbar. :-<
MaWin schrieb:> Die interessanten Dinge wie polymorphe Objekte mit virtuellen Methoden> sind aufgrund mangelnder Resourcen nicht nutzbar. :-<
Das stimmt so natürlich nicht.
mh schrieb:> Das stimmt so natürlich nicht.
Aber sicher doch. Gibts schon was besseres als den 8051?
Für einige hier ist die Erde wohl immer noch eine Scheibe.
Johannes S. schrieb:> mh schrieb:>> Das stimmt so natürlich nicht.>> Aber sicher doch. Gibts schon was besseres als den 8051?> Für einige hier ist die Erde wohl immer noch eine Scheibe.
Auch auf einen 8051 sollte das gehen, wenn es einen modernen Compiler
dafür gibt.
MaWin schrieb:> mh schrieb:>> Das stimmt so natürlich nicht.>> An welche STM32 denkst du, bei denen das problemlos geht?
Das problemlos kommt von dir. Probleme gibt es immer. Eben hast du noch
behauptet, dass sie nicht nutzbar sind und das ist einfach falsch.
MaWin schrieb:> Die interessanten Dinge wie polymorphe Objekte mit virtuellen Methoden> sind aufgrund mangelnder Resourcen nicht nutzbar. :-<
MaWin schrieb:> Die interessanten Dinge wie polymorphe Objekte mit virtuellen Methoden> sind aufgrund mangelnder Resourcen nicht nutzbar.
Das stimmt pauschal so nicht!
Ich würde es mit C++ auf Mikrocontrollern versuchen. Durch die
Kompatibilität mit C kann man ja auch beliebig mischen. Lernt man etwas
neues dazu, kann man auch nach und nach von C auf C++ refactorn.
Auf einfachen Mikrocontrollern sind Teile der Standardbibliothek nicht
nutzbar, weil diese einen Heap oder eine Betriebssystem erfordern. Alles
andere ist aber kein Problem. Es könnte sogar sein, dass der Compiler
besser optimieren kann oder die Sytax weniger Fehler erlaubt.
Und es stehen viele Algorithmen bereits fertig und optimiert zur
Verfügung.
z.B.
1
// C
2
voidfoo(int*data)// Es wird erwatet, dass data 4 Element hat, der Aufrufer sieht das aber nicht.
3
{
4
for(int*p=data;p<data+4;++p);
5
}
6
// C++
7
// Größe wird vom Compiler erzwungen, data kann kein null pointer sein.
8
voidfoo(std::array<int,4>&data)
9
{
10
// Mit range based for ist sofort ersichtlich, dass man über alles iteriert
11
for(inti:data);
12
}
Und speziell noch zu den virtuellen Methoden. Beispiel ist ein Interface
für einen Treiber mit zwei Implementierung, für verschiedene Geräte.
1
classIDriver
2
{
3
public:
4
virtualvoidstart()=0;
5
};
6
7
classDriver_DevA:publicIDriver
8
{
9
public:
10
voidstart()override;
11
};
12
13
classDriver_DevB:publicIDriver
14
{
15
public:
16
voidstart()override;
17
};
18
19
voidbootSystem(IDriver&);
20
21
intmain()
22
{
23
#ifdef DEV_A
24
// Im Code gibt es eine Sprungtabelle (v_table) für Driver_DevA, egal wie viele Instanzen davon angelegt werden.
25
// Driver_DevA setzt hier den Pointer auf seine Sprungtabelle.
26
Driver_DevAdriver;
27
#else
28
Driver_DevBdriver;
29
#endif
30
// Hier greift Polymorphismus, es wird aber weder ein Heap noch RTTI (Run Time Type Infomation) benötigt.
31
bootSystem(driver);
32
}
In C müsste man dies mit Funktionspointern lösen und kann dabei mehr
Fehler machen, weil man es selbst machen muss. Wird start in einer
Ableitung nicht implementiert, sagt einem der Compiler dass direkt. Von
den Resourcen wäre es das gleiche wie in C per Hand, wenn das Interface
mehr als eine Methode hat.
Mit neuen C++ Standards ist es auch möglich Funktionen zu schreiben, die
garantiert zur Compilezeit ausgewertet werden (constexpr, consteval),
ohne dafür den Präprozessor zu misbrauchen. Damit lasse sich z.B.
Parameter mit floating point Code berechnen, ohne dass dieser Code
später auf dem Controller zur Laufzeit ausgeführt wird.
Hallo Otto,
für mein Beispiel brauchst du keinen größeren Controller. Es sollte fast
auf das gleiche wie bei C rauskommen, wenn der C Code korrekt war. In C
kann man nämlich leicht die Freigabe von Resourcen beim return
vergessen, dann ist der C Code natürlich kleiner, aber auch falsch.
Ich gebe dir recht, dass C++ komplexer ist, wenn man C gewöhnt ist und
sehr hardwarenah denkt. (Bei deinem Kommentar würde ich dich genau hier
einordnen.)
Deshalb fragt der TO ja auch nach Büchern um das zu lernen oder sein
Wissen zu erweitern.
Mit C++11 und neueren Standards kann man aussagekräftigen Code schreiben
und nicht alles, was in C++ Text ist, produziert auch später Code.
Aber jetzt doch nochmal war hilfreiches für den TO.
Ich würde an deiner Stelle erstmal C++ lernen und die Denkweise
verinnerlichen. Aus eigener Erfahrung kann das auch etwas dauern.
C++ bringt dir erst dann einen Vorteil, wenn du in Objekten denkst und
diese nutzt.
z.B.
* std::array<int, 10> a; std::find(a.begin(), a.end(), 42); statt eine
for Schleife, die mit Pointern und Größen manuell arbeitet
* Freigabe von Locks am Ende des Funktion, egal wie man aus der Funktion
rausspringt. (Scoped Lock)
Für Embedded kenne ich keine Bücher, weil ich das nur als Schulung
hatte.
Bei der Anwendung hängt es von deinem System ab:
* OS mit Heap => Es geht wahrscheinlich alles. Interessant ist, wie
teuer es dann wirklich ist.
* Bare Metal => Die Standardbibliothek ist nur eingeschränkt nutzbar.
Sonst kannst du aber alles nutzen, was keinen dynamischen Speicher
braucht.
Als Resourcen beim Üben kann ich noch folgendes empfehlen:
* Compiler Explorer: Hier kann man schnell mal ein C++ Konstrukt
eingeben und sich im Assembler anschauen, wie es umgesetzt wird und was
eine Optimierung ändert. https://godbolt.org/
* Cpp Core Guidelines: Das sind best Practices für C++ Code. Immer wenn
man denkt, dass muss doch besser gehen kann man hier mal reinschauen.
https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines
* Die Referenz, wenn du dir anschauen willst, was die Standardbibliothek
bietet. https://en.cppreference.com/w/
Und wenn du deinen Code auch auf einem Desktop bauen kannst, dann ist
clang-tidy sehr hilfreich. Das ist eine Codeanalyse, die dir aber in
vielen Fällen auch gleich sagt, wie man es besser machen könnte.
https://clang.llvm.org/extra/clang-tidy/
Und dann happy coding :-)
Daniel schrieb im Beitrag #6684786:
> C++ und Mikrocontroller.> Warum denn einfach wenns auch kompliziert geht?
Wenn dein Hirn zu klein ist um C++ zu verstehen ist das dein Problem,
deshalb musst du nicht einen Thread zur Frage nach C++ Literatur voll
müllen.
Mach einen eigenen Thread auf oder lies erstmal die zigtausend
existierenden Beiträge zu diesem Thema.
Dieses Unterforum sollte auch für anonyme Störer gesperrt werden, das
war bisher ein seriöser Teil des Forums.
Danke, Embedded C++ kannte ich auch noch nicht.
Mich wunder aber, dass sie folgender Konstrukte auch weglassen wollten.
Namensräume:
Das ist ja nur eine Bennenung, die nach der Compilierung sowieso keine
Rolle mehr spielt. Vorteilhaft gegenüber den Funktionspräfixen bei C
ist, dass man innerhalb des Namensraum (namespace) diese nicht angeben
muss.
Templates:
Es müssen ja nicht gleich die komplizierten sein. Aber wenn ich einen
Algorithmus für zwei Datentypen haben möchte, dann habe ich es einmal
als Template und erzeuge es dann für beide Typen. Besser als Kopieren.
Franz W. schrieb:> Da ich bei meinen Spielereien mit diversen MCUs (STM32, ESP32) bezüglich> der Programmierung immer wieder schnell an meine Grenzen stoße, da ich> kaum über C++ Erfahrung verfüge und diese Sprache ganz allgemein nicht> so prickelnd finde (um nicht zu sagen, dass ich mittlerweile ein> erklärter C++-Hasser bin)
Nun,ich verstehe dies sehr gut. Insbesondere wenn man vor hat, die
Libraries von ST zu verwenden, wird es echt unübersichtlich. Kommt dazu
noch sowas wie LWIP, kann man sich von der Übersichtlichkeit
verabschieden. Das Ganze ist in C schon eine Herausforderung und wird in
C++ durch das ganze Mapping zu einem Kauderwelsch, was man nicht
braucht.
Ja, man kann alles auch selber schreiben - muss man aber nicht.
In C++ gefällt mit das Bilden von Klassen und Methoden, sowie die
Möglichkeit der Kapselung von Variablen, sehr gut. Warum C++ auf
Microcontrollern so wenig Verwendung findet, ist wohl nur durch den
begrenzten Speicherplatz zu erklären. Viele Vorteile der Sprache kosten
wohl zuviel Platz.
Gerhard W. schrieb:> Warum C++ auf Microcontrollern so wenig Verwendung findet, ist wohl nur> durch den begrenzten Speicherplatz zu erklären.
Nein, eher durch Vorurteile wie:
>Viele Vorteile der> Sprache kosten wohl zuviel Platz.
Johannes S. schrieb:> Gerhard W. schrieb:>> Warum C++ auf Microcontrollern so wenig Verwendung findet, ist wohl nur>> durch den begrenzten Speicherplatz zu erklären.>> Nein, eher durch Vorurteile wie:>>>Viele Vorteile der>> Sprache kosten wohl zuviel Platz.
Sehe ich genauso: gerade templates und header-only ermöglichen dem
Compiler beste Optimierungsvoraussetzungen, die man eben mit
object-libraries nicht hat.
Compiletime-Computation spart Laufzeit und/oder externe Tools. Und
Meta-Funktionen ermöglichen bestmögliche Expressivität und damit
Fehleraufdeckung ebenfalls zur Compilezeit.
Hallo,
laut meiner Erfahrung eignen sich Klassen Templates aber nicht dazu
Objekt Instanzen mittels Array anzulegen um damit einfach darüber in
einer for Schleife iterieren zu können. Dazu benötigt man wieder 'new'
zum initialisieren und für den Zugriff virtuelle Methoden was die
Codegröße regelrecht explodieren lässt. Was genau den gegenteiligen
Effekt bringt was man vorher mühselig eingespart hat. Deswegen komme ich
wieder weg von Klassen Templates. Wenn der Code nicht explodieren soll
kann man nur einzelne Instanzen anlegen und muss dann auch alle einzeln
behandeln was keinen großen Sinn macht.
Veit D. schrieb:> laut meiner Erfahrung eignen sich Klassen Templates aber nicht dazu> Objekt Instanzen
das ist eine Tautologie: Objekte sind (unbenannte) Instanzen von Typen
(Klassen)
> mittels Array anzulegen um damit einfach darüber in> einer for Schleife iterieren zu können.
Da std::array<> ein homogener Container ist, kannst Du ja nur Objekte
eines Typ darin aufnehmen.
Veit D. schrieb:> Dazu benötigt man wieder 'new'> zum initialisieren und für den Zugriff virtuelle Methoden was die> Codegröße regelrecht explodieren lässt.
Du meinst also hier wohl eher, dass Du ein Array mit Basisklassenzeigern
anlegst. Was hat das jetzt mit dem code-bloat durch templates zu tun,
wenn die std::array<Base*,N> verwendest?
Veit D. schrieb:> Was genau den gegenteiligen> Effekt bringt was man vorher mühselig eingespart hat.
Nun ist also Laufzeitpolymorphie doch schlechter in Deinen Augen als
statische Polymorphie? Etwas wirr.
Veit D. schrieb:> Wenn der Code nicht explodieren soll> kann man nur einzelne Instanzen anlegen und muss dann auch alle einzeln> behandeln was keinen großen Sinn macht.
Du meinst statt eines std::array<A,N> dann N-mal eine eigene Variable?
Ich glaube, Du sortierst besser erst einmal Deine Gedanken.
Hallo,
vielleicht nur unglücklich ausgedrückt.
Ich habe zum Bsp. eine Klasse ohne Template.
Mit der Klasse kann ich mir ein Array namens object bauen und mittels
for darüber iterieren.
1
classFoo
2
{
3
private:
4
constuint8_tmember;
5
6
public:
7
Foo(byten):member{n}
8
{}
9
10
uint8_tgetData(){returnmember;}
11
};
12
13
Fooobject[]=
14
{
15
{10},
16
{5}
17
};
18
19
voidsetup(void)
20
{
21
Serial.begin(250000);
22
Serial.println(F("\nµC Reset ### ### ###"));
23
24
for(Foo&i:object){
25
Serial.println(i.getData());
26
}
27
}
28
29
voidloop(void)
30
{}
Das geht laut meines Wissens mit einem Klassen Template nicht so leicht.
Dieses sieht wie folgt aus. Jedes Objekt muss einzeln angelegt und
behandelt werden.
1
template<constuint8_tn>
2
classFoo
3
{
4
private:
5
constuint8_tmember{n};
6
7
public:
8
Foo()=default;
9
10
uint8_tgetData(){returnmember;}
11
};
12
13
Foo<10>obj1;
14
Foo<5>obj2;
15
16
voidsetup(void)
17
{
18
Serial.begin(250000);
19
Serial.println(F("\nµC Reset ### ### ###"));
20
21
Serial.println(obj1.getData());
22
Serial.println(obj2.getData());
23
}
24
25
voidloop(void)
26
{}
Um die Klassen Template Objekte als Array anzulegen benötige ich new zum
anlegen dieser und eine zusätzliche Schnittstelle die mir durch
virtuelle Methoden Zugriff auf die eigentlichen Methoden erlaubt. Genau
das lässt den Code explodieren. Oder kennst du eine einfachere
Möglichkeit das man diesen Aufwand nicht treiben muss und die Codegröße
nicht weiter ansteigt? Wohlgemerkt mit normalen Mitteln was die avr-gcc
Toolchain bietet.
Veit D. schrieb:> Um die Klassen Template Objekte als Array anzulegen benötige ich new zum> anlegen dieser und eine zusätzliche Schnittstelle die mir durch> virtuelle Methoden Zugriff auf die eigentlichen Methoden erlaubt.
Ja, denn in Deinem Beispiel sind Foo<1> und Foo<2> ja unterschiedliche
Template-Instanziierungen und damit unterschiedliche Datentypen. Und in
einen homogenen Container kannst Du nur Objekte desselben DT ablegen.
(Das const in der Template-Präambel ist btw. überflüssig für NNTP.)
Natürlich kannst Du das mit einem Interface für das Template Foo<>
umgehen. Also etwa:
1
structI{};
2
template<autoN>structFoo:publicI{
3
// ...
4
};
5
6
Foo<0>f0{42};
7
Foo<1>f1{43};
8
9
I*a[]{&f0,&f1};
Allerdings wäre jetzt die Frage: was modelliert denn Dein template? Eine
HW-Geräte-Instanz wie etwa einen Timer?
Um heterogene bzw. quasi-heterogene Container zu verwenden, kann man
std::variant<> einsetzen (also OHNE den Basistyp I):
1
std::variant<Foo<0>,Foo<1>>a[]{{42},{43}};
oder
1
std::variant<Foo<0>*,Foo<1>*>a[]{&f0,&f1};
(bzw. statt einem rohen C-Array besser mit std::array<>).
Als (echt) heterogener Container dient dann std::tuple<>:
1
autoa=std::make_tuple(Foo<0>{42},Foo<1>{42}};
Allerdings vermute ich, dass Du ggf. etwas modellieren möchte, was man
anders machen sollte.
(P.S.: alles obige kann beliebig viele Tippfehler enthalten)
Hallo,
Danke. Das sieht auf den ersten Blick vielversprechend aus. Beim
ausprobieren stelle ich aber fest, das struct I keinen Zugriff auf die
Member/Methoden von Foo hat. Wie auch, die Vererbung verläuft doch von I
nach Foo und nicht umgekehrt.
1
structI{};
2
3
template<uint8_tn>
4
structFoo:publicI
5
{
6
Foo()=default;
7
uint8_tgetData(){returnn;}
8
};
9
10
Foo<42>obj0;
11
Foo<43>obj1;
12
13
I*object[]{&obj0,&obj1};
14
15
voidsetup(void)
16
{
17
Serial.begin(250000);
18
Serial.println(F("\nµC Reset ### ### ###"));
19
20
Serial.println(obj0.getData());
21
Serial.println(obj1.getData());
22
23
for(I&i:object)
24
{
25
Serial.println(i.getData());
26
}
27
}
28
29
voidloop(void)
30
{}
Wenn ich das verstanden habe bzw. mein Fehler geklärt ist können wir uns
über den Zweck meinen Anliegens unterhalten. Sonst wird es mir zu
durcheinander.
Eigentlich benötigt man wie du das zeigst new zur Objekt Initialisierung
im Array und das struct I beinhaltet die virtuellen Methoden. Ich lande
demzufolge immer wieder bei dem Aufbau.
Veit D. schrieb:> Eigentlich benötigt man wie du das zeigst new zur Objekt Initialisierung> im Array und das struct I beinhaltet die virtuellen Methoden. Ich lande> demzufolge immer wieder bei dem Aufbau.
Ich denke in der Realität könnte es auch so aussehen. Dafür wäre dann
auch kein Heap notwendig.
1
// Code der Klassen, wie bei devil-elec
2
structInterface;
3
template<uint8_tn>
4
structFoo:publicInterface;
5
6
std::array<Interface*,2>foo;
7
8
voidsetup()
9
{
10
Serial.begin(250000);
11
Serial.println(F("\nµC Reset ### ### ###"));
12
for(Interface*i:foo)
13
{
14
Serial.println(i->getData());
15
}
16
}
17
18
intmain()
19
{
20
// Geräte spezifischer Code
21
Foo<42>interface_A;
22
Foo<43>interface_B;
23
24
foo[0]=&interface_A;
25
foo[1]=&interface_B;
26
27
// Allgemeiner Code
28
setup();
29
30
// Man darf main nicht verlassen, sonst wären die Pointer in foo dangling.
Hallo,
Danke, selbst wenn ich das sortiere, ich habe doch gar kein std::array<>
zur Verfügung. Ich nutze doch den avr-gcc. Dem fehlt die all umfassende
Standard Libray.
Gerald K. schrieb:> C++ Wissen kann die Porgrammierung in C positiv beeinflussen. Z.B. beim> Organisieren von Daten durch Kapselung von Daten.> https://de.m.wikipedia.org/wiki/Datenkapselung_(Programmierung)>> Und Erstellung von Zugriffsfunktionen.
Nun ja. Wenn man auf sämtliche Daten einer Klasse über Getter- und
Setter-Funktionen zugreifen kann, dann hat man an Datenkapselung nicht
wirklich viel gewonnen. ;-)
Besser: Objekte tauschen zur Laufzeit Nachrichten miteinander aus. So
wie es ursprünglich in Smalltalk gemacht wurde.
Veit D. schrieb:> Danke, selbst wenn ich das sortiere, ich habe doch gar kein std::array<>> zur Verfügung. Ich nutze doch den avr-gcc. Dem fehlt die all umfassende> Standard Libray.
Dann ist es eine gute Übung, sich gerade mal ein std::array<> zu
schreiben.
Oben stand Arduino, dass muss ja nicht zwingend AVR sein. Aber ich habe
es mir schon gedacht, dass Du Arduino/AVR meinst.
std::variant<> ist natürlich schon kompilzierter, damit man UB
vermeidet. Es schadet aber auch nicht, sich die Implementierung in der
stdlib anzuschauen.
Veit D. schrieb:> Wenn ich das verstanden habe bzw. mein Fehler geklärt ist können wir uns> über den Zweck meinen Anliegens unterhalten. Sonst wird es mir zu> durcheinander.
Ich hatte ja geschrieben, dass ich das ohne IDE so herunter getippt
habe. Und dabei habe ich es mir einfach gemacht, und Deine
Basisklasse/Schnittstelle nicht ausformuliert. Ich dachte, wenigstens
das sein klar. Ok, Du hast es dann selbst gewmerkt.
Allerdings ist der new-Op wirklich nicht nötig, dass hatte ich Dir oben
ja schon gezeigt.
Ich bin immer noch der Ansicht, dass Du hier etwas versuchst, was
entweder der dyn. oder der statischen Polymorphie entgegen läuft. Eine
Mischung von beidem ist meistens nicht gut.
Mark B. schrieb:> Nun ja. Wenn man auf sämtliche Daten einer Klasse über Getter- und> Setter-Funktionen zugreifen kann, dann hat man an Datenkapselung nicht> wirklich viel gewonnen. ;-)
Das sollte man ja auch auf keine Falls tun. Auch die Bezeichnung
getter-/Setter-Funktionen ist ja m.E. daneben, auch wenn es viel
verwendet wird (aber weniger im C++ Umfeld).
In C++ ist eine const-Elementfunktion ein Beobachter (getter), und alles
was nicht const ist, ist dann ein Mutator (setter). Denn der
beobachtbare Zusatnd eines Objektes muss nicht 1:1 aus den
Datenelementen des Typs bestehen, sondern kann beliebig daraus berechnet
werden.
Hallo,
ganz ehrlich, ich komme mir etwas veralbert vor. Weil ohne new geht es
nicht. Probiere es bitte aus. Wie es ohne new gehen soll weiß ich nicht
und ich kann es auch nicht erkennen. Wenn das alles Quark ist dann zeige
mir bitte wie man es anders macht. Im Grunde sollte es im Bsp. auch egal
wofür man ein Klassen Template baut. Hier gehts doch nur darum wie man
die dann davon instanzierten Objekte mittels for Schleife iteriert,
wofür man ein Array benötigt. Ganz am Anfang hatte ich geschrieben das
ich einen avr-gcc verwende. Das ist unabhängig ob Arduino oder nicht
Arduino. Mit der Arduino IDE bin ich nur schneller am testen. Tief
durchatmen ...
Veit D. schrieb:> Hallo,>> ganz ehrlich, ich komme mir etwas veralbert vor. Weil ohne new geht es> nicht. Probiere es bitte aus.
Mit welchem konkreten Code und Compiler
hast du es denn ausprobiert? Was sagt dir, dass es nicht geht? Wie von
wimalopaan und mir gezeigt braucht man dafür keinen Heap, also kein new.
Man braucht new nur, wenn man die Objekte dynamisch Objekte anlegen,
weil es verschiedene Typen sind.
Hallo,
irgendwie hatten wir aneinander vorbei geredet. ;-)
So Leute, jetzt habe ich das hinbekommen.
Vielen Dank an eure und meine Geduld. :-)
1
structInterface
2
{
3
virtualuint8_tgetData()=0;
4
};
5
6
template<uint8_tn>
7
structFoo:publicInterface
8
{
9
Foo()=default;
10
uint8_tgetData(){returnn;}
11
};
12
13
Foo<42>f0;
14
Foo<43>f1;
15
16
Interface*foo[2]={&f0,&f1};
17
18
voidsetup(void)
19
{
20
Serial.begin(250000);
21
Serial.println(F("\nµC Reset ### ### ###"));
22
23
for(Interface*i:foo)
24
{
25
Serial.println(i->getData());
26
}
27
}
28
29
voidloop(void)
30
{}
Das heißt ohne new klappt das aber das Interface mit den virtuellen
Methoden benötigt man weiterhin? Richtig?
Was ist am Namen getData() verkehrt? Ich hole mir doch Daten mittels
einer Methode. Wie benennst du solche Methoden? Sowas nennt man doch
Gettermethoden --> get...
@ M.K.B.
Ich bin von dem unteren Code 13:49 Uhr ausgegangen und habe new
weggelassen wollen, was nicht ging. Das ist nun hinfällig. :-)
avr-gcc 10.3.0 auf C++20 eingestellt.
Bei dem range based for muss der Typ links der selbe wie im Container
sein oder konvertierbar sein.
Interface* kann nicht zu Interface& konvertiert werden. Man könnte
Interface*& machen, aber beim Pointer ist das nicht so sinnvoll.
Hallo,
verstehe, Danke.
Wenn man ohne virtuelle Methoden auskommen möchte, dann benötigt man
std::array? Soweit sollte ich dann richtig verstanden haben?
An dem Punkt macht es Sinn weiter auszuholen. Wofür die generelle Frage?
Meine bisherigen Klassen Templates beziehen sich auf die Hardware eines
µC. Aktuell für einen ATmega4809 und AVR128DB48 bezogen. Diese dienen
zum konfigurieren, schalten und walten der I/O Pins und zum
konfigurieren der Timer TCA und TCB. Bei der Pin Klasse würde ein Array
richtig Sinn machen. Beim TCA eher weniger. Beim TCB schon mehr, weil
vom TCB Typ mehrere vorhanden sind.
Es würde also Sinn machen wenn man über alles bequem iterieren kann.
Desweiteren habe ich noch eine Timer Klasse erstellt die als
Kurzzeitwecker oder Stoppuhr dienen kann. Die nutzt den millis Zähler
von Arduino und ist ansonsten unabhängig. Davon kann man auch mehrere
Instanzen gebrauchen.
Was wäre die anfangs angesprochene Alternative?
Ich glaube da gibt es noch ein Missverständnis.
Die virtuellen Methoden braucht man, damit man über den gemeinsamen Typ
des Interfaces verschiedene Ableitungen mit verschiedenen
Implementierung ansprechen kann.
Damit kann man z.B. eine Methode mit Referenz oder Pointer auf das
Interface anlegen, die aber nichts von den möglichen Implementierungen
wissen muss.
1
voidsetup(Interface&ifc);
Das Problem mit dem Array und new ist ein anderes. Für den Zugriff auf
das Interface reicht ein Pointer/Referenz, um damit arbeiten zu können.
Möchte man jedoch die implementierenden Objekte anlegen, dann brauchen
diese Speicher, dessen Größe und Initialisierung aber vom konkreten
Objekt abhängen.
Ein Array kann von seinem Prinzip her aber nur gleiche Typen speichern.
Mit new holt man sich den Speicher vom Heap. Das praktische daran ist,
dass es reicht wenn man den Pointer auf die Basisklasse behält, um das
Objekt später zerstören zu können. Das Array hält dann nur noch die
Pointer, die dann wieder vom gleichen Typ sind.
Statt Heap könnte man auch noch std::variant für das Array verwenden.
Oder man legt sich einen statischen Speicherbereich an, gibt diesen
einer std::pmr::monotonic_buffer_resource und legt dort seine Objekte
mit std::pmr::polymorphic_allocator an. Das ist aber sehr neu und
vermutlich nicht in allen Compilern verfügbar. Wenn es trotzdem jemand
ausprobieren will, dann sollten wir dafür aber einen neuen Thread
aufmachen.
https://opensource.googleblog.com/2020/03/pigweed-collection-of-embedded-libraries.html
ist auch großteils in C++ geschrieben.
Virtuelle Funktionen sind natürlich sehr wohl möglich und auch sinnvoll
verwendbar. Kommt halt auf die Anwendung drauf an. Die meisten embedded
Applikationen sind halt recht statisch und sollten da auch mit einem
"statischen" Polymorphismus auskommen, da können templates helfen den
Code übersichtlich und sicher zu machen (im Vergleich zu C-Makros). Auch
exceptions sind kein Problem und funktionieren ausgezeichnet auf ARM.
Man muss halt herumfiddln und newlib-nano mit aktivierten exceptions
selber bauen.
Insgesamt muss man halt genau wissen welches feature wieviel overhead
erzeugt, und man darf sich auch vorm asm listing nicht fürchten.
Veit D. schrieb:> Was ist am Namen getData() verkehrt? Ich hole mir doch Daten mittels> einer Methode. Wie benennst du solche Methoden? Sowas nennt man doch> Gettermethoden --> get...
Es geht überhaupt nicht um die Benennung: Hast Du meinen Post weiter
oben eigentlich gelesen? Dort stand:
Wilhelm M. schrieb:> In C++ ist eine const-Elementfunktion ein Beobachter (getter), und alles> was nicht const ist, ist dann ein Mutator (setter).
Veit D. schrieb:> Was wäre die anfangs angesprochene Alternative?
Die Alternative ist parametrische (statische) Polymorphie. Denn die
(meisten ;-) HW-Geräte sind nur in endlicher Anzahl vorhanden. Der macht
es wenig Sinn, für die 4 TCB der avr0 eine konkrete Klasse zu schreiben.
Denn diese kann man beliebig oft instanziieren. Um das zu verhindern,
müsste man entweder factorys einsetzen und / oder die Klasse als
monostate realisieren.
Beides ist eigentlich eine Krücke, weil Fehler erst zur Laufzeit
auftauchen, obwohl es ein statisches Problem ist. Die Alternative wäre
ein Klassentemplate wie etwa
1
template<autoN,typenameMCU>
2
requires(N<4)// c++20
3
structTCB{
4
static_assert(N<4);// pre-c++20
5
// ...
6
};
wobei man natürlich den Contraint bzw. die statische Zusicherung besser
als Metafunktion über den MCU-Type schreibt, denn nicht jede MCU hat 4
Instanzen den TCB.
Also: statt Objekte zu instanziieren (Laufzeit), instanziiert man
Templates (Compilezeit).
Das ist das grobe Muster, was man beliebig verfeinern kann.
Und es wird wohl kaum bis nie vorkommen, dass Du über die Timer
iterieren willst. Und wenn doch, dann geht das auch z.B. durch
Fold-Expressions ganz einfach und Maschinencode-sparender als zur
Laufzeit mit dyn. Polymorphie.
Veit D. schrieb:> Wenn man ohne virtuelle Methoden auskommen möchte, dann benötigt man> std::array? Soweit sollte ich dann richtig verstanden haben?
Nein, da besteht kein Zusammenhang.
Dein Problem scheint zu sein, dass Du nicht verstanden hast, was dyn.
Polymorphie bedeutet. Google mal bzw. schlage eine Buch auf an der
Stelle: Liskov'sches Substitutionsprinzip und eben
Schnittstellenvererbung sowie Sub-Typ / Super-Typ-Beziehung ("is-a").
Wikipedia geht auch.
Veit D. schrieb:> Beim Timer habe ich mitstatic_assert(n < 4, "ATmega4809 has only 4> TCB");> schon vorgesorgt das man keinen Unsinn macht. :-)
Jetzt musst Du allerdings natürlich auch noch unterscheiden, dass die
mega0-Serie keinen TCD hat, die avr128db allerdings schon.
Wenn Du nun den MCU-Typ als DT modellierst, dann brauchst Du eine
Meta-Funktion, um den DT der MCU auf ein Integer abzubilden (type-traits
sind eine Form von Meta-Funktionen). Das ist ganz leicht, aber auch ein
gutes Beispiel für solche Compilezeit-Berechnungen.
Wenn man Compilerzeitberechnungen macht, die Werte und keine Typen
berechnen, dann kann man mit neueren C++ Versionen auch constexpr
Funktionen und constexpr if verwenden.
Das sieht dann aus wie normaler Code und ist leichter lesbar als
Templates.
Man kann auch Typ->Typ, oder Wert->Typ Abbildungen mit constexpr bzw.
immediate Funktionen machen, allerdings benötigt der Aufrufer kann immer
noch eine extra Typinferenz via decltype, was auch nicht besonders toll
aussieht.
Ich bevorzuge für die beiden Arten von den vier immer noch traditionelle
Meta-Funktionen als template, während ich oft Typ->Wert oder natürlich
immer Wert->Wert als reguläre Funktionen schreibe.
Die MCU Abhängigkeit wollte ich einfacher lösen.
Jeder Timer hat seine eigene Klasse. TCA, TCB, TCD.
Die Klassen werden intern auf die Anzahl der Timer pro Typ je nach MCU
begrenzt. Soweit die aktuelle Überlegung dafür.
Ganz am Ende gibts MCU spezifisch eine "Hauptklasse" die alle anderen
inkludiert was diese MCU an Hardware hat. Wenn ein ATmega4809 keinen TCD
hat, dann wird die TCD Klasse einfach nicht inkludiert. Sprich die
#include Zeile nicht getippt. Und selbst wenn man das fälschlicherweise
machen würde geht das auch nicht, weil die Registeradressen und Bit
Definitionen nicht vorhanden wären und mir der Compiler dann sowieso
alles um die Ohren haut.
Veit D. schrieb:> Die Klassen werden intern auf die Anzahl der Timer pro Typ je nach MCU> begrenzt.
Ja, genau das meine ich ja (s.o.). Dazu kannst Du dann die Anzahl aus
dem MCU-Typ berechnen, bei anderer interner Peripherie ist das ja
genauso. Und das Nichtvorhandensein entspricht dann Anzahl 0.
Franz W. schrieb:> Hat jemand von euch hier einen Tipp ? Mir geht es wie gesagt in erster> Linie um C++ - eine Einführung in Mikrocontroller ist nicht so> notwendig.
Weiß nicht, ob's noch notwendig ist, aber als gute Bücher zum Thema habe
ich die folgenden selber gekauft und kann die bedenkenlos empfehlen:
- Stephan Roth: Clean C++
- Bruce Eckel: Thinking in C++
- Andrei Alexandrescu: Modern C++
Gruß,
db8fs
So, da hab ich ja was losgetreten...
Danke für die Literaturtipps - ich hab mir folgende Bücher gekauft und
wollte meinen Eindruck schildern:
Programming Principles and Practice Using C++ (Stroustroup)
- verständlich geschrieben
- sehr umfangreich
- hat sogar ein eigenes Kapitel, das sich mit den Einschränkungen der
Embedded-Programmierung beschäftigt (Memory-Fragmentation etc.)
- Standardwerk, daher sind die Beispiele vielleicht etwas gekünstelt
bzw. von meinem Bedarf entfernt
C++ Primer (Lippmann, Lajoje, Moo)
- noch verständlicher geschrieben
- wirkt auf mich etwas kompakter
- wenn ich was suche, schaue ich zuerst in dieses Buch
- wirkt auf mich etwas praxisnäher als der Stroustroup (wobei der für
ein Standardwerk auch recht zugänglich ist)
Den Rest hole ich mir über diverse youtube-Videos.
Von C++ verwende ich nur einen kleinen Teil, aber der ist mir den
Wechsel von C allemal wert:
* weit besseres Typsystem mit strikteren Compiler-Regeln
* daher mehr Fehler zur Compile-Zeit erkennbar
* Namespaces
* Klassen (ohne komplizierte Vererbungskonstrukte)
* die etwas modernere Syntax
C++ habe ich zuletzt irgendwann in den 90er-Jahren programmiert, konnte
mich aber nie wirklich damit anfreunden. Allerdings muss ich zugeben,
dass es hier in den letzten 30 Jahren eine offensichtliche
Weiterentwicklung gegeben hat und das heutige C++ (11/14) schon um
einiges moderner ist als damals. Nichts desto trotz ist es leider recht
mühsam, auf dynamische Speicherreservierung zu verzichten und so gleich
mal die std-lib auszuschließen. Jede String-Manipulation mit char* fühlt
sich an wie Long Covid, aber das liegt nicht an der Sprache, sondern an
der Zielumgebung.
Hallo,
ich habe nochmal klassische Lib mit .h/.cpp mit der Klassen Template
Variante verglichen. Irgendwie stört mich das mit jeder virtuellen
Methode, die nur zur Verfügung steht aber nicht genutzt werden muss, der
Flashverbrauch immer weiter ansteigt. Man hat zwar vom Flash eigentlich
immer genügend nur wenn das mehr Libs werden wird das unschön.
Jetzt bin ich erstmal bei der klassischen .h/.cpp Variante hängen
geblieben. Hierbei stellt sich mir die Frage warum beim Pin Toggle ein
Pegelwechsel 2µs dauert. Alle verwendeten Variablenwerte sind const bzw.
wo immer möglich constexpr. Aus meiner Sicht ist alles zur Compiletime
bekannt.
Aus einer Port Basisadresse wird mittels Offset das eigentliche Register
ermittelt und dann mit einer Bitmaske geändert. Mehr ist das nicht. Das
Einzigste was ich nicht constexpr machen kann ist die letztlich
verwendete Methode toggle(). Warum weiß ich nicht. Kann es sein das das
mit getrennten .h/.cpp einfach nicht geht? Beim Versuch bekomme ich
immer
"error: inline function 'constexpr void OutputP::toggle() const' used
but never defined [-Werror]". Lasse const weg
Lasse ich const weg bekomme ich:
"error: inline function 'constexpr void OutputP::toggle()' used but
never defined [-Werror]".
Veit D. schrieb:> Irgendwie stört mich das mit jeder virtuellen> Methode, die nur zur Verfügung steht aber nicht genutzt werden muss, der> Flashverbrauch immer weiter ansteigt.
Das Thema zieht sich ja nun schon durch deine ganzen Beträge dieses
Subthemas hier, aber nach wie vor ist völlig unklar, was virtuelle
Funktionen mit Templates zu tun haben sollen.
Und warum man virtuelle Funktionen zur Verfügung stellen sollen müsste,
die gar nicht genutzt werden, musst du auch mal erklären. Abgesehen
davon, daß der Compiler die bei der Optimierung einfach rauswirft. Das
einzige, was bei virtuellen Funktionen in Zusammenhang mit Arduino-AVR
wirklich stört, ist die unschöne Platzierung der vtables im SRAm durch
den avr-gcc.
Irgend etwas wirfst du da noch ganz gehörig durcheinander.
Veit D. schrieb:> Alle verwendeten Variablenwerte sind const bzw.> wo immer möglich constexpr. Aus meiner Sicht ist alles zur Compiletime> bekannt.
Dann wird der Compiler das auch zur Compilezeit auswerten, und das auch
ohne constexpr. Allerdings nur, wenn er das auch kann, d.h. er kann alle
const(expr)-Deklarationen sehen.
Bei solchen Fingerübungen muß man halt immer den generierten
Asemblercode anschauen.
Oliver
Oliver S. schrieb:> Das einzige, was bei virtuellen Funktionen in Zusammenhang mit> Arduino-AVR wirklich stört, ist die unschöne Platzierung der vtables> im SRAm durch den avr-gcc.
Naja, jede virtuelle Funktion kostet damit 2 Byte im SRAM. Lägen die
Vtables im Flash, bräuchte jeder virtuelle Funktionsrufruf ein paar
Taktzyklen mehr, was auch nicht perfekt ist. Wenn man virtuelle
Funktionen irgendwo wirklich sinnvoll einsetzen kann, ist IMHO beides
ganz gut verschmerzbar.
Hallo,
virtuelle Methoden werden nicht rausgewurfen. Auch dafür habe ich ein
Assembler Listing gemacht.
Ich erkläre es nochmal.
Wenn ich mit mehreren erstellten Objekten eines Klassentemplates das
selbe machen möchte, wäre es ja blöd immer alle einzeln zu behandeln was
im Bandwurm endet. Wofür wurden Arrays und for Schleifen erfunden? Genau
das geht nun einmal nicht mit Klassen Templates. Darum braucht es ein
zusätzliches Interface mit virtuellen Methoden.
Ich habe das nochmal am Beispiel kompilieren lassen.
Ein Objekt eines Klassen Templates erstellt. Ohne virtuelle Methoden,
die sind in der Lib noch auskommentiert. Sprich einen Output Pin
initialisiert, 1x init und 1x toggle aufgerufen. Macht 806 Byte Flash
und 22 Byte RAM. Soweit alles okay.
Danach init und toggle als virtuelle Methode zur Verfügung gestellt. Im
Code noch nicht genutzt.
Macht 880 Byte Flash und 24 Byte RAM.
Danach alle 11 Methoden virtuell zur Verfügung gestellt. Im Code noch
nicht genutzt.
Macht 1006 Byte Flash und 24 Byte RAM.
Nehme ich so einen zweiten Pin dazu macht das 1172 Byte Flash und 26
Byte RAM.
Werfe ich die beiden Pin Objekte in ein Array und nutze damit die
virtuellen Methoden jeweils 1x pro Objekt macht das 1250 Byte Flash und
30 Byte RAM.
Schaut man sich das Assembler Listung an sieht man ab Zeile 114 alle
virtuellen Methoden.
1
#include<NanoEveryPin.h>
2
3
OutputPin<12>led12;
4
OutputPin<13>led13;
5
6
Interface*outpin[2]={&led12,&led13};
7
8
voidsetup(void)
9
{
10
for(Interface*i:outpin)
11
{
12
i->init();
13
}
14
15
}
16
17
voidloop(void)
18
{
19
for(Interface*i:outpin)
20
{
21
i->toggle();// 1,25µs
22
}
23
}
Mache ich das wie anfangs mit 2 Objekten ohne virtuelle Methoden und
damit ohne Array macht das 812 Byte Flash und 22 Byte RAM. Jetzt kann
sich jeder ausrechnen wohin das explodiert mit den virtuellen Methoden.
Das new weglassen macht keinen Unterschied. Etwas langsamer taktet ein
Pin mittels virtuellen Methoden auch. 1,25µs für einen Pegelwechseln.
Ohne sind 125ns drin.
Wenn mir das Oliver ohne virtuelle Methoden zeigen könnte wäre ich sehr
dankbar. ;-)
Meine Frage lautet bekommt man die klasssische Varianten .h/.cpp noch
weiter optimiert? Sodass zur Compilezeit noch mehr aufgelöst werden
kann? Wie das alles ausschaut habe ich gezeigt. Es steht im Grunde alles
vorher fest. Nur die Methode toggle lässt sich nicht inline und auch
nicht constexpr machen. Wobei er es auch selbst optimieren könnte aber
nicht macht.
Ansonsten müßte ich mich entscheiden ob ich Schnelligkeit hätte oder
einen Code Bandwurm. Jedesmal die Lib zu ändern oder unterschiedliche
Libs anzulegen macht ja auch keinen Sinn. Man wäre nur am ändern und
bekommt keine Ruhe rein. Deswegen der Aufwand und meine Fragen.
(wieder gelöscht...)
Um mehr sagen zu können, müsste man den aktuellen Stand sehen.
Am besten mit den bisherigen Tipps eingepflegt, wie z.B. getter-Methoden
als const.
Veit D. schrieb:> Wofür wurden Arrays und for Schleifen erfunden? Genau> das geht nun einmal nicht mit Klassen Templates.
Natürlich geht das. Das hatte ich schon zigmal gesagt und Dir auch
Beispiel-Code dazu gegeben. Du brauchst kein Laufzeit-Interface.
Irgendwie habe ich das Gefühl, dass Du das entweder gar nicht liest,
nicht durchdenkst oder nicht willst.
Du kannst sowohl eine Laufzeit-Iteration formulieren (die der Compiler
bis zu einer begrenzten Array-Größe auch ausrollt) oder auch eine
Compile-Zeit-Iteration benutzen.
Wilhelm M. schrieb:> Du kannst sowohl eine Laufzeit-Iteration formulieren (die der Compiler> bis zu einer begrenzten Array-Größe auch ausrollt)
Allerdings ist der Aufwand dafür ohne verfügbare C++-Standardlib, d.h.
ohne tuple, variant, array, etc., doch schon etwas größer.
Aber die eigentliche Frage ist doch: was ist das Ziel der ganzen Übung?
Oliver
Veit D. schrieb:> Nur die Methode toggle lässt sich nicht inline und auch> nicht constexpr machen. Wobei er es auch selbst optimieren könnte aber> nicht macht.
Als Denkübung könntest du ja einmal beschreiben, was genau ein zur
Compilezeit per constexpr auswertbares toggle eigentlich tun soll. Das
hilft dir vielleicht, die Abgrenzungen der zur Compilezeit bekannten,
statischen constexpr-Template-Typenwelt von der dynamischen
Laufzeitwelt besser zu erkennen.
Oliver
Das Buch ist nicht wirklich für einen absoluten C++-Anfänger geeignet,
ich fand es aber interessant:
Real-Time C++
Efficient Object-Oriented and Template Microcontroller Programming
https://www.springer.com/de/book/9783662567173
Christian T. schrieb:> Das Buch ist nicht wirklich für einen absoluten C++-Anfänger geeignet,> ich fand es aber interessant:>> Real-Time C++>> Efficient Object-Oriented and Template Microcontroller Programming>> https://www.springer.com/de/book/9783662567173
Das Buch selbst kenne ich nicht, die Vorschau sieht aber ganz gut aus.
Wichtig ist ja nicht, ob C oder C++, sondern was die einzelnen
Konstrukte kosten (Laufzeit, Speicher, Entwicklungszeit).
Damit kann man dann auch fundiert entscheiden, ob man es verwenden
will/kann.
Wilhelm M. schrieb:> Auch das Buch (in einer vorigen Auflage) steht schon in der Liste in> diesem Beitrag drin (hatte ich hier sehr weit oben schon erwähnt, wird> natürlich jetzt nicht mehr gelesen):
Ich denke um solche Duplikate zu vermeiden ist es sinnvoll wenigstens
den Titel des Buches zu erwähnen, danach hatte ich hier gesucht, aber
nicht jeden Link in jedem Beitrag geclickt.
Hallo,
@ Wilhelm:
Das ist ganz schön starker Tobak von dir. Ohne Interface und ohne C++
Standardlib ist das möglich?
M.K.B. hatte ich auch so verstanden. Ohne C++ Standardlib benötigt man
virtuelle Methoden.
@ Klaus:
Wenn du die Lib mit Klassen Template meinst kann ich heute Abend
bereitstellen.
Das Ziel ist ich möchte mir eine Lib schreiben um Pins zu verwalten.
Eingänge lesen, Ausgänge schalten. Das alles möglichst schlank und
schnell. Und es muss für eine Array Objektverwaltung nutzbar sein. Bis
jetzt kann ich entweder das Eine oder das Andere machen. Beides zusammen
funktioniert wie oben beschrieben. Mit Array und mittels for Schleifen
Iteration kann man dann zum Bsp. Eingänge mit Ausgängen verknüpfen zum
schalten und walten.
> Als Denkübung könntest du ja einmal beschreiben, was genau ein zur> Compilezeit per constexpr auswertbares toggle eigentlich tun soll. Das> hilft dir vielleicht, die Abgrenzungen der zur Compilezeit bekannten,> statischen constexpr-Template-Typenwelt von der dynamischen> Laufzeitwelt besser zu erkennen.
Genau darum geht es. Ich kann nicht erkennen warum das der Compiler ohne
Klassen Template nicht zur Compilezeit auswerten kann.
In beiden Fällen müßte er erkennen das er nur eine Bitmaske in VPORTIN
schreiben muss und das an Ort und Stelle einfügen soll.
Mir hat noch niemand die Frage beantwortet ob es überhaupt möglich ist
die toggle Methode inline oder constexpr zu machen.
Wenn die Compilezeitauswertung ja/nein genau der Unterschied zwischen
Klassen Templates und non Templates ist, dann könnt ihr das ruhig sagen.
Weil genau das steht in keinem Buch und ist keinem Bsp. der Welt
erkennbar. Ich sehe nur das alle Bsp. Header only sind. Aber nirgends
steht das es nur Header only überhaupt möglich ist.
@ Johannes S:
Ich habe schon mitbekommen das es komplett offtopic gewurden ist. Aus
meinem Kommentar wurde eine Unterhaltung. Nur wenn ich jetzt einen
Thread eröffne fange ich bei Null an. Also was soll ich machen?
Veit D. schrieb:> @ Klaus:> Wenn du die Lib mit Klassen Template meinst kann ich heute Abend> bereitstellen.
gerne, aber doch bitte nicht in diesem Thread.
Was hat das bitte mit einer Buchempfehlung zu tun?
Veit D. schrieb:> Das ist ganz schön starker Tobak von dir. Ohne Interface und ohne C++> Standardlib ist das möglich?
Ja. Auch das habe ich Dir schon gezeigt weiter oben.
Ich habe Dir auch gesagt, dass selbst wenn Dur keine stdlib hast, Dir
ein tuple oder variant selbser schreiben kannst. Und wenn das auch
nichts für Dich ist, dann google einfach mal nach "fromscratch c++".
Dann hast Du alles was Du brauchst mundgerecht.
Veit D. schrieb:> Und es muss für eine Array Objektverwaltung nutzbar sein.
Auch da hatte nicht nur ich sondern auch einige andere auch nach
gefragt, warum Du das möchtest. Die Frage hast Du nicht beantwortet.
Auch hatte ich Dir erklärt, warum es (meistens) nicht notwendig ist,
HW-Ressourcen mit Instanzen von Klassen zu modellieren, sondern
statische Template-Instanzen zu verwenden. Anscheinend ist das an Dir
vorbei gegangen.
Veit D. schrieb:> Mir hat noch niemand die Frage beantwortet ob es überhaupt möglich ist> die toggle Methode inline oder constexpr zu machen.
Natürlich kann sie inline sein. Funktionstemplates sind es automatisch.
constepr nicht, weil darin ein reinterpret_cast verborgen sein wird.
Lies Dir mal durch, was constexpr / consteval und constinit bedeutet.
Veit D. schrieb:> Wenn die Compilezeitauswertung ja/nein genau der Unterschied zwischen> Klassen Templates und non Templates ist, dann könnt ihr das ruhig sagen.
Was soll das bedeuten? Du drückst Dich sehr unspezifisch aus.
Veit D. schrieb:> Weil genau das steht in keinem Buch und ist keinem Bsp. der Welt> erkennbar. Ich sehe nur das alle Bsp. Header only sind. Aber nirgends> steht das es nur Header only überhaupt möglich ist.
Hast Du schon mal darüber nachgedacht, warum templates in header
üblicherweise stehen? Hast Du schonmal irgendein Buch darüber gelesen
oder versucht zu verstehen.
Veit D. schrieb:> Mir hat noch niemand die Frage beantwortet ob es überhaupt möglich ist> die toggle Methode inline oder constexpr zu machen.
Inline ist gar kein Problem.
Bezgl. constexpr hast meine Frage überhaupt nicht verstanden. Ich stelle
die mal anders: Erkläre mal den Sinn einer constexpr Funktion vom Typ
void. Oder halt, was ein constexpr Pin-toggle sein soll.
Veit D. schrieb:> Ich kann nicht erkennen warum das der Compiler ohne> Klassen Template nicht zur Compilezeit auswerten kann.
Der Compiler kann und wird alles zur Compilezeit auswerten, was er zur
Compilezeit auswerten kann, egal, ob da nun Template oder constexpr
dransteht oder auch nicht.
Oliver
Veit D. schrieb:> M.K.B. hatte ich auch so verstanden. Ohne C++ Standardlib benötigt man> virtuelle Methoden.
Das sehe ich nicht so.
Du kannst alle C++ Features nutzen. Evtl. fallen new/delete weg, weil du
auf dem Target keinen Heap hast.
Die Standardbibliothek bietet für viele Fälle schon fertig Algorithmen
oder Typen. Wenn diese aber nicht verfügbar sind oder die
Implementierung zu teuer, muss man diese aber nicht nutzen.
Im Prinzip kann man alles aus der Bibliothek auch selbst nachbauen.