Guten Abend,
ich schlage mich gerade mit der C++ Programmierung auf einem STM32F446
herum, das Problem ist aber insgesamt recht allgemein.
Nämlich habe ich eine Klasse, in der Objekte einer anderen Klasse
enthalten sind. Um deren Initialisierung geht es mir.
Beispielsweise mein Uart:
1
Uart.h
2
classUart
3
{
4
Uart(instanz);
5
6
private:
7
OutputPinTxPin;//Memberobjekte
8
InputPinRxPin;
9
}
InputPin und OutputPin sind Klassen, die ich erstellt habe, deren
Konstruktor konfiguriert unter anderem den Pin, und bekommt Pin und Port
übergeben.
Je nach dem, welche Instanz ich dem Uart-Konstruktor übergebe, soll der
entsprechende Pin konfiguriert werden.
Hier muss ich mich aber zuvor entscheiden, wie die Pins konfiguriert
werden sollen, was ich ja aus offensichtlichen Gründen nicht kann.
Was funktioniert, ist die Definition der beiden Pins als Referenz im
Header:
1
OutputPin*TxPin;
2
InputPin*RxPin;
Danach kann ich dann die Pins mittels "new" zur gewünschten Zeit
erstellen:
1
Uart.cpp
2
Uart::Uart(instanz)
3
{
4
switch(instanz)
5
{
6
caseUART1:
7
TxPin=newOutputPin(portA,Pin3);
8
RxPin=newInputPin(portA,Pin5);
9
break;
10
11
//usw
12
}
13
}
Soweit so gut, was habe ich mich gefreut!
Dummerweise bin ich dann bei der Lösung eines anderen Problems über
dieses Thema gestolpert:
Beitrag "STM32 C++ Memberfunktion in Interrupt"
Wo sich ein forumskollege vehement gegen new ausspricht...
Daher, ganz lange Rede aber kurze Frage:
Wie löse ich mein Problem denn ohne new?
Viele Grüße
Karsten
Karsten D. schrieb:> Guten Abend,>> ich schlage mich gerade mit der C++ Programmierung auf einem STM32F446> herum> Wie löse ich mein Problem denn ohne new?
In Assembler. Denn es geht auch ganz ohne die künstlichen Probleme einer
komplexen Sprach-Syntax.
Was gegen new spricht: es ist leicht falsch zu verwenden. Falsch soll
hier heißen, dass es immer wieder aufgerufen wird, während das Programm
läuft. Das führt dann dazu, dass der Speicher(bei new "heap" genannt)
irgendwann schlicht alle ist. Ein weiteres Problem ist, dass der
Speicher früher alle sein kann als gedacht. Das liegt an der
Fragmentierung.
Zu deinem Problem: Das könnte man mit Copy Initialisierung lösen, wenn
das für dich eine Option ist.
Richard schrieb:> In Assembler. Denn es geht auch ganz ohne die künstlichen Probleme einer> komplexen Sprach-Syntax.
Assembler? So ein neumodischer Schwachsinn. Wenn man direkt Binärcodes
schreibt braucht man nur zwei Tasten!
@ Karsten
Die Empfehlungen 'new' nicht zu verwenden muss man, denke ich, mit etwas
Salz geniessen.
Ich denke dabei an die Menschen, die seit über 20 Jahren malloc/free
(new/delete sind im wesentlichen die modernen Analogien) verwenden und
damit zurecht kommen.
Es ist zweifellos richtig, dass man in Programmen, die komplexe
Kontrollflüsse haben, mit new/delete sehr sorgfältig arbeiten muss. Auch
hat sich so Mancher das ein oder andere Mal Stunden bis Tage damit
herumgeschlagen, Speicherlecks zu finden. Nicht umsonst gibt es einige
Tools für diesen Fall.
Es ist aber, wie eben genau diese Vergangenheit zeigt, durchaus möglich
korrekte Programme mit new/delete zu schreiben.
Ich selbst verstehe die Empfehlung so, dass ich, um mir die Arbeit
leicht zu machen, mich auf Konstruktoren/Destruktoren in einem gewissen
Maß verlasse, aber auch immer damit rechne, dass was schiefgehen kann.
Murphy lebt!
Dennoch verwende ich new/delete immer dann, wenn es mir angebracht
erscheint.
Einzig ein Anfänger bzw. jemand der das noch nie verwendet hat, sollte
gewarnt sein, dass es Probleme geben kann und das die Suche nach dem
Fehler oft sehr mühsam ist.
Die Warnung ist jedenfalls nicht absolut zu sehen. Wenn es Dir richtig
erscheint und anders nur noch schwieriger wird, würde ich die Verwendung
durchaus für erwägenswert erachten.
A. G. schrieb:> Assembler? So ein neumodischer Schwachsinn. Wenn man direkt Binärcodes> schreibt braucht man nur zwei Tasten!
Damit ist aber die nötige Kenntnis der vorhandenen CPU-Instruktionen
nicht ersetzt. Zum einfach Merken/Programmieren- Können der 0/1er Wüste
gibts die wenigen entsprechenden Asm-Wörter. Zu notieren auf einem A4
Blatt statt in einem 1000 seitigen Wälzer. Clever, was?
Bezieht sich die Warnung vor new/delete nicht vor allem auf Fälle in
denen es nicht absehbar ist in welchem Umfang zur Laufzeit Speicher
allokiert wird, oder wo ständig verschieden große Objekte erzeugt und
gelöscht werden?
Richard schrieb:> Damit ist aber die nötige Kenntnis der vorhandenen CPU-Instruktionen> nicht ersetzt. Zum einfach Merken/Programmieren- Können der 0/1er Wüste> gibts die wenigen entsprechenden Asm-Wörter. Zu notieren auf einem A4> Blatt statt in einem 1000 seitigen Wälzer. Clever, was?
Ich denke, dass es interessant ist mal ein paar Sachen in Assembler
gemacht zuhaben um zu wissen was der Prozessor im Hintergrund eigentlich
macht. Aber heutzutage ist dieses Micro-Management doch in fast allen
Fällen weder notwendig noch sinnvoll. Aber so weit wollte ich garnicht
vom Thema abschweifen, zu dem ich nichtmal irgendwas sinnvolles
beigetragen habe (schäm).
Arduino Fanboy D. schrieb:> Uart test{OutputPin{1,1},InputPin{2,2}}; // Instanziierung
Damit müssten man das Wissen darüber, welcher Uart welchen Pin benötigt
von außen mitbringen. Nicht sehr elegant.
nfet schrieb:> Damit müssten man das Wissen darüber, welcher Uart welchen Pin benötigt> von außen mitbringen.
Constructor Injection
Ein übliches Verfahren...
Offensichtlich soll die Information irgendwie in die Klasse rein....
Alternativen wird es geben.
Der Fakt: Injection muss sein!
Egal wie.
Und new ist unerwünscht.
So geht es ohne.
nfet schrieb:> Nicht sehr elegant.
Fest verdrahtet in der Klasse ist doch auch doof.
A. G. schrieb:> Bezieht sich die Warnung vor new/delete nicht vor allem auf Fälle in> denen es nicht absehbar ist in welchem Umfang zur Laufzeit Speicher> allokiert wird, oder wo ständig verschieden große Objekte erzeugt und> gelöscht werden?>> [...]
Wenn ich auf diese Frage eingehen darf:
Es kommt darauf an, was genau man als "nicht absehbar" betrachtet und
was das für Fälle sind.
Programme an sich werden im Grunde mit dem Ziel geschrieben, dass sich
ein deterministischer Ablauf ergibt. Wenn das erreicht und durch Tests
geprüft wird, dann existieren solche Fälle nicht. Also ist auch absehbar
in welchem Umfang in welcher Situation Speicher alloziiert wird.
Andere Fälle sind demzufolge Programme, die nicht deterministisch
ablaufen. Oder solche, in denen man leider nicht jeden möglichen Fall
berücksichtigt hat; was man beim Test feststellen kann.
Durchaus ein übliches Verfahren, aber wenn man das Interface von Uart
beibehalten will, wofür es ja gute Gründe gibt eben eher ungeeinget.
Denn wenn ich jetzt "Uart uart(UART1);" schreibe oder "Uart
uart(OutputPin{1,1},InputPin{2,2})" dann würde ich doch das erste
definitv bevorzugen.
Man kann sich natürlich auch irgendwelche Builder drum herum schreiben,
um das zu verhindern, aber wann da dann der default constructor
aufgerufen wird und wann nicht, da bin ich mir nie ganz sicher..
nfet schrieb:> Was gegen new spricht: es ist leicht falsch zu verwenden.
Ehrlich gesagt verstehe ich nicht, warum das immer wieder geschrieben
wird, als ob es der heilige Gral wäre. Sind denn alle Programmierer zu
blöd, new und delete paarweise Einzusetzen? Kann ich mir nicht
vorstellen.
Messer sind auch leicht falsch zu verwenden, dennoch nimmt das kein
normaler Mensch zum Anlass, auf die Benutzung von Messern zu verzichten.
Wahrscheinlich ist das eher so ein psychologisches Ding: Was alle sagen,
muss gut sein. Maggie macht die einzig guten Miracolis, nur Coca Cola
ist richtige Cola und Pointer sind böse. Schön wär's, doch so einfach
wird man nicht ein guter Fachmann.
Wer mit Pointern nicht zurecht kommt oder sich vor ihnen fürchtet, der
hat sicher auch weitere gravierende Schwächen in der
Software-Entwicklung.
Theor schrieb:> Es ist aber, wie eben genau diese Vergangenheit zeigt, durchaus möglich> korrekte Programme mit new/delete zu schreiben.
Eben. Und es ist genau so möglich, ohne new/delete fehlerhafte Programme
zu schreiben. Sogar mit dem gefühlt zwanzigsten verbesserten Garbage
Collector (siehe Java).
Richard schrieb:> Zu notieren auf einem A4> Blatt statt in einem 1000 seitigen Wälzer. Clever, was?
Wenn du zu jedem ASM Befehl aufschreibst, was er bewirkt, dann kommst du
mit einem A4 Blatt aber nicht weit. Die Doku des Befehlssatzes vom
SAB80535 umfasste zum Beispiel schon weit über 100 Seiten.
Rolf M. schrieb:> Ich würde irgendwas so grob in der Richtung machen:
Und warum möchte man mehr als einmal ein UART-Objekt mit derselben
Instanznummer(?) erzeugen können?
Eigentlich ist das Universalrezelt, wenn mna nicht weiter kommt, einen
weiterel Level Indirection einzufügen.
In dem Fall eine Klasse/Funktion, die eine Instanz als Patameter nimmt,
und damit die passenden Pins setzt.
Oliver
Wilhelm M. schrieb:> Und warum möchte man mehr als einmal ein UART-Objekt mit derselben> Instanznummer(?) erzeugen können?
Das musst du den TE fragen. Ich hab nur die Schnittstelle so umgesetzt,
wie er sie vorgegeben hat. Wie lautet denn dein Gegenvorschlag?
Rolf M. schrieb:> Wie lautet denn dein Gegenvorschlag?
Die Instanz-ID (Nummer, Typ, whatever, ...) als template-parameter und
voll-statische Klasse, d.h. konkreter Typ nicht-instanziierbar. Denn
HW-Ressourcen sind in einem µC immer nur in endlicher, zur compile-zeit
bekannter Anzahl vorhanden. Eine Laufzeit-Instanziierung für
HW-Abstraktionen ist daher falsch. Daher nur conmpile-zeit
Instanziierung (template Instanziierung).
Stefan ⛄ F. schrieb:> Ehrlich gesagt verstehe ich nicht, warum das immer wieder geschrieben> wird, als ob es der heilige Gral wäre. Sind denn alle Programmierer zu> blöd, new und delete paarweise Einzusetzen? Kann ich mir nicht> vorstellen.
Wahrscheinlich weil ihnen jahrelang gepredigt wurde, dass dynamische
Speicherverwaltung auf einem µC "böse" ist.
Ich setze seit einiger Zeit ohne Bedenken sogar "malloc" ohne "free" auf
einem µC ein. (Gerade so eine USART ist ein tolles beispiel dafür, die
Klasse/das Modul kann man beim Start des Programms erzeugen und so lange
der µC läuft macht es (normalerweise) keinen Sinn das ganze wieder zu
löschen, da die Hardware ja so bleibt wie sie ist.)
Eine Library, die genau so etwas zur Verfügung stellt, findet sich hier:
https://github.com/KonstantinChizhov/Mcucpp
*******************************************
mit Updates c++17 03-2020
../mcucpp/ARM/Stm32F40x/usart.h
****************************
Hallo zusammen,
erst einmal vielen Dank für die vielen Antworten!
Das mit den Speicherlecks und new hatte ich zuvor schon aufgeschnappt,
wie einige auch schon richtig bemerkt haben, ist das bei einem Objekt
wie dem UART und den Pins, die nur einmal erzeugt und dann nicht weiter
verändert werden glaube ich kein größeres Problem?
Ich hatte nur andererseits gelesen, dass der Compiler dann weniger
Möglichkeiten zu optimieren hat und dass die dynamische
Speicherverwaltung weitere Ressourcen verbraucht. Ist das nicht korrekt?
Die folgenden Themen werde ich mir auf jeden Fall mal ansehen:
- Copy initialisierung (Danke an nfet)
- Constructor Injection (Arduino Fanboy D.)
Der Ansatz von Rolf M. ist auch spannend, danke dafür!
Wilhelm M. schrieb:> Rolf M. schrieb:>> Wie lautet denn dein Gegenvorschlag?>> Die Instanz-ID (Nummer, Typ, whatever, ...) als template-parameter und> voll-statische Klasse, d.h. konkreter Typ nicht-instanziierbar. Denn> HW-Ressourcen sind in einem µC immer nur in endlicher, zur compile-zeit> bekannter Anzahl vorhanden. Eine Laufzeit-Instanziierung für> HW-Abstraktionen ist daher falsch. Daher nur conmpile-zeit> Instanziierung (template Instanziierung).
Grundsätzlich erscheint mir das hier richtig, die ganze
Hardware-Initialisierung verändert sich nicht mehr und der Compiler kann
das gerne für mich alles fertig machen.
Jetzt muss ich nur noch herausfinden, wie das konkret geht...
Das von A. B. (Danke!) verlinkte Codebeispiel scheint auch in diese
Richtung zu gehen, das muss ich mir aber nochmal in Ruhe ansehen.
Ich danke auch allen, die ich jetzt nicht namentlich aufgeführt habe für
die konstruktive Diskussion und freue mich natürlich, wenn jemandem noch
weitere Aspekte oder Ansätze einfallen!
Viele Grüße
Karsten
Karsten D. schrieb:> Ich hatte nur andererseits gelesen, dass der Compiler dann weniger> Möglichkeiten zu optimieren hat und dass die dynamische> Speicherverwaltung weitere Ressourcen verbraucht. Ist das nicht korrekt?
Ja das ist Korrekt. ABer solange du beim Start nur eine Hand voll
Objekte erzeugst und die dann auch immer im Speicher bleiben, ist der
Aufwand sehr gering.
der F446 hat ja 128 kB RAM, da wird man doch nicht um jedes Byte kämpfen
müssen und kann etwas in Komfort investieren.
Ein uart1 mit fixer Belegung eines Pins spiegelt nicht die richtige
Hardware wieder, es gibt bei den uart auch alternative Pins. Oder gar
eine Switchmatrix wo die Funktion auf fast jeden Pin gelegt werden kann
(allerdings nicht bei den F4). Von daher ist eine Initialisierung mit
uart(PA_9, PA_10) gar nicht so verkehrt. Bei Mbed ist das auch so,
intern gibt es dann MCU Abhängig Pinmaps die nach gültigen Kombinationen
durchsucht werden.
Karsten D. schrieb:>>> Wie lautet denn dein Gegenvorschlag?>>>> Die Instanz-ID (Nummer, Typ, whatever, ...) als template-parameter und>> voll-statische Klasse, d.h. konkreter Typ nicht-instanziierbar. Denn>> HW-Ressourcen sind in einem µC immer nur in endlicher, zur compile-zeit>> bekannter Anzahl vorhanden. Eine Laufzeit-Instanziierung für>> HW-Abstraktionen ist daher falsch. Daher nur conmpile-zeit>> Instanziierung (template Instanziierung).>> Grundsätzlich erscheint mir das hier richtig, die ganze> Hardware-Initialisierung verändert sich nicht mehr und der Compiler kann> das gerne für mich alles fertig machen.> Jetzt muss ich nur noch herausfinden, wie das konkret geht...> Das von A. B. (Danke!) verlinkte Codebeispiel scheint auch in diese> Richtung zu gehen, das muss ich mir aber nochmal in Ruhe ansehen.
Ich denke man sollte sich vorher im Klaren sein, was man eigentlich
will/braucht.
Unter Umständen will man die HW ja zur Laufzeit recht dynamisch
konfigurieren können, da kanns schon sein, dass ein Pin entweder als
UART-RX genutzt wird oder als GPIO.
Wenns z.B. Funktionen geben soll, die als Parameter eines von mehreren
UART-Objekten verwenden können sollen, wird man mit der statischen
template-Version wahrscheinlich auch nicht glücklich werden.
Wenn aber alles halbwegs fix ist, was vermutlich bei der überwiegenden
Mehrzahl an µC-Anwendungen so ist, kann man mit den template-Klassen
Laufzeitoverhead verringern, schon im Code falsche Verwendung verhindern
etc..., eventuell aber auf Kosten der Programmgröße und "debugability".
Ich hab das in etwa so gelöst:
1
namespace sowieso {
2
template <>
3
struct USART<3> {
4
struct Pins {
5
using PB0 = AlternateFunctionPin<1, 0, 7>;
6
using PB12 = AlternateFunctionPin<1, 12, 7>;
7
// etc...
8
};
9
10
using RX_Pins = std::tuple<Pins::PB11, Pins::PC11, Pins::PC5, Pins::PD9>;
11
using TX_Pins = // etc...
12
};
13
14
}
15
16
template <int n>
17
struct USARTParams
18
: public sowieso::USART<n>, // ...
19
{
20
//...
21
};
22
23
template <int n,
24
typename Params = USARTParams<n> >
25
struct USART
26
: public Params
27
{
28
static void init() {...}
29
static void deinit() {...}
30
static void set_baudrate(uint32_t baudrate) {...}
31
// etc
32
};
Über die Params bekommt das USART-Template auch noch generierte
Register-Definitionen und diverse andere Parameter wie verwendbare
DMA-Kanäle, IRQ, Clock etc...
verwendet wird das dann so
Johannes S. schrieb:> der F446 hat ja 128 kB RAM, da wird man doch nicht um jedes Byte kämpfen> müssen und kann etwas in Komfort investieren.
Darum geht es doch gar nicht.
Johannes S. schrieb:> Oder gar> eine Switchmatrix wo die Funktion auf fast jeden Pin gelegt werden kann> (allerdings nicht bei den F4).
Das kann man alles wunderbar zur Compilezeit(!) machen. Und auch
abhängig vom MCU-Type und seinen Eigenschaften.
rµ schrieb:> Wenns z.B. Funktionen geben soll, die als Parameter eines von mehreren> UART-Objekten verwenden können sollen, wird man mit der statischen> template-Version wahrscheinlich auch nicht glücklich werden.
Warum nicht?
rµ schrieb:> Wenn aber alles halbwegs fix ist, was vermutlich bei der überwiegenden> Mehrzahl an µC-Anwendungen so ist, kann man mit den template-Klassen> Laufzeitoverhead verringern, schon im Code falsche Verwendung verhindern> etc..., eventuell aber auf Kosten der Programmgröße und "debugability".
Man ist bestrebt Code zu haben, der, wenn er semantisch falsch ist,
nicht compiliert. Und das erreicht man nur, indem man wie oben schon
gesagt, möglichst viel zur Compilezeit macht.
rµ schrieb:> Ich hab das in etwa so gelöst:
Ja, genau! Das geht in die richtige Richtung. Ich weiß nicht, wie oft
ich das hier schon in den letzten Jahren geschrieben habe, aber Du bist
wohl der erste, der das richtig verstanden hat. Glückwunsch!!! Freut
mich ;-)
Wilhelm M. schrieb:> rµ schrieb:>> Wenns z.B. Funktionen geben soll, die als Parameter eines von mehreren>> UART-Objekten verwenden können sollen, wird man mit der statischen>> template-Version wahrscheinlich auch nicht glücklich werden.>> Warum nicht?
Eine Instanz kann ich als Parameter übergeben, eventuell auch eine
Referenz/Pointer davon machen und mir merken. Mit der statischen
template-Variante ginge das nur über gemeinsame Basisklasse und einem
Wald an virtuellen Funktionen oder "statischem Polymorphismus" den man
sich selber stricken muss, aber das ist eine andere Dose Würmer ;-)
Wenn man zur Laufzeit nichts umkonfigurieren muss dann soll man zur
Compilezeit erledigen was geht, total d'accord.
Wilhelm M. schrieb:> Ja, genau! Das geht in die richtige Richtung. Ich weiß nicht, wie oft> ich das hier schon in den letzten Jahren geschrieben habe, aber Du bist> wohl der erste, der das richtig verstanden hat. Glückwunsch!!! Freut> mich ;-)
Ich verwende bei meinem Arbeitgeber eine STM32L4 Bibliothek die sehr
ähnlich aussieht. Für mich persönlich ist der größte Pluspunkt, dass man
Hardware-Abhängigkeiten (insbesondere zu Interrupt-Vektoren und
DMA-Kanälen) sehr gut darstellen kann. Das erlaubt mir bei verschiedenen
Produkten je nach Pin-Belegung verschiedene Schnittstellen zu benutzen
ohne das ich mir Gedanken darüber machen muss welcher DMA Kanal da jetzt
wofür gut ist.
Ich lege dazu für den Prozessor eine Art Device-Tree an der alle
Peripherien enthält. Das kann man sich etwa so vorstellen, hier mit
einer I2C Schnittstelle als Beispiel:
Diesen Typen fütter ich dann in eine I2C Klasse, die entsprechende
(statische) Methoden zum senden/lesen/etc. hat.
Möchte ich zwischen zwei I2C Schnittstellen umschalten so genügt ein:
1
usingEepromI2c=I2c<I2c1_t>;
2
// using EepromI2c = I2c<I2c2_t>;
rµ schrieb:> Eine Instanz kann ich als Parameter übergeben, eventuell auch eine> Referenz/Pointer davon machen und mir merken. Mit der statischen> template-Variante ginge das nur über gemeinsame Basisklasse und einem> Wald an virtuellen Funktionen oder "statischem Polymorphismus" den man> sich selber stricken muss, aber das ist eine andere Dose Würmer ;-)
Du kannst statt einer Referenz oder einem Pointer aber auch einfach
einen Typen übergeben:
1
template<typenameT>
Egal ob bei Klassen oder Funktionen.
Zugegeben, dass der ganze Code dann in Headern lebt ist bei Problemen
die man wirklich nur an der Hardware debuggen kann manchmal schon
mühsam. Aber das ist ein Trade-Off mit dem ich Leben kann.
Vincent H. schrieb:> Du kannst statt einer Referenz oder einem Pointer aber auch einfach> einen Typen übergeben:template<typename T>>> Egal ob bei Klassen oder Funktionen.
Das ist schon klar, wenn ich aber eine Factory habe, die Abhängig von
einer Laufzeit-Konfigurations-Datei einen UART1 oder UART2 macht und ich
die dann wohin übergeben will nützt das nichts.
Wow, vielen Dank!
@rµ: Jetzt hast du mir was zu knabbern gegeben, das wird ein bisschen
dauern, bis ich durch die Syntax und die Funktionsweise der Templates
gestiegen bin...
Aber zum Glück gibt es ja google :)
Hat es eigentlich einen Grund, dass ihr so viel mit structs und selten
mit Klassen arbeitet?
Viele Grüße
Karsten
Karsten D. schrieb:> Hat es eigentlich einen Grund, dass ihr so viel mit structs und selten> mit Klassen arbeitet?
Gibt leichte Unterschiede bei den Sichtbarkeitsregeln, ist aber sonst
egal.
Also verwendet man das, wo es weniger zu tippen gibt.
Faulheit.
Alternatives Argument:
Keine Methoden, dann struct.
Karsten D. schrieb:> @rµ: Jetzt hast du mir was zu knabbern gegeben, das wird ein bisschen> dauern, bis ich durch die Syntax und die Funktionsweise der Templates> gestiegen bin...
Das geschieht Euch ganz recht.
Je komplizierter die Sprachbürokratie desto besser!
Der Vernunftbegabte steuert seine UART direkt an und gut ist. Natürlich
mit der einfachsten MCU die für den Zweck reicht. Aber wenn man halt
nach unötigen Komplexitäten dürstet nur zu! Verliert Euch im Absurden!
Immerhin lässt sich dann hier trefflich streiten- wie man ja bei solchen
abstrakten Wolkenkuckuckssprachen immer wieder sieht.
Asm'ler schrieb:> Verliert Euch im Absurden!
Es gibt 3 Wege:
Der Goldene
Problem analysieren, Lösung erarbeiten, Lösung durchsetzen
Der Silberne
Genau so machen, wie die anderen, abschauen.
Der Bronzene
Aus Erfahrung lernen.
Alle anderen Wege führen ins Versagen.
Stefan ⛄ F. schrieb:> Ehrlich gesagt verstehe ich nicht, warum das immer wieder geschrieben> wird, als ob es der heilige Gral wäre. Sind denn alle Programmierer zu> blöd, new und delete paarweise Einzusetzen? Kann ich mir nicht> vorstellen.
Ja, wenn der Kontrollfluss einfach ist, dann kann man das Paarweise
schreiben.
Mit Exceptions kann es jedoch sein, dass man unterwegs aussteigt, ohne
am delete vorbeizukommen.
Mit unqiue_ptr ist sichergestellt, dass beim Verlassen des Scopes
zerstört wird. Egal ob durch Exception oder ein Return, was sich
irgendwo eingeschlichen hat. Auch wenn man den Pointer doppelt zuweisen
sollte entsteht kein Speicherleck.
Karsten D. schrieb:> Hier muss ich mich aber zuvor entscheiden, wie die Pins konfiguriert> werden sollen, was ich ja aus offensichtlichen Gründen nicht kann.
Du könntest auch zwei static Methoden in der Klasse anlegen, die dir den
Pin für Tx und RX zurückgeben. In der Initialisierung rufst du die
Methode dann mit den Parametern auf.
Arduino Fanboy D. schrieb:> Es gibt 3 Wege:>> Der Goldene> Problem analysieren, Lösung erarbeiten, Lösung durchsetzen
Merkt Ihr nicht, wie Ihr hier nur Zeit für die Probleme und das
Verständnis einer Sprachbürokratie verschleudert statt das eigentliche
Problem anzugehen? Schaut doch bitte mal statt in holden Elfenbeinturm
Hoch-und Höchstsprach- Lehrbücher lieber ins Datenblatt Eurer Controller
und findet dort die beste Lösung! Die dürfte (womöglich in Gestalt
bereits vorhandener Hardware-Features) wesentlich eleganter und
effizienter sein! Das und nur das ist der richtige Weg!
Asm'ler schrieb:> Merkt Ihr nicht, wie Ihr hier nur Zeit für die Probleme und das> Verständnis einer Sprachbürokratie verschleudert statt das eigentliche> Problem anzugehen?
Hat c-hater sich umbenannt?
Ach, Priester, die ihre eigenen Beschränkungen als alleinig selig
machende Heilslehre verkaufen wollen, gibts genug.
Immerhin ist das Asm'ler Kerlchen freundlich dabei.
Das schafft auch nicht jeder Priester.
Asm'ler schrieb:> Merkt Ihr nicht, wie Ihr hier nur Zeit für die Probleme und das> Verständnis einer Sprachbürokratie verschleudert statt das eigentliche> Problem anzugehen? Schaut doch bitte mal statt in holden Elfenbeinturm> Hoch-und Höchstsprach- Lehrbücher lieber ins Datenblatt Eurer Controller> und findet dort die beste Lösung!
Nicht alles, was man auf so einem µC machen will hat direkt mit der
Hardware zu tun. Signalverabeitung, Filter, etc... Sowas will man auch
nicht in Assembler programmieren.
Gerade im Bereich von DSP bietet sich C++ an, da man Algorithmen recht
allgemein als templates formulieren kann, und dann mit konkreten
Datentypen je nach Anwendung (double, float, fixkomma, integer)
verwenden kann ohne immer alles neu zu schreiben.
In Assembler würde man da immer bei 0 wieder anfangen. Abgesehen davon
ist Assembler auf den ARMs auch nicht mehr so einfach und übersichtlich
wie zu Z80-Zeiten. Schon alleine einen Überblick über die
Registerinhalte zu behalten stell ich mir schwierig vor. Zwangsweise
muss man sich da selber einschränken, was erst recht wieder auf
suboptimalen Code und C- oder Pascal-artige Aufrufkonventionen
hinausläuft.
Heutzutage darf man nicht vernachlässigen wie effizient ein Programm
erstellt werden kann, wie wartbar das ist was herauskommt. Assembler ist
da eher nicht bei den besten Kandidaten.
Arduino Fanboy D. schrieb:> Ach, Priester, die ihre eigenen Beschränkungen als alleinig selig> machende Heilslehre verkaufen wollen, gibts genug.
Die Beschränkung besteht im sinnlosen Verbraten von Zeit für
Kunstprobleme, die mit der Sache nichts mehr zu tun haben.
rµ schrieb:> Sowas will man auch> nicht in Assembler programmieren.
Davon war auch nie die Rede.
> Gerade im Bereich von DSP bietet sich C++ an, da man Algorithmen recht> allgemein als templates formulieren kann, und dann mit konkreten> Datentypen je nach Anwendung (double, float, fixkomma, integer)> verwenden kann ohne immer alles neu zu schreiben.
Man wird immer diese oder jene Begründung finden können warum nun
dieses oder jenes Sprachkonstrukt genau hier und dort hilfreich wäre.
Das ist ja gerade der Grund der solche Sprachen immer weiter aufbläht.
Der Betriebsblinde sieht aber das Ganze nicht mehr: Zunehmende
Abstraktheit und Komplexität fordern ihren Tribut. Sie verlagern Zeit
und Mühe nur an andere Stellen. Letztlich und nüchtern betrachtet in
immer mehr intellektuellen Overhead den die eigentlichen Anwendung gar
nicht braucht. Den immer weniger Leute noch lernen, durchschauen und
noch viel weniger perfekt beherrschen! Das ist auch das Gegenteil von
wartbar und fehlerminimierend. Keep it simple wäre eigentlich auch hier
das Motto, aber das ist Leuten, die nach Komplexität und Intransparenz
(in Abgrenzung von anderen?) lechzen natürlich schwer vermittelbar...
Ich werden gegen wissen, wie die, die C++ gewerblich nutzen und damit
sofort 61508/26262/50128 oder sogar 178C erfüllen müssen, das alles
zertifizieren und hauptsächlich testen werden. Und dazu kommt ein
Compiler mit Pre- Qualification, nicht Rede über Prozesse wie (A)SPICE
usw. Es ist nicht böse gemeint, ich werde es gerne wissen.
Max schrieb:> Ich werden gegen wissen, wie die, die C++ gewerblich nutzen und damit> sofort 61508 ...
Wie sonst auch? Wo soll der Unterschied sein? Für so lustige Dinge wie
misra gibt's natürlich auch c++ Äquivalente und auch zertifizierte
compiler gibt es natürlich.
Inwiefern das irgendeine Sicherheit erhöht...
Ich für meinen Teil würde auf jeden Fall eher einer von einem aktuellen
GCC kompilierten Software vertrauen, als einen zertifizierten Compiler
mit einem GCC von vor 10 Jahren, der via proven in use einen TÜV Stempel
hat.
Und lesbarer Code ist mir auch lieber als misra konformer.
nfet schrieb:> Ich für meinen Teil würde auf jeden Fall eher einer von einem aktuellen> GCC kompilierten Software vertrauen, als einen zertifizierten Compiler> mit einem GCC von vor 10 Jahren, der via proven in use einen TÜV Stempel> hat.> Und lesbarer Code ist mir auch lieber als misra konformer.
Aber wenn die Vorgabe nun mal ist, dass der Compiler zertifiziert sein
muss und der Code MISRAten, dann muss man damit leben, auch wenn's einem
nicht lieb ist.
Guten Abend,
da ich diesen Thread ins Leben gerufen habe, möchte ich mich noch
einmal kurz zu Wort melden:
Ich habe inzwischen ein wenig mit der Templateprogrammierung
herumgespielt, aber mich auch mit den anderen Ansätzen befasst. Es wird
vermutlich noch etwas dauern, bis ich meinen eigenen Weg gefunden habe,
allerdings hat mir allein das Aufzeigen der verschiedenen Möglichkeiten
sehr weitergeholfen.
Denn wie soll man über etwas nachdenken, von dem man nichts weiß?
Ich bedanke mich bei allen, die sich beteiligt haben für die wertvollen
Hinweise und wünsche euch noch einen schönen Abend und frohe Ostern
Viele Grüße
Karsten
PS: Ich weiß, dass dieser Beitrag inhaltlich nicht besonders gehaltvoll
war, aber komplett kommentarlos wollte ich mich aber auch nicht
verdrücken ;)