Im speziellen stell ich mir die Frage ob ich über den Konstruktor den Zeiger auf das gerade angelegte Klassenobjekt übergeben kann. Anders gefragt wie kann ich am besten der Klasse die Adresse der gerade erstellten Instanz bekanntgeben? Zuerst die Instanz bilden und dann einer Methode den Zeiger auf die Instanz übergeben, kann ich mir schon vorstellen.
Karl schrieb: > Im speziellen stell ich mir die Frage ob ich über den Konstruktor den > Zeiger auf das gerade angelegte Klassenobjekt übergeben kann. Der Konstruktor der Klasse kennt doch die Adresse des Objekts. Warum musst du ihm die "übergeben"? > Anders gefragt wie kann ich am besten der Klasse die Adresse der gerade > erstellten Instanz bekanntgeben? Geht es dir darum, eine Liste alle Instanzen zu führen, oder was soll die Klasse mit einem Zeiger auf das Objekt machen? > Zuerst die Instanz bilden und dann einer Methode den Zeiger auf die > Instanz übergeben, kann ich mir schon vorstellen. Erkläre doch mal etwas genauer, was du vor hast.
Ich nutze FreeRtos welches in C geschrieben ist. Nun übergebe ich der Funktion xTaskCreate die "Thread" Methode der Klasse Ca, von der ich vor der xTaskCreate Funktion eine Instanz oCa erstellt habe. Da ich nun in dieser statischen Thread Methode keine weitere KlassenMethode von oCa aufrufen konnte, habe ich im Argument "pvParameters" von xTaskCreate eben die Adresse von oCa übergeben. Ein Typcast auf Ca und nun konnte ich aus der Thread Metode von oCa auf alle anderen Methoden von oCa zugreifen. Soweit so gut. Ob das sauber ist ...... ? Nun frage ich mich ob ich die Adresse der Instanz oCa über xTaskCreate pvParameters übergeben muss. Tippe gerade auf dem Handy..... hoffe es war verständlich, ansonsten kann ich später code beifügen.
Karl schrieb: > Da ich nun in dieser statischen Thread Methode keine weitere > KlassenMethode von oCa aufrufen konnte, habe ich im Argument > "pvParameters" von xTaskCreate eben die Adresse von oCa übergeben. Richtig. Und ja, man kann den Thread bereits im Constructor starten. Ich bevorzuge es, einen Constructor weitgehend Exception-frei zu halten und vollständig zu initialisieren bevor drauf zugegriffen wird: Erst wird eine Instanz der Klasse erzeugt (mit "leerem" Thread) und dann der Thread gestartet. Bspw.
1 | static Foo* Foo::make(...) |
2 | {
|
3 | auto foo = new Foo(...) ; |
4 | foo->thread = new Thread(start,(void*)foo) ; |
5 | return foo ; |
6 | }
|
Im echten Anwendungsfall würde ich Smart Pointer nutzen.
:
Bearbeitet durch User
Mikro, das ist für mich noch erklärungsbedürftig. Was ich verstehe: Eine statische Implementierung der Methode make() der Klasse Foo. In dieser Methode wird ein Objekt foo der Klasse FOO, in der wir uns gerade befinden, auf dem Heap erzeugt. Was bedeutet hier auto? Interpretiere ich das auto hier richtig? Foo foo = new Foo(...) ; äquivalent? Diese Zeile verstehe ich eigentlich nicht. foo->thread = new Thread(start,(void*)foo) ; was passiert hier genau? Danke!
Karl schrieb: > Was bedeutet hier auto? Das gibt es ab C++11. Der Compiler folgert durch den Ergebnistyp der Erstellung den Typ. Ist total unnötig hier, Du kannst auch einfach
1 | Foo * pF = new Foo(...); |
2 | pF->thread = new Thread(start,(void*)foo) ; |
3 | return pF; |
schreiben. Es wird ja sowieso in der nächsten Zeile ein Memberzugriff über einen Zeiger verwendet. Insgesamt ist das alles eh sehr komisch. Die üblichere Variante um Tasks anzulegen ist, dass diese run() und activate() Methoden bekommen. Wenn Du also ein "aktives Objekt" machen willst, dann sollte das nur eine Taskklasse sein und bei activate() wird dann
1 | BaseType_t xTaskCreate( TaskFunction_t pvTaskCode, ...) |
von freeRTOS aufgerufen, mit run() als Taskfunction. Dieser Matsch wo man um jeden Preis neue Sprachmittel einsetzt ist für mich nicht nachvollziehbar. Das auto ist für Lambdafunktionen und Vorgänge wo sich später der Rückgabewert ändert oder wo Initialisierlisten einfacher im Code dargestellt werden sollen (C++14).
Ich verstehe folgenden Zusammenhang noch nicht wirklich.
1 | Foo * pF = new Foo(...); |
2 | pF->thread = new Thread(start,(void*)pF) ; |
3 | return pF; |
Hier (unten) wird wohl auf dem Heap durch das new ein Objekt der Klasse Foo angelegt. pF enthält die Adresse auf dieses Objekt und der Pointer *pF zeigt darauf, wenn man das so interpretieren kann. Über pF-> werden public Methoden und Felder im Objekt pF der Klasse Foo erreicht.
1 | Foo * pF = new Foo(...); |
Was passiert hier unten? Foo hat eine Methode thread. Und es gibt eine Klasse Thread. Deren Konstruktor zwei Parameter übergeben bekommt. "start" und die Adresse von pF. Meine Vermutung: Wird hier ein Objekt der Klasse Thread auf dem Heap angelegt und der Zeiger pF->thread enthält durch "=" die Adresse der Instanz von Thread? Thread ist doch hier eine Klasse, oder?
1 | pF->thread = new Thread(start,(void*)pF) ; |
Karl schrieb: > Meine Vermutung: > Wird hier ein Objekt der Klasse Thread auf dem Heap angelegt und > der Zeiger pF->thread enthält durch "=" die Adresse der Instanz von > Thread? > Thread ist doch hier eine Klasse, oder?pF->thread = new > Thread(start,(void*)pF) ; Was die Klasse "Thread" ist hat Wie schon vorher gesagt: Wenn Du für Deinen Fall ein aktives Objekt erstellen willst, dann mach das mit den Methoden á la run() und activate() und nicht mit zusätzlichen Klassen. Vielleicht meint der mikro77 auch sowas in der Art von
1 | std::thread |
aus C++11 und das "start" eine globale run()-Funktion ist die das Objekt als Argument annimmt. Dann gibt er dem aufrufenden Code einen Zeiger auf das Objekt zurück und es gibt einen Member "thread" der dann den laufenden stl-Thread repräsentiert. Warum run() selbst keine Methode darin sein darf ist mir auch nicht klar. Ist aber auch egal, weil das hier Kaffeesatzleserei ist.
Chris F. Erstell doch einen etwas ausführlichen Weg, wie du das sauber machen würdest. Ich habe da noch so meine Anlaufschwierigkeit. Ich z.B. erstelle ein Objekt auf dem Heap. Dessen Konstruktor bekommt ein QueueHandel übergeben, wenn ich in der Klasse ein Queue habe. xTaskCreate bekommt dann noch in einem parameter die statische thread funktion übergeben und über einen weiteren Parameter übergebe ich die Adresse des Objektes, damit ich über einen static cast auf weitere methoden des Objektes zugreifen kann. Wegen C und C++. Nun suche ich eben nach einem Weg, dass möglicherweise optimaler zu lösen. Nur konnte ich dir mangels Kenntnisse nicht 100% folgen.
Hallo Karl, du kannst das einfach so machen, wie du selbst eingangs beschrieben hast. Das wäre in etwa:
1 | struct Thread |
2 | {
|
3 | Thread(void(*f)(void*),void *data) ; |
4 | } ; |
5 | struct Foo |
6 | {
|
7 | Thread thread ; |
8 | Foo() : thread(&Foo::start,(void*)this) {} |
9 | static void start(void *data) ; |
10 | } ; |
Thread ist hier eine Klasse die zum Ausführen asynchroner Funktionen dient. Ob das nun ein RTOS Task (handle) ist, ein Posix Thread (handle) oder was auch immer, typischerweise gibt es einen Einsprungspunkt (hier start) und mindestens einen Parameter (hier die Instanz der Klasse). Problematisch wird es wenn die start Funktion bereits auf die Instanz zugreift, bevor diese überhaupt vollständig initialisiert ist. Im obigen Beispiel kann das nicht passieren. In der Praxis hat man aber häufig mehr als einen Parameter, und es ist nicht immer einfach umzusetzen, dass der Thread-Aufruf erst am Ende der Initialisierungsliste ausgeführt wird. Komplizierter wird es zudem, wenn die Initialisierung nicht oder nicht vollständig über die Initialisierungsliste geschieht, und wenn mit Exceptions gerechnet werden muss. Ein Lösungsansatz wäre das von mir oben beschriebene Verfahren: Erzeuge die Instanz mit "leerem" Thread, und starte den Thread danach.
1 | struct Foo |
2 | {
|
3 | Thread* thread ; |
4 | Foo() : thread(nullptr) {} |
5 | static void start(void *data) ; |
6 | } ; |
7 | Foo* make() |
8 | {
|
9 | auto foo = new Foo() ; |
10 | foo->thread = new Thread(start,(void*)foo) ; |
11 | return foo ; |
12 | }
|
Jetzt wird die Instanz ohne Thread erzeugt, und der Thread im Anschluss gestartet. Das hat allerdings den Nachteil, dass der Benutzer immer mit zwei Zuständen von Foo rechnen muss (mit oder ohne gestarteten Thread). Also versteck man den Constructor und macht nur die make Funktion sichtbar.
1 | struct Foo |
2 | {
|
3 | static Foo* make() ; |
4 | private:
|
5 | Thread* thread ; |
6 | Foo() : thread(nullptr) {} |
7 | static void start(void *data) ; |
8 | } ; |
Die Klasse "Thread" dient hier lediglich als generischer Platzhalter. Diese kannst du durch den RTOS Task ersetzen. Karl schrieb: > Was bedeutet hier auto? http://en.cppreference.com/w/cpp/language/auto Karl schrieb: > Diese Zeile verstehe ich eigentlich nicht. > foo->thread = new Thread(start,(void*)foo) ; Typischerweise werden Threads mit einer Start-Funktion (start) und einem Zeiger auf Daten aufgerufen (hier die Instanz der Klasse Foo). Wie du das als RTOS Taks abbildest, musst du wissen. Auf Anhieb sehe ich die Parameter pvTaskCode und pvParameters.
Beitrag #5138094 wurde von einem Moderator gelöscht.
Beitrag #5140111 wurde von einem Moderator gelöscht.
Beitrag #5140120 wurde von einem Moderator gelöscht.
Beitrag #5140143 wurde von einem Moderator gelöscht.
Beitrag #5142607 wurde von einem Moderator gelöscht.
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.