Hallo Forengemeinde,
nachdem hier Beitrag "AVR Studio C++ vs. Arduino Processing" gerade ein
interessanter Thread über den Einstieg über C oder C++ im Gange ist
möchte ich hier noch einen separaten Thread speziell über C++ auf µCs
starten.
Ich bin gerade dabei mich in C++ einzuarbeiten weil ich die Hoffnung
habe damit meine Software besser strukturieren zu können. Grundätzlich
ist mir das Konzept von C++ denke ich auch klar aber ein "paar" Details
verstehe ich eben noch nicht ganz und finde im Netz auch noch nicht
richtig die Lösung dafür. So jetzt aber genug
bla bla...
Mein Projekt ist derzeit in C umgesetzt wobei ich versucht habe
möglichst alles objektorientiert umzusetzen, d.h. es gibt keine globalen
Variablen und der Zugriff auf Daten erfolgt über definierte Interfaces.
Speziell geht es um die Ansteuerung und Regelung mehrer E-Motoren die
über den gleichen Algorithmus angesteuert werden aber unterschiedliche
Daten und Parameter besitzen. Weiterhin müssen gewisse
Sicherheitsanforderungen erfüllt werden (z.B. dürfen die Motoren nicht
zu heiß werden). Aus meiner Sicht schreit das ein wenig nach Klassen und
das habe ich in C auch in etwa so umgesetzt. Die aktuelle Umsetzung ist
so:
Für jeden Motor gibt es eine Struktur die die Daten und Parameter
enthält. Die Grundstruktur/Objekt ist so ausgelegt, dass sie/es die
Parameter enthält und einen Pointer auf die Datenstruktur mit den
Variablen. Funktionen erhalten jetzt einen Pointer auf eines dieser
Objekte (also denke ich so eine Art this Pointer) und können damit
Arbeiten. Das Grundobjekt liegt im Flash des Controllers. Das war mir
wichtig, damit sicher gestellt ist das Parameter und Daten immer
zusammen gehören. Im Hintergrund läuft eine CRC Routine über das Flash
die das überprüft. Das Grundobjekt ist in einer Header Datei deklariert
aber nicht näher definiert, d.h. im Header kann ich nur einen Pointer
auf so ein Objekt definieren. Die genaue Spezifikation der Struktur
befindet sich in der C-Quelle.
Meine Problem..
.. ist nun, wie ich ein solches Konstrukt in C++ umsetzten kann (gerne
auch einfacher) weil mein aktueller Code ein ziemliches Pointergrab ist.
Ich habe zwar eine klar definierte Struktur die in allen meinen Modulen
gleich ist aber für die Erzeugung der Objekte ist noch viel Handarbeit
erforderlich was damit auch fehlerträchtig ist. Wenn mir das der C++
Compiler abnehmen würde wäre ich also dankbar.
Was mir wichtig wäre ist, dass Parameter und Daten fest miteinander in
Beziehung stehen. D.h. die Parameter eines Objekts würde ich gerne im
Flash platzieren ebenso wie die Verknüpfung zu den dazugehörigen
Variablen (macht das der C++ Compiler vielleicht irgendwie
automatisch?). Die Parameter werden dann für jeden Motor anders
initialisiert. Soweit ich C++ bisher verstanden habe werden selbst const
definierte Elemente nicht zwangsweise im Flash plaziert. Bei C++11 gibt
es wohl die constexpr aber soweit ich sehen konnte unterstützt der Keil
Compiler den ich einsetzen muss kein C++11 und damit auch keine
constexpr.
Weiterhin suche ich noch nach einer Art Tutorial wo erklärt wird wie der
Compiler bei der Platzierung der einzelnen Objekte vorgeht, d.h. was
kommt wohin, welche Sections werden benötigt und wofür sind die
einzelnen Memory Sections da.
Mein Design soll völlig statisch sein, d.h. kein new oder delete
verwenden. Einen Heap möchte ich also nicht verwenden.
Gibt es für Meine Themen vielleicht fertige Entwurfsmuster mit denen
sich sowas umsetzen lässt?
Ich hoffe ich habe euch nicht mit zu viel Text erschlagen! Über ein paar
Tipps von den Experten würde ich sehr freuen. Und wenn noch zu viel
Glaskugel erforderlich ist einfach sagen ;-)
Viele Grüße,
Jupp
Jupp schrieb:> Das Grundobjekt liegt im Flash des Controllers.
Na da liegen doch erstmal alle Daten. Und dann werden sie zur Laufzeit
ins RAM geladen. Variablen, "Objekte" usw. enstehen dann im RAM.
Jupp schrieb:> Im Hintergrund läuft eine CRC Routine über das Flash> die das überprüft.
Im Ernst? Warum? Traust du dem Flash nicht? Vor allem: Wie machst du
das?
Jupp schrieb:> Weiterhin suche ich noch nach einer Art Tutorial wo erklärt wird wie der> Compiler bei der Platzierung der einzelnen Objekte vorgeht, d.h. was> kommt wohin, welche Sections werden benötigt und wofür sind die> einzelnen Memory Sections da.
Du kannst dem Compiler ja gerne erzählen, in welche section welche Daten
sollen. Wo diese sections nachher aber landen, mußt du schon dem
Linker (im besagten ...script) mitteilen ...
Jupp schrieb:> Ich habe zwar eine klar definierte Struktur die in allen meinen Modulen> gleich ist aber für die Erzeugung der Objekte ist noch viel Handarbeit> erforderlich was damit auch fehlerträchtig ist.
Ich wollte nur mal anmerken, dass du mit typedef eine Art Klasse
erstellen kannst, aus der du mit einer Zeile neue Objekte gleichen Typs
erzeugen kannst
Beispiel:
Button.h Auszug:
Jupp schrieb:> Ich bin gerade dabei mich in C++ einzuarbeiten weil ich die Hoffnung> habe damit meine Software besser strukturieren zu können. Grundätzlich> ist mir das Konzept von C++ denke ich auch klar aber ein "paar" Details> verstehe ich eben noch nicht ganz und finde im Netz auch noch nicht> richtig die Lösung dafür.
Ne, du verstehst C++ nicht. C++ ist eine Multi-Paradigm-Sprache und die
meisten Menschen (inkl. mir) tun sich schwer ein Paradigm komplett zu
verstehen. Allein das Typsystem ist enorm komplex (erlaubt aber auch
enorme Möglichkeiten, zur Compilezeit)...
Simon S. schrieb:> Ich wollte nur mal anmerken, dass du mit typedef eine Art Klasse> erstellen kannst, aus der du mit einer Zeile neue Objekte gleichen Typs> erzeugen kannst
Ne, du verstehst C nicht. ;)
Das macht man hier nicht mit typedef, sondern struct erzeugt deine
"Klasse" (im Prinzip machst du hier nix anderes als manuell das zu tun,
was der C++ Compiler bei einfachen Klassen macht). typedef erzeugt nur
das Alias, sodass du nicht "struct foo variable;" schreiben musst.
kopfkratz
Wo ist denn nun Dein eigentliches Problem ?
Du erstellst eine Klasse MOTOR in der Du die Vorgaben für die Regelung
kapselst und entsprechend Deiner Anforderung regelst.
Oder verstehst Du Kapselung und Vererbung nicht richtig ?
Stell einfach mal Dein aktuelles C Programm und Deine C++ Variante hier
rein und man kann sehen wo es hapert ...
Mir drängt sich der Eindruck auf, dass C++ hauptsächlich für abstrakte
multithread-Anwendungen geeigneter ist.
Also eigentlich nix für einen Mikrocontrollör.
Danke für die schnellen Antworten!
@Ähem: Das alles erst mal im Flash landet und von dort erzeugt wird ist
prinzipiell klar. Das Problem das ich habe ist, das ich komplett
Konstante Objekte (Parameter) habe und diese fest mit mit den
dazugehörigen Daten verknüpft werden sollen. Die Objekte sollen also
auch zur laufzeigt aus dem Flash laufen und nicht bei der Instanziierung
ins RAM geladen werden. Dem RAM traue ich hier nicht so ganz und zum
anderen habe ich recht viele Parameter und würde so sehr viel RAM mit
konstanten Daten verschleudern.
Dem Flash traue ich im Prinzip schon aber ich habe aus der Serie auch
schon Controller mit Flash Fehlern zurück bekommen. Ist also nur eine
Frage der Zeit und Stückzahl das sowas mal auftritt. Weiterhin ist die
CRC Überprüfung eine Normanforderung (Safety Norm) und damit ein Muss
weil die Software auch geprüft/zertifiziert wird.
Mein Ziel ist eigentlich nur zur laufzeit sicher zu stellen, dass
Parameter und Daten/Variablen zur Laufzeit nicht auseinander laufen.
Durch was auch immer (Flash/RAM-Fehler, EMV, Software Bug ...)
@Simon: So in der Art wie Du es schreibst habe ich es im Moment gelöst,
d.h. über typedef werden die Modulobjekt definiert und dann
instanziiert, also mit den entsprechenden Daten belegt. Für jeden Motor
wird dann immer ein Objekt erzeugt und dann an die entsprechendne
Funktionen per Pointer übergeben.
Hier ein Beispiel wie es jetzt ungefähr aussieht:
1
typedefstruct_TModData{
2
uint16_tbla;
3
....
4
}TModData;
5
6
typedefstruct_TModPar{
7
uint16_tblub;
8
....
9
}TModPar;
10
11
typedefstruct_TGenericModObject{
12
TModData*ptDat;
13
constTModPar*ptPar;
14
}TGenericModObject;
15
16
staticTModDataMotor1ObjData={0};
17
18
staticconstTModParMotor1ObjPar={0};
19
20
/* Verknüpfung zwishen Parametern und Daten im Flash => von CRC geprüft */
In den Funktionen kann dann über den this-Pointer auf Daten und Parameter
26
des Motors zugegriffen werden.
27
*/
28
voidFoo(constTGenericModObject*this);
Was meint ihr? Sieht aus meiner Sicht nicht wirklich schön aus und
erfordert in jedem Modul viel Handarbeit. Besonders die Zugriffe auf
Daten und Parameter würde ich gerne einfacher gestalten.
Wie macht ihr das mit sicherheitskritischen Parametern? Liegen die bei
euch zur Laufzeit im RAM oder wie erfolgt der Schutz der Parameter zur
Laufzeit?
Selbstverständlich liegen alle Laufzeitparameter im RAM. Wenn Du dem RAM
nicht traust, kannst Du gleich alles wegwerfen.
Manche Computer unterstützen automatische Fehlererkennung und Korrektur
(Stichwort: ECC memory). Aber bei Mikrocontrollern eher nicht. Da sind
Taktrate und Datendichte niedrig genug, um auch ohne ECC zuverlässig zu
funktionieren.
Manche Computer prüfen ihren Flash Speicher mit Hilfe einer Prüfsumme.
Im Einfachsten Fall erstellt die Firmware beim allerersten Start eine
Prüfsumme von sich selbst und legt sie im EEprom ab. Bei allen folgenden
Starts kontrolliert sie, ob die Prüfsumme noch stimmt. Wenn nicht, ist
der Flash SPeicher oder das EEprom ausgefallen, dann könnte man eine
Fehlermeldung anzeigen. Aber ob das dann überhaupt noch funktioniert,
ist wiederum fraglich. Eher warscheinlch ist, das bei einer Fehlfunktion
des Flash Speichers nichtmal mehr die Selbstkontrolle funktioniert.
Jupp schrieb:> Wie macht ihr das mit sicherheitskritischen Parametern? Liegen die bei> euch zur Laufzeit im RAM oder wie erfolgt der Schutz der Parameter zur> Laufzeit?
Mirroring ist eine mögliche Technik, benötigt bei geteilten Variablen
aber locks.
Marian B. schrieb:> sondern struct erzeugt deine> "Klasse" (im Prinzip machst du hier nix anderes als manuell das zu tun,> was der C++ Compiler bei einfachen Klassen macht)
Und selbst C++ verstanden? Zur Datenkapselung gehören auch die
Zugriffsmethoden. Wie fummelst du die in eine struct? Wie schützt du die
Datenbereiche (private)?
C++ geht auch auf µC gut. Man muss es ja nicht mit einer dynamischen
Speicherverwaltung umsetzen.
Hier mal ein kleines Beispiel wie das geht:
http://ideone.com/fRXBnv
Die eine Klasse landet im Flash (dank constexpr), die andere im RAM, und
über die Referenz werden beide verbunden. Unten eine Beispiel wie man
davon ein paar Instanzen anlegt.
Mit dem GCC kann man das dann auch für C++11 zB für ARM oder AVR
kompilieren.
Jupp schrieb:> Weiterhin suche ich noch nach einer Art Tutorial wo erklärt wird wie der> Compiler bei der Platzierung der einzelnen Objekte vorgeht, d.h. was> kommt wohin, welche Sections werden benötigt und wofür sind die> einzelnen Memory Sections da.
Das funktioniert genau wie in C. lokale Instanzen auf dem Stack, globale
in .data und globale constexpr Instanzen in .text.
Roland Ertelt schrieb:> Mir drängt sich der Eindruck auf, dass C++ hauptsächlich für abstrakte> multithread-Anwendungen geeigneter ist.
- Was ist eine abstrakte Anwendung? Klingt wie ein schwarzer Schimmel.
- Was hat C++ mit Multithreading zu tun? Dass man PCs heute oft in C++
programmiert und aufgrund der aktuellen PC-Hardware tunlichst ausgiebig
Multithreading nutzen sollte hat reinweg nichts mit der Sprache zu tun.
Compiler schrieb:> Und selbst C++ verstanden? Zur Datenkapselung gehören auch die> Zugriffsmethoden. Wie fummelst du die in eine struct?
Wenn du unbedingt willst, als Funktionszeiger.
Ansonsten per Namenskonvention: Methoden fangen halt mit dem
Klassennamen an.
> Wie schützt du die> Datenbereiche (private)?
Split header files. Das public header file enthält nur "struct foo;"
oder eine verkürzte Struktur mit nur den public members, das private
dann alles.
Ist nicht so, dass es sowas nicht schon gegeben hätte. Schau dir Motif
an für ein Beispiel.
Compiler schrieb:>> "Klasse" (im Prinzip machst du hier nix anderes als manuell das zu tun,>> was der C++ Compiler bei einfachen Klassen macht)>> Und selbst C++ verstanden? Zur Datenkapselung gehören auch die> Zugriffsmethoden. Wie fummelst du die in eine struct? Wie schützt du die> Datenbereiche (private)?>> C++ geht auch auf µC gut. Man muss es ja nicht mit einer dynamischen> Speicherverwaltung umsetzen.
Nö, ich behaupte nicht C++ verstanden zu haben, das wäre gelogen. Ich
habe allerdings auch **einfache** Klassen geschrieben (keine Vererbung,
Access protection, keine vtable, keine Templateklasse, ...)
Danke für die vielen Beiträge!
Zur Datenkapselung in C:
Bei mir habe ich das so in der Art gelöst wie Jörg das beschrieben hat,
d.h. im Header ist nur ein algemeines struct definiert damit ich mir
einen Pointer darauf generieren kann. Die Funktionen im Header bekommen
dann also einen Pointer auf diese Struktur übergeben. Im C-File steht
dann die ganze Wahrheit, wie also die Struktur im Detail aussieht. Damit
ist alles was im Header ist public und die Implementierung im C-File
private. Typprüfungen sind damit auch noch schön möglich. Mein Beispiel
oben zeigt also eigentlich noch nicht ganz meine Umsetzung.
@Dr. Sommer: Danke für den Code-Schnipsel! Hast Du das so auf die
schnelle noch zusammengeklickert :-)? Geht das ganze auch ohne
constexpr? Ich glaube das mein Keil ARM Compiler noch kein C++11
unterstützt :-(. Mein Target ist der alt bekannte STM32.
@Stefanus: Dem RAM traue ich prinzipiell schon. Ich habe aber auch bei
EMV-Versuchen schon bei diversen Controllern unterschiedlicher
Hersteller die Erfahrung gemacht das sich im RAM bei gewissen Störungen
mal was verändern kann wenn man es nicht will :-(. Der andere Punkt der
mich ein wenig stört ist, dass Parameter, also nicht veränderliche
Daten, ins RAM kopiert werden wenn man sie auch schon im Flash vorliegen
hat. Hier fände ich es eleganter auf diese Verdopplung der Daten und des
erforderlichen Speichers zu verzichten.
@Marius: Mirroring ist klar eine Möglichkeit aber auch mit
entsprechender Laufzeit und RAM-Speicherverlust verbunden. Wenn's geht
wollte ich mir das ersparen. Die Norm fordert es im Moment auch noch
nicht :-).
Jupp schrieb:> @Dr. Sommer: Danke für den Code-Schnipsel! Hast Du das so auf die> schnelle noch zusammengeklickert :-)?
Jo klar
> Geht das ganze auch ohne> constexpr?
Mit Standard C++ nicht, denn C++ kennt, genau wie C, keinen "Flash". Da
musst du mal im Manual vom Keil nachschauen wie das geht (wird wohl für
C unc C++ gleich gehen), wie man die Instanz (Variable) in den Flash
packt. Beim GCC wärs wohl mit __attribute__((section(".text"))).
> Ich glaube das mein Keil ARM Compiler noch kein C++11> unterstützt :-(.
Ja, richtig. Den STM32 kann man auch wunderbar mit dem GCC
programmieren, siehe STM32: GCC und ARM GCC.
Jörg Wunsch schrieb:> Wenn du unbedingt willst, als Funktionszeiger.
Sach ich doch: gefummelt. ;-)
Jörg Wunsch schrieb:> Split header files. Das public header file enthält nur "struct foo;"> oder eine verkürzte Struktur mit nur den public members, das private> dann alles.
Wunderbar zu pflegen und völlig fehlerunanfällig. :-(((
Und wie vererbst du das Ganze (hier ohne Polymorphie)?
Jupp schrieb:> Ich glaube das mein Keil ARM Compiler noch kein C++11> unterstützt :-(. Mein Target ist der alt bekannte STM32.
Das ist doch ein prima Päarchen für C++.
Compiler schrieb:> Und wie vererbst du das Ganze (hier ohne Polymorphie)?
Wo ist das Problem? Das funktioniert grad so wie in C++, nur macht man
das zu Fuss, was in C++ der Compiler macht. Abgeleitete Klassen müssen
natürlich auf die Basisklassen voll zugreifen können.
Klar ist das Fummelei. Das macht man nur, wenn bessere Alternativen
nicht bestehen oder mangels Verbreitung nicht in Frage kommen.
A. K. schrieb:> nur macht man> das zu FussA. K. schrieb:> Klar ist das Fummelei. Das macht man nur, wenn bessere Alternativen> nicht bestehen oder mangels Verbreitung nicht in Frage kommen.
Danke, genau meine Meinung.
Aber er hat einen C++ Compiler auf 'nem STM32. Es also alles schön
vorbereitet (verbreitet)! Warum dann nicht nutzen???
Compiler schrieb:>> Ich glaube das mein Keil ARM Compiler noch kein C++11>> unterstützt :-(. Mein Target ist der alt bekannte STM32.>> Das ist doch ein prima Päarchen für C++.
An sich ist der Compiler gut und alles funktioniert auch wunderbar.
Soweit ich sehen kann ist auch die C++ Lib recht kompakt. Wenn ich aber
Dinge in C++11 umsetzen will, klappt das nicht. Ich muss also mal sehen
wie ich meine Anforderungen mit dem alten C++ Standard umsetzen kann.
Meine andere Alternative ist eine gänzlich andere Lösung zu finden.
Dr. Sommer schrieb:>> Ich glaube das mein Keil ARM Compiler noch kein C++11>> unterstützt :-(.> Ja, richtig. Den STM32 kann man auch wunderbar mit dem GCC> programmieren, siehe STM32: GCC und ARM GCC.
Ja, richtig. Bei privaten Projekten nutze ich den GCC auch. Beruflich
bin ich aber leider gebunden :-(.
Mein aktueller C Code ist auf jeden Fall nicht unbedingt optimal zu
warten. Wenn man einmal drin ist, ist alles klar aber wenn man von außen
draufschaut muss man erst mal überlegen. Von C++ verspreche ich mir also
wie oben schon geschrieben eine bessere Struktur und Wartbarkeit.
würd mich wundern wenn keil so weit hinten wäre.. schon mal bei den
language settings geschaut was man da einstellen kann? meiner erfahrung
nach ist da keil an misra angelehnt und die leben seit 2013 schon im
jahr 1999 ;)
könnt also sein, das du nur einen neueren dialekt auswählen musst.
73
@Hans: Hab gerade mal ins aktuelle online Manual gesehen:
The compiler compiles C++ as defined by the 2003 standard, excepting
wide streams and export templates:
ISO/IEC 14822:2003. The 2003 International Standard for C++.
Sieht nach dem alten Standard aus :-(
was du in C gemacht hast, hat für mich nicht wirklich was mit
Objektorientierung zu tun aber ich verstehe was du meinst :)
Imho gehört zur Objektorinetierung (unter anderem) sachen wie Vererbung
und Polymorphismus, die dann den Einsatz von C++ im vergleich zu C
rechtfertigen.
Wie andere schon geschrieben haben.
In C++ bräuchtest du einfach eine Klasse Motor mit den gleichen
Attributen wie in deinem struct und den methoden die du vorher als lose
funktionen hattest.
Ein weiteres schmankel könnten die sichtbarkeiten (private, protected,
public) werden.
hans schrieb:> würd mich wundern wenn keil so weit hinten wäre..
Selbst Microsoft kriegt das in seinem Compiler noch nicht hin. GCC &
Clang sind halt die besten :o)
Jupp schrieb:> Ich muss also mal sehen> wie ich meine Anforderungen mit dem alten C++ Standard umsetzen kann.
Musst halt im Keil Manual nachschauen wie man da Sachen in den Flash
packt; geht vermutlich genau wie in C.
benwilliam schrieb:> Imho gehört zur Objektorinetierung (unter anderem) sachen wie Vererbung> und Polymorphismus, die dann den Einsatz von C++ im vergleich zu C> rechtfertigen.
Polymorphismus ist zunächst einmal unabhängig von Objekt-Orientierung.
Du kannst C++ Programme ohne Objekte aber mit Polimorphismus schreiben.
Das sollten wir also nicht durchmischen. Die Polymorphie von Klassen in
C++ auf die du hier (vermutlich) Bezug nimmst, begründet sich
ausschließlich durch Vererbung.
Vererbung ist auch in der C-Version möglich. Du musst halt das was der
Compiler für dich übernimmt manuell erzeugen. D.h. ftable, vftable,
Strukturüberdeckung der pseudo-Klassen und die Übergabe der (Unter-)
Objektzeiger über deine Finger laufen lassen.
Das ist nur in sofern Friemelei, als dass es einfach Fleissarbeit ist,
die dir der C++ Compiler abnehmen würde, aber es ist kein
Alleinstellungsmerkmal gegenüber C.
Ich empfehle sowieso gerne sich mal sich das Kompilat einfacher C++
Programme anzusehen um mal zu sehen, wie diese Dinge umgesetzt werden,
und ein Gespür dafür zu bekommen warum etwas funktioniert oder nicht
funktioniert.
Hallo,
ich stehe ebenfalls davor, mit der C++ Programmierung von µCs zu
beginnen. Vor allem würde ich dafür Atmel Prozessoren und Atmel Studio
verwenden.
Könnte mir jemand einen Tipp für einen guten Start geben? Gibt es
Literatur, die sich mit C++ für µC-Programmierung beschäftigt (Bücher,
Anleitungen, Links, ...)? Ich habe massenweise Literatur für C gefunden
und vorher auch in C Mikrokontroller programmiert. Allerdings finde ich
keine passenden Informationen für µC-Programmierung in C++. Hat jemand
einen Vorschlag?
lG
Hey Leidensgenosse ;-)
ich habe mir das folgende mal beschafft:
Real-Time C++: Efficient Object-Oriented and Template Microcontroller
Programming
ISBN: 3642346871
Ist auf jeden Fall fokussiert auf µC. Die Beispiele darin sind für den
AVR.
Soweit ich auf den ersten Blick sehen kann ist das Buch aber nicht
direkt für Anfänger geschrieben. Ich werde mich daher noch ein wenig mit
"allgemeinem" C++ beschäftigen.
Aber zunächst werfe ich mal wie oben empfohlen einfach mal den GCC
Compiler an und spiel ein wenig rum. Mal sehen was da so alles hinter
den Kulissen aus Klassen und Objekten gemacht wird :-)
Hey super, danke für den Tip!
Also als kompletter Einsteiger würde ich mich nicht bezeichnen, hab in
C++ zwar noch nicht viel programmiert, aber doch ein bisschen.
Allerdings nicht auf µC Ebene.
Von C bringe ich einiges an Erfahrung mit, auch das Prinzip der
Obejktorientierung ist mir soweit klar.
Danke, ich werde mir deinen Buchvorschlag mal zulegen!
LG
Jupp schrieb:> Aus meiner Sicht schreit das ein wenig nach Klassen
Da schreit gar nichts, alles ist ganz still. Wenn man wollte, könnte man
das auch in purem Assembler-Code umsetzen. SCNR ;-)
Liege ich richtig in der Annahme, dass die C++-Programmierung mit Atmel
Studio bzw generell für µCs noch in der Anfangsphase ist? Man findet
massenweise Information und Unterlagen zur µC-C-Programmierung, aber
sehr wenig zu C++. Liegt das wirklich daran, dass C++ für die
µC-Programmierung noch nicht lange verwendet wird?
lG
Naja, C++ ist sehr problematisch auf kleineren Microcontrollern, da es
sehr leicht mit den Standard-Libraries und verschiedenen Sprachfeatures
zu aufgeblähtem Code führt. Das Problem gibt's ja bereits mit C und
einigen Library-Funktionen, z.B. printf. Mit C++ ist das allerdings eine
Zehnerpotenz schlimmer, und schon unscheinbare Konstrukte können zu
großem Ballast führen, insbesondere Templates.
Ich halte es für keine gute Idee, auf kleineren Controllern (wenig
Flash/RAM) C++ zu verwenden.
das problem ist, das c++ ganz früher nicht so richtig geeignet war weil
die compiler und die supportlibs noch nicht gut waren ...
der mythos hält sich aber noch immer, dass c++ zwingend langsameren und
größeren code macht als c... stimmt aber lange nicht... das selbe gilt
für die anfangszeit von c... da war die welt auch der meinung man wird
ewig asm am uC brauchen...
oder auch die libc.. heute normal, vor 10 jahren wurdest du angeprangert
für jeden sprintf...
die errungenschaften der pc-sw-entwicklung dringen nur langsam in die
industrie durch... in 10 jahren wird das ganz anders ausschaun...
wir leben gottseidank nicht mehr in einer zeit wo 64k flash das höchste
der gefühle war ;)
73
Werz schrieb:> Liege ich richtig in der Annahme, dass die C++-Programmierung mit Atmel> Studio bzw generell für µCs noch in der Anfangsphase ist?
Nein es ist nicht "neu". Auch C++ hat in der uC Welt seine Verbreitung.
Allerdings gibt es im Bereich des bare-metall, also dem Einsatz auf uC
ohne zugrundeliegendes OS recht wenig C++.
> Man findet> massenweise Information und Unterlagen zur µC-C-Programmierung, aber> sehr wenig zu C++. Liegt das wirklich daran, dass C++ für die> µC-Programmierung noch nicht lange verwendet wird?
Das liegt eher an anderen Beschränkungen für die man eine direktere
Kontrolle der verwendeten Resourcen haben will, so dass C++ seine
Vorteile nicht ausspielen kann. (Z.B. uC mit sehr kleinem RAM)
Also liege ich richtig in der Annahme, dass es zur Zeit noch in der
Anfangsphase ist und in Zukunft C++ für größere 32 Bit µCs durchaus
Verwendung finden wird.
Ich werde mir einmal das von Jupp angesprochene Buch aneignen, um mich
in die C++ Programmierung von µCs einzuarbeiten :)
decimad schrieb:> Kannst Du ein problematisches Beispieltemplate zeigen?
Da braucht man gar nichts konkretes zeigen. Wenn man ein Template
mehrmals mit verschiedenen Typen instanziert um schönen typsicheren Code
zu schreiben (ohne void-Pointer u.ä.), dann wird für jeden Typ eine
Klasse erstellt. Das führt sehr schnell zu Bloat, wenn man nicht
aufpasst.
Wenn man hunderte KB Flash zur Verfügung hat, ist das vielleicht egal,
aber auf einem kleinen MCU mit < 64 KB sieht es schon anders aus.
Letztendlich muss man, wenn man C++ auf kleineren MCUs verwenden will,
komplett auf die Standard-Library verzichten und auf diverse
Sprachfeatures (Klassen, Exceptions, Templates). Dann kann man aber
eigentlich gleich C verwenden.
greg schrieb:> decimad schrieb:>> Kannst Du ein problematisches Beispieltemplate zeigen?>> Da braucht man gar nichts konkretes zeigen. Wenn man ein Template> mehrmals mit verschiedenen Typen instanziert um schönen typsicheren Code> zu schreiben (ohne void-Pointer u.ä.), dann wird für jeden Typ eine> Klasse erstellt. Das führt sehr schnell zu Bloat, wenn man nicht> aufpasst.
Ja, und für äquivalenten C-Code muss man das "template" mehrfach
hinschreiben, was in der selben Code-Größe resultiert (und extrem
hässlichen Sourcecode). Wenn die Funktion des Codes an sich
Typ-Agnostisch ist, d.h. es wird nur mit void-Pointern rumhantiert, dann
wird bei guten™ C++ Compilern auch nur 1 Template instanziert (d.h.
wieder genau so gut wie der C Code); in anderen Compilern kann man sich
mit etwas rumgecaste abhelfen und das template nur als schönes typsicher
API verwenden.
> Letztendlich muss man, wenn man C++ auf kleineren MCUs verwenden will,> komplett auf die Standard-Library verzichten
Wieso sollte ich z.B. auf std::numeric_limits<size_t>::max() verzichten?
Ist viel schöner&sicherer als INT_MAX o.ä., braucht aber 0 bytes mehr
Speicher.
> und auf diverse> Sprachfeatures (Klassen, Exceptions, Templates). Dann kann man aber> eigentlich gleich C verwenden.
Wenn du auf Klassen verzichtest, solltest du aber auch auf structs in C
verzichten, die sind nämlich 100% genauso verschwenderisch. templates
hab ich oben schon erläutert. Bei Exceptions gebe ich dir sogar recht.
bullshit!
ob eine funktion von einem double oder einem uint8_t ausgehn soll kann
sie beim void* nicht schmecken...
wegen so miesen praktiken hab ich mich meine anti-misra haltung schon
derart oft verteidigen müssen, dass ich froh bin nur mehr indirekt
production-code zu schreiben...
anderer typ erfordert andere behandlung, wenn nicht, dann ist einer der
beiden typen unnötig!
ähnliches gilt für klassen... ich kenne das herumgecaste und
structerweitern... weil ist ja nur was hinten im struct dazugekommen, da
castet man dann einfach den v2-pointer auf v1 und der compiler meckert
nicht mehr... der nächste ändert dann in der bearbeitungsroutine
irgendwas belangloses und da ja sowas wie regression-testing auf
embedded unmöglich ist, kommt der bug erst im feld auf... alles schon
gehabt...
nimm casses und subclasses und solange du nix virtual definierst gibts
keinen einzigen unterschied im kompilat zu einer reinen c-struct und
funktions implementierung... dafür ists überschaubar und wenn
tatsächlich was nicht passt mäckert immerhin der compiler!
exceptions geb ich dir recht... aber das liegt vor allem an der natur
der sache... braucht man muss es auch sein... egal ob in c oder c++
für c++ sprechen dafür solche nettigkeiten:
http://www.cprogramming.com/c++11/c++11-compile-time-processing-with-constexpr.html
73
hans schrieb:> exceptions geb ich dir recht... aber das liegt vor allem an der natur> der sache... braucht man muss es auch sein... egal ob in c oder c++
Meines Wissens nach gibt es Exceptions in C überhaupt nur durch extra
nachgerüstete (Nicht-Standard) Bibliotheken; nativ gar nicht.
Dr. Sommer schrieb:> Wenn du auf Klassen verzichtest, solltest du aber auch auf structs in C> verzichten, die sind nämlich 100% genauso verschwenderisch.
Sorry, da hab ich mich vertan. Was ich eigentlich meinte ist, dass man
für den "new"-Operator die Standardlibrary braucht. Dynamische
Allokation von Objekten geht also nur wie in C mit malloc/free, man kann
C++-Klassen also nicht vollständig nutzen.
> templates> hab ich oben schon erläutert. Bei Exceptions gebe ich dir sogar recht.
Templates verführen einfach schnell zu einem Stil, der zwar schön
typsicher ist, aber auch zu Bloat führt. Gerade wenn man die STL nutzt.
Nicht mehr und nicht weniger. Mit C hat man solchen Bloat besser unter
Kontrolle.
Nee, ich meinte die Frage schon ernst: An welchen Stellen kann man in C
praktibel Code-Size sparen, an denen das mit templates (mit denen man
höchstwahrscheinlich den Trick "sicherer" machen kann) nicht geht?
@vorposter: Ich halte new jetzt eigentlich für kein so wichtiges Feature
in C++, vor allem mal auf statischen µC-Programmen, in denen es sowieso
nicht vorkommt (C++ != Java!). Ansonsten kann man sich das auch gerne
auf malloc/free umbiegen.
Das der Code bei C++ größer wird habe ich bei meinen ersten Versuchen
auch festgestellt. Ich muss aber glaube ich noch ein wenig suchen worans
wirklich liegt weil ich bisher nur mal alles zusammenkopiert und zum
compilieren gebracht habe. Genauer heißt das, dass ich mal ein C-Projekt
von mir komplett entrümpelt habe, d.h. es ist nur noch die main-Funktion
samt Endlosschleife, sowie der Startup Code und die System Init (Clock
init) des STM32 vorhanden. Die Codegröße liegt dann bei ca. 11k. Mal
sehen woran das liegt. Verwendet habe ich den CodeSourcery GCC Compiler.
@Werz: Bzgl. des Buchs kann ich dir keine Empfehlung geben da ich es
auch erst sehr frisch habe und noch nicht wirklich reinschauen konnte.
Ich will nur vermeiden dass du umsonst Geld ausgibst ;-)
Mark Brandis schrieb:> Meines Wissens nach gibt es Exceptions in C überhaupt nur durch extra> nachgerüstete (Nicht-Standard) Bibliotheken; nativ gar nicht.
Naja, longjmp/setjmp gibt's auch in C, und zumindest eine mögliche
Implementierung von Exceptions arbeitet genau auf diesem Wege.
Hallo Jupp,
ja ich hab mir das Inhaltsverzeichnis mal angeschaut. Ich denke, dass
ich mir das Buch trotzdem zulegen werde, weil es kompakt von Grund auf
das beschreibt, was ich lernen will, nämlich C++ für Mikrokontroller zu
programmieren.
Im Internet finde ich keine passende Information/Quelle, wo dies in der
Art bechrieben ist.
greg schrieb:> Sorry, da hab ich mich vertan. Was ich eigentlich meinte ist, dass man> für den "new"-Operator die Standardlibrary braucht.
Ja. Aber was hat das mit Klassen zu tun? Nix. Wie C-Structs kann man
Klassen nach Wahl auf den Stack, auf den Heap oder global anlegen.
> Dynamische> Allokation von Objekten geht also nur wie in C mit malloc/free, man kann> C++-Klassen also nicht vollständig nutzen.
C-Structs aber auch nicht, da man sie nicht malloc()en kann.
> Templates verführen einfach schnell zu einem Stil, der zwar schön> typsicher ist, aber auch zu Bloat führt.
Eine wilde Behauptung. Dank Compiler-Optimierung wird so viel Code
generiert wie das C-Äquivalent auch bräuchte.
> Gerade wenn man die STL nutzt.
Warum denken eigentlich immer alle dass die stdlib/STL nur aus
std::vector und std::string besteht? Da gibt es noch viel mehr nützliche
Dinge, wie zB std::numeric_limits<int>::max() - das ist ein Template,
ist aber super effizient.
> Nicht mehr und nicht weniger. Mit C hat man solchen Bloat besser unter> Kontrolle.
Ja, weil man sich die Finger wund schreibt alles zu copy&pastan.
Jupp schrieb:> Die Codegröße liegt dann bei ca. 11k. Mal> sehen woran das liegt. Verwendet habe ich den CodeSourcery GCC Compiler
Habe mir ein ähnliches Dummy-Projekt in C++ angelegt und komme mit dem
GCC-ARM-Embedded auf 328 Bytes, und das ist nur wegen der SystemInit so
viel.
Jörg Wunsch schrieb:> Naja, longjmp/setjmp gibt's auch in C, und zumindest eine mögliche> Implementierung von Exceptions arbeitet genau auf diesem Wege.
Ja gut. Aber will man das wirklich haben ;-)
1. Beim Einschalten ist das RAM leer, also werden die Daten vom Flash
ins RAM geladen. Danach wird immer auf das RAM zugegriffen.
Dies geht auch gar nicht anders da :
Der Code direkt aus dem Flash ausgeführt wird und die Daten im RAM
liegen müssen (Harvard).
Das heißt zur Laufzeit kann ein wilderder Pointer, Stack Überlauf usw.
jederzeit deine Motorparameter zerstören.
Denn der µC lies deine Struktur nicht neu aus dem Flash (Es sei denn du
Programmierst das so). Aber selbst wenn du das tust dann muß du Sie ins
RAM lesen, denn nur von dort aus kannst du Sie benutzen ! tada !
Uwe schrieb:> 1. Beim Einschalten ist das RAM leer, also werden die Daten vom> Flash> ins RAM geladen. Danach wird immer auf das RAM zugegriffen.> Dies geht auch gar nicht anders da :> Der Code direkt aus dem Flash ausgeführt wird und die Daten im RAM> liegen müssen (Harvard).> Das heißt zur Laufzeit kann ein wilderder Pointer, Stack Überlauf usw.> jederzeit deine Motorparameter zerstören.> Denn der µC lies deine Struktur nicht neu aus dem Flash (Es sei denn du> Programmierst das so). Aber selbst wenn du das tust dann muß du Sie ins> RAM lesen, denn nur von dort aus kannst du Sie benutzen ! tada !
Alles was du schreibst gilt nur für AVR o.ä.. Der OP benutzt aber STM32,
und der kann:
* Code aus dem RAM oder Flash ausführen.
* Daten aus dem Flash und RAM mit derselben Instruktion laden; die Daten
können also problemlos im Flash bleiben und der Code sie ohne besondere
Vorkehrungen direkt von da lesen ohne Umweg über den RAM. Nur schreiben
geht nicht ohne weiteres.
Uwe schrieb:> Beim Einschalten ist das RAM leer, also werden die Daten vom Flash> ins RAM geladen.
Jo, sehe ich auch so, das die Init-Werte der Variablen vom Startup Code
ins RAM kopiert werden müssen.
Uwe schrieb:> Das heißt zur Laufzeit kann ein wilderder Pointer, Stack Überlauf usw.> jederzeit deine Motorparameter zerstören.
Warum sollte das so sein? Bei den Motorparametern steht zur Compilezeit
fest das die Daten unveränderlich, also const sind. Damit wäre es auch
möglich sie nur im Flash des Controllers zu halten. Wenn die Parameter
im Code verwendet werden, werden sie zunächst in die entsprechenden
CPU-Register geladen und dort verarbeitet.