Hi,
ich probiere mich an C++ auf Mikrocontroller mit FreeRTOS und einem
dynamischen Nachrichtenhandling und ich möchte Speicherfragmentierung
verhindern
Header
message->~Message();// manually call destructor because of placment new!!!!
6
}
Prinzipiell funktioniert das. Schau ich mir im Debugger die Adresse von
memory_ und vom Message Objekt an so sind die gleich.
Was ich mich jetzt frage:
Müßte ich das ganze nicht noch Threadsafe machen? Ich habe bei Freertos
heap4.c eingebunden, und dort gibts auch ein pvMalloc was Threads
blockiert usw.
Wenn ich bei mir durchsteppe, scheine ich bei meinem new aber keine der
Freertos Funktionen aufzurufen. Ich frage mich jetzt also ob ich das new
noch überladen muss bzw. was genau passiert da überhaupt?
Und mein Memory Block ist jetzt fix auf 2kB Größe festgelegt. Wie kann
ich denn sicherstellen, das meine abgeleiteten MessageObjekte da
aufjedenfall reinpassen? Ich habe eine Abstrakte BasisMessage Klasse und
die process() Methode wird dann immer überladen.
Ist der Ansatz so überhaupt valide oder ist das totaler Schrott auf
einem Mikrocontroller?
Sofern du MessageFabric::createMessage nur von 1 Thread aus aufrufst
passt das schon. Placement New allokiert ja nicht wirklich Speicher,
sondern ruft im Wesentlichen einfach nur den Konstruktor auf.
A. B. schrieb:> Und mein Memory Block ist jetzt fix auf 2kB Größe festgelegt. Wie kann> ich denn sicherstellen, das meine abgeleiteten MessageObjekte da> aufjedenfall reinpassen?
Vielleicht wäre es cleverer einfach std::variant<Message1, Message2,
Message3> zu verwenden. Das hat dann genau die richtige Größe.
A. B. schrieb:> Ich habe bei Freertos> heap4.c eingebunden, und dort gibts auch ein pvMalloc was Threads> blockiert usw.
Ja, malloc() arbeitet auf einer Datenstruktur um Speicherblöcke zu
finden und zu reservieren, auf welche auch andere Threads zugreifen.
Placement new hingegen nutzt einfach den einen Block der da ist.
Ja, ich rufe das nur aus einem Thread auf.
Danke für die Erläuterungen.
std::variant würde sicher funktionieren und wäre Speicherschonender als
mein Ansatz. Muss man dann nur drauf achtgeben wenn man eine neue
Message anlegt, dass dort auch einzutragen
A. B. schrieb:> Muss man dann nur drauf achtgeben wenn man eine neue> Message anlegt, dass dort auch einzutragen
Ja genau das ist doch der Vorteil, dass so die Speichergröße immer
garantiert ist und man es nicht vergessen kann. Wenn du bei deiner
Variante eine neue größere Nachrichtenklasse implementierst aber die
Speichergröße nicht erhöhst, geht es schief.
Hab es mal probiert, leider ist variant c++17 und ich hänge bei 11.
Es gibt aber paar Implementierungen fpr 11...werde das mal probieren ob
ich damit zum Ziel komme
Ein paar Gedanken von mir:
* Fabric ist das englische Wort für Stoff, wahrscheinlich suchst Du
Factory ;-)
* Wenn der c'tor von MessageFabric privat ist, kann keine Instanz davon
angelegt werden, ergo braucht es auch keinen d'tor.
* Virtueller d'tor zeigt in der Regel an, dass die Klasse als Basis
dienen soll. Ergibt hier dann so keinen Sinn.
* So wie es jetzt aussieht, hast Du immer maximal eine Instanz einer
Message und rufst genau eine Funktion darauf auf. Wolltest Du mehr als
eine Message z.Z. haben, müsstest Du memory_ anders dimensionieren und
vor allem, auch anders verwalten. Dann würde es auch nicht mehr
ausreichen, den d'tor explizit aufzurufen, sondern Du müsstest es dann
der factory überlassen, das Objekt wieder abzuräumen.
* Wenn die Anforderungen mit dem, was Du uns bis jetzt gezeigt hast,
abgedeckt sind, würde es absolut ausreichen, unterschiedliche
`process()` Funktionen anhand des Event.types aufzurufen (ggf. diese
Funktion auch direkt in `Event` zu implementieren). Anhand des
Event.types temporär eine korrespondierende Message zu erzeugen, nur um
eine Funktion auf der Message aufzurufen, ist maximal verwirrend.
Torsten R. schrieb:> * Wenn der c'tor von MessageFabric privat ist, kann keine Instanz davon> angelegt werden, ergo braucht es auch keinen d'tor.
Das würde ich nicht so sehen. Es ist ein gängiges Pattern manche Klassen
nur per Factory(-Funktion) erstellen zu können, aber normal zerstören zu
können.
Torsten R. schrieb:> Wenn die Anforderungen mit dem, was Du uns bis jetzt gezeigt hast,> abgedeckt sind, würde es absolut ausreichen, unterschiedliche> `process()` Funktionen anhand des Event.types aufzurufen
Stimmt!
A. B. schrieb:> Hab es mal probiert, leider ist variant c++17 und ich hänge bei> 11.> Es gibt aber paar Implementierungen fpr 11...werde das mal probieren ob> ich damit zum Ziel komme
Eine union geht auch, std::variant ist im Endeffekt nichts anders, man
muss halt genau aufpassen wann man welche Instanz darin verwendet.
Danke für die Anmerkungen.
Mit der Fabric das ist ein wenig peinlich...
Eigentlich wollte ich weg von riesigen switch cases in denen alle
MessageIDs gehandelt&bearbeitet werden.
Jetzt hab ich immer noch ein großes switch case (etwas übersichtlicher
da dort nur die Message erzeugt wird) aber irgendwie ist das immer noch
Murks, viel Schreibarbeit und Boilerplate und der Aufwand mit Objekt
erzeugen, process() aufrufen und Objekt löschen ist auch nicht geil.
Also irgendwo werde ich immer einnen großen switch case haben um jede
Message zu erzeugen/zu handeln :/
A. B. schrieb:> Eigentlich wollte ich weg von riesigen switch cases in denen alle> MessageIDs gehandelt&bearbeitet werden.
Wenn in jedem "case" nur ein normaler Funktionsaufruf zu einer Funktion
für den jeweiligen Typ ist, passt das schon. Lange Funktionen möchte man
ja normalerweise deswegen vermeiden, weil sie schwer verständlich und
wartbar sind, aber ein langes switch-case ist ja nicht schwer zu
begreifen.
Alternativ kannst du auch std::map<int,MsgFun> o.ä. arbeiten, um von
Nachrichten-Typ auf einen Funktionsaufruf abzubilden. MsgFun wäre
einfach ein Funktionszeiger; alternativ ging auch eine Klasse mit einer
virtuellen Funktion oder std::function.
Torsten R. schrieb:> * Wenn der c'tor von MessageFabric privat ist, kann keine Instanz davon> angelegt werden, ergo braucht es auch keinen d'tor.
Ich würde die Klasse ganz rauswerfen. Für mich machen Klassen, von denen
es weder direkt noch über abgeleitete Klassen je eine Instanz gibt,
irgendwie wenig Sinn. Wenn es in der Klasse nur Dinge gibt, die nicht zu
einer Instanz gehören (also alles static ist), dann kann man das auch
ohne Klasse als freie Funktion definieren, ggf. in einem namespace.
> * Virtueller d'tor zeigt in der Regel an, dass die Klasse als Basis> dienen soll. Ergibt hier dann so keinen Sinn.
Und es führt normalerweise dazu, dass er Compiler dafür extra eine
vtable anlegen muss, die dann nie benutzt wird.
Niklas G. schrieb:> Torsten R. schrieb:>> * Wenn der c'tor von MessageFabric privat ist, kann keine Instanz davon>> angelegt werden, ergo braucht es auch keinen d'tor.>> Das würde ich nicht so sehen. Es ist ein gängiges Pattern manche Klassen> nur per Factory(-Funktion) erstellen zu können, aber normal zerstören zu> können.
Hier geht's aber nicht um das per Factory erzeugte Objekt, sondern um
die Factory selbst, die im Prinzip nur aus einer static-Memberfunktion
einer Klasse besteht. Eine Instanz dieser Factory-Klasse gibt es also
nicht.
Es gibt mit dem Code noch ein Problem:
1
staticuint8_tmemory_[2048];
Dieses Array muss nicht unbedingt ein passendes Alignment für ein
Message-Objekt haben.
Mir reichts :( Ich stell das ganze wieder um und verabschiede mich von
diesem ich kreiere mir mein Temporäres Message Object Ansatz.
Aber kennt jemand ein größeres Projekt für einen Mikrocontroller mit c++
was man sich mal auf github oder so ansehen kann umd mal zu sehen wie
sowas da umgesetzt ist? ich finde immer nur sehr abstrakte
Codeschnippsel für embedded und im Nebensatz wird dann gesagt: auf
Mikrocontrollern mit den ganzen Einschränkungen geht es so dann doch
nicht und man muss es anders lösen. Wie ist mir aber immer noch nicht
ganz klar, ausser ich programmiere quasi wieder C-Style :(
Es gibt mit dem Code noch ein Problem:
static uint8_t memory_[2048];
Würde __aligned() reichen oder was muss man da noch beachten?
Erstmal hatte es so funktioniert, aber evtl war das auch Glück
A. B. schrieb:> Aber kennt jemand ein größeres Projekt für einen Mikrocontroller mit c++> was man sich mal auf github oder so ansehen kann umd mal zu sehen wie> sowas da umgesetzt ist?https://github.com/TorstenRobitzki/bluetoe
Ist halt eine Library die mehr als einen Anwendungsfall abdecken muss
und damit etwas komplexer wirkt. Kommt ohne heap aus und resultiert in
sehr kleinen binaries.
Als ich damit vor langer Zeit angefangen hatte, war C++11 noch "neu".
Jetzt könnte es langsam mal einen update auf z.B. C++20 gebrauchen.
A. B. schrieb:> Es gibt mit dem Code noch ein Problem:> static uint8_t memory_[2048];> Würde __aligned() reichen oder was muss man da noch beachten?> Erstmal hatte es so funktioniert, aber evtl war das auch Glück