Hallo zusamen, ich kenne C und C# für mich zufriedenstellend und beschäftige mich gerade mit C++. Hier habe ich Fragen zu public static vor Variablen. Was funktioniert: Anmerkung, SerialNrFromCMDGetNetStart ist hier noch nicht static. Im h:------------------------------------------------------ class CSMAData { public: CSMAData(); virtual ~CSMAData(); uint32_t SerialNrFromCMDGetNetStart; }; Im cpp:---------------------------------------------------- CSMAData::CSMAData() { // TODO Auto-generated constructor stub } CSMAData::~CSMAData() { // TODO Auto-generated destructor stub } In z.B. main():----------------------------------------------- CSMAData oCSMAData; oCSMAData.SerialNrFromCMDGetNetStart = 12345; Das Obige funktioniert soweit ohne das der Compiler meckert. Nun will ich aber diese Variable "SerialNrFromCMDGetNetStart" statisch haben. Setze ich also ein static davor, dann meckert der Compiler. "...undefined reference to `CSMAData::SerialNrFromCMDGetNetStart'" Wenn ich allerdings im zugehörigen cpp file folgendes "definiere" muss es wohl heißen, dann gibt sich der Compiler zufrieden. Im h:------------------------------------------------------ class CSMAData { public: CSMAData(); virtual ~CSMAData(); static uint32_t SerialNrFromCMDGetNetStart; }; Im cpp:---------------------------------------------------- CSMAData::CSMAData() { // TODO Auto-generated constructor stub } CSMAData::~CSMAData() { // TODO Auto-generated destructor stub } uint32_t CSMAData::SerialNrFromCMDGetNetStart = 0; In main():----------------------------------------------- CSMAData oCSMAData; oCSMAData.SerialNrFromCMDGetNetStart = 12345; Das scheint so zu funktionieren. Finde es nur etwas umständlich. Diese Klasse soll eigentlich nur Daten für alle anderen Objekte bereitstellen. Ist hier diese aus meiner Sicht umständliche Handhabung mit einer Klasse eine gute Wahl oder gibt es andere Möglichkeiten?
fred schrieb: > Das scheint so zu funktionieren. Finde es nur etwas umständlich. So ist es aber korrekt. Speicher muss nunmal irgendwo angefordert weden. fred schrieb: > Ist hier diese aus meiner Sicht umständliche Handhabung mit einer Klasse > eine gute Wahl oder gibt es andere Möglichkeiten? Das kommt drauf an was du machen möchtest. statische Variablen in einer Klasse von verschiedenen Stellen im Code zu schreiben erscheint nicht sehr objektorientiert und ist im Endeffekt nur eine globale Variable, welche ja bekanntermaßen zu unübersichtlichem schlecht wartbarem Code führen. Die Frage ist also: Warum ist die Variable statisch, brauchst du sicher nur eine Instanz davon, kannst du den Zugriff kapseln?
fred schrieb: > Das Obige funktioniert soweit ohne das der Compiler meckert. > Nun will ich aber diese Variable "SerialNrFromCMDGetNetStart" statisch > haben. Setze ich also ein static davor, dann meckert der Compiler. > "...undefined reference to `CSMAData::SerialNrFromCMDGetNetStart'" Du hast die Variable zwar deklariert, aber nicht definiert. > Wenn ich allerdings im zugehörigen cpp file folgendes "definiere" muss > es wohl heißen, dann gibt sich der Compiler zufrieden. > uint32_t CSMAData::SerialNrFromCMDGetNetStart = 0; Ja, richtig. > Diese Klasse soll eigentlich nur Daten für alle anderen Objekte > bereitstellen. Was du damit sagen willst, erschießt sich mir nicht. Welche "anderen Objekte" meinst du? > Ist hier diese aus meiner Sicht umständliche Handhabung mit einer Klasse > eine gute Wahl oder gibt es andere Möglichkeiten? Falls du damit meinst, dass die Klasse gar nicht instanziiert werden soll, sondern nur statische Member hat, dann würde ich keine Klasse, sondern einen Namespace dafür verwenden. Die Aufteilung in Deklaration und Definition hast du damit aber trotzdem.
Habe etwas nachgelesen und glaube es soweit verstanden zu haben. Bei nicht statischen Variablen werden diese automatisch definiert, wenn ein Objekt angelegt wird. Bei statischen Variablen ist dies nicht der Fall. Deswegen diese umständliche Definition und und in meinem Fall die Initialisierung mit 0. Unabhängig davon mal im Ernst.... wer heute auf dem PC zu C++ greift wird sehr triftige und spezielle Gründe haben, sofern er sich nicht gerne Quält. Ich mache was privat auf einem µC und will das mit C++ und nicht C machen.
@Rolf Magnus Namespace ... verstehe ich und kenne ich auch von C#. Scheint aber etwas verpönt zu sein. Ginge das auch über struct? Vermutlich schon, doch da werde ich wohl auch das mit der Definition haben.
Alternativ geht auch: > In main():----------------------------------------------- > CSMAData::SerialNrFromCMDGetNetStart = 12345; Extra ein eigenes Objekt musst du dafür nicht anlegen. Außerdem ist die Schreibweise klarer dass das Ding an der Klasse klebt, nicht an einem speziellen Objekt.
fred schrieb: > Bei statischen Variablen ist dies nicht der > Fall. Deswegen diese umständliche Definition und und in meinem Fall die > Initialisierung mit 0. Diese "umständliche Definition" hat aber wenig mit C++ zu tun. Auch in C musst du Definition und Deklaration trennen (Stichwort: external). Insgesamt hat auch das wenig mit C selbst zu tun, sondern mit dem Linken. > Unabhängig davon mal im Ernst.... wer heute auf dem PC zu C++ greift > wird sehr triftige und spezielle Gründe haben, sofern er sich nicht > gerne Quält. Na wenn du meinst.
fred schrieb: > @Rolf Magnus > > Namespace ... verstehe ich und kenne ich auch von C#. Dort geht das aber meines Wissens so nicht. > Scheint aber etwas verpönt zu sein. Das wäre mir neu. > Ginge das auch über struct? Vermutlich schon, doch da werde ich wohl > auch das mit der Definition haben. struct und Klassen sind in C++ das gleiche, bis auf die default-Zugriffsrechte und die default-Vererbungsrechte (public bei struct, private bei Klassen). Aber eine struct, die nur statische Member hat, halte ich für genauso unsinnig, wie eine Klasse, bei der das so ist.
fred schrieb: > Bei nicht statischen Variablen werden diese automatisch definiert, wenn > ein Objekt angelegt wird. Bei statischen Variablen ist dies nicht der > Fall. Genau wie bei C#. Ich dachte das kannst du... fred schrieb: > wer heute auf dem PC zu C++ greift > wird sehr triftige und spezielle Gründe haben, sofern er sich nicht > gerne Quält. Auch auf dem PC bietet C++ hohe Performance bei starker Abstraktion. C++'s Möglichkeiten zur Meta-Programmierung haben nur wenige andere Sprachen (z.B. Lisp), weshalb es durchaus Grund gibt C++ zu verwenden. Mach dir doch mal eine ordentliche Klassenstruktur (wie in C#) und ob da wirklich statische Member rein müssen, oder ob das später nur zu Chaos führt. Ich halte mich meistens an den Grundsatz, dass man praktisch das ganze Programm 2x in einem Prozess ausführen können sollte, indem man nur in der main() die entsprechenden Objekte doppelt anlegt. Wenn das nicht geht, weil da globale/statische Member -Variablen reinpfuschen, ist die Struktur vermutlich schlecht. Auch wenn man das Programm wahrscheinlich nie wirklich so "verdoppeln" würde, erzwingt diese Vorgehensweise eine saubere Klassen-Struktur. Randfälle gibt es bei "fixen" Daten die sich nie ändern aber 1x geladen werden müssen, wie Grafiken o.ä.
Also ich habe mit freeRTOS vorerst mindestens zwei tasks am laufen. Der eine task soll permanent über eine serielle Schnittstelle mit einem Gerät kommunizieren und alle x Sekunden Daten sammeln. Der andere task soll auch auf diese gesammelten Daten zugreifen können und diese auf eine SD Karte speichern. Das mit den beiden tasks ist für mich gesetzt. Das will ich so machen. Also bitte keine Diskussion darüber. Klar, das könnte man auch alles ohne RTOS machen. Daher mein Gedanke zu statischen Variablen in einer Datenklasse. Auf diese kann aus beiden tasks zugegriffen werden. Frage.... wie würdet ihr das prinzipiell elegant machen? Danke und Gruß
fred schrieb: > Daher mein Gedanke zu statischen Variablen in einer Datenklasse. > Auf diese kann aus beiden tasks zugegriffen werden. Was spricht gegen normale nicht-statische Daten in einer "Datenklasse", von der du 1 Instanz in der main() anlegst, und Pointer darauf an beide Tasks übergibst? So ist das gleich viel sauberer. Du könntest auch die Thread-Funktionen der Tasks als Memberfunktionen der Klasse ausführen, dann ist die ganze Angelegenheit in einer Klasse verpackt... fred schrieb: > Das mit den beiden tasks ist für mich gesetzt. Das will ich so machen. > Also bitte keine Diskussion darüber. Ich diskutiere doch so gerne: Die Kommunikation mit dem Gerät in Interrupts mithilfe von FSM und DMA abwickeln, und in der main()-Loop auf die SD-Karte schreiben...
@Dr. Sommer Das ist auch eine Möglichkeit. Leider habe ich bei freeRtos Problemchen mit C++. Also, der freeRtos task funktion hatte ich zuerst eine "statische" Memberfunktion also die angedachte Threatfunktion einer Klasse übergeben. Hat soweit auch funktioniert. Nur alle anderen Memberfunktionen dieser Klasse konnte ich irgendwie nicht mehr aus dieser dem task übergebenen funktion aufrufen. Soviel zu verpacken der ganzen Angelegenheit in einer Klasse. Würde das schon gerne so machen aber geht leider nicht bei freeRtos. Habe das so gelöst, dass ich in einem cpp file eine nicht member funktion habe, welche ich der freeRtos taskfunktion übergebe. Diese Taskfunktion beinhaltet die endlosschleife for(;;). Vor dieser endlosschleife habe ich alle instanzen, die ich in diesem prozess benötige. Das klappt. Einen anderen Weg kenne ich leider nicht. Haste ne Idee wie das doch funktionieren könnte mit der Klassenmethode als Threadfunktion?
Für einen C++-Wrapper braucht man in irgend einer Form den this* auf die entsprechende Instanz der (Applikation-)Klasse. Man erzeugt dazu die Task mit einer statischen Methode der Klasse als TaskFunction und übergibt ihr den this* als pvParameters. Dann ruft man über den (gecasteten)this* die Member-Function-Run() der Klasse.
Fred schrieb: > Leider habe ich bei freeRtos Problemchen mit C++. > Also, der freeRtos task funktion hatte ich zuerst eine "statische" > Memberfunktion also die angedachte Threatfunktion einer Klasse > übergeben. Hat soweit auch funktioniert. > Nur alle anderen Memberfunktionen dieser Klasse konnte ich irgendwie > nicht mehr aus dieser dem task übergebenen funktion aufrufen. Wieso nicht? (Natürlich kannst Du von einer statischen Elementfunktion nur wieder statische Elementfunktionen aufrufen ...)
@carl drexler Hallo, An diese Variante hatte ich auch mal gedacht. Momentan übergebe ich über diesen pvParameters nur das QueueHandel. Spricht aber wohl nichts dagegen einen Zeiger auf eine Struktur (Objekt) zu übergeben. Diese Struktur enthält Den Zeiger auf die Klasseninstanz und das QueueHandel. In dieser angedeuteten Klasse habe ich dann vermutlich eine (statische?) Methode die meine endlos for(;;) Thread Schleife enthält. (Habe ich da nicht wieder das Problem das aus einer statischen Methode nur auf statische Methoden und Variablen zugegriffen werden kann? Demzufolge müsste alles in der Klasse statisch sein?) Werde ich demnächst mal ausprobieren ob das für mich die passendere Möglichkeit ist. Gibt es noch andere?
Fred schrieb: > Diese Struktur enthält Den Zeiger auf die Klasseninstanz und das > QueueHandel. Zu kompliziert... Fred schrieb: > In dieser angedeuteten Klasse habe ich dann vermutlich eine (statische?) > Methode die meine endlos for(;;) Thread Schleife enthält. Na eben nicht statisch. Also: Du machst dir eine Klasse, nennen wir sie "Task". Du übergibst ihr im Konstruktor das QueueHandle, die Klasse merkt sich die Referenz. Du verpasst der Klasse eine statische Member-Funktion, welche die Thread-Funktion sein wird. Als pvParameters übergibst du einen Pointer auf die "Task" Instanz, und die statische Funktion ruft die nicht-statische auf:
1 | class Task { |
2 | public:
|
3 | Task (QueueHandle& qh) : m_bar(0), m_queue (qh) {} |
4 | static void threadFunction (void* pvParameters) { |
5 | static_cast<Task*> (pvParameters)->threadFunction (); |
6 | }
|
7 | private:
|
8 | void threadFunction () { |
9 | // Hier die eigentliche Thread-Arbeit durchführen.
|
10 | // Auf m_queue kann zugegriffen werden.
|
11 | while (1) { |
12 | foo (); |
13 | }
|
14 | }
|
15 | // Hier beliebige weitere Funktionen & Daten für die interne Behandlung unterbringen
|
16 | void foo() { |
17 | ++m_bar; |
18 | }
|
19 | int m_bar; |
20 | |
21 | QueueHandle& m_queue; |
22 | };
|
23 | |
24 | Task myTask; |
25 | int main () { |
26 | // ...
|
27 | xTaskCreate (&Task::threadFunction, "asdf", 42, &myTask, 0, nullptr); |
28 | }
|
In der Klasse kannst du dann beliebige weitere Funktionen und interne Thread-Daten unterbringen, hier angedeutet durch m_bar und foo(). Ich habe hier die "myTask" Instanz mal global angelegt, s.d. sie keinen Stack-Speicher belegt, und somit die Member-Variablen (m_bar) ebenfalls nicht. Das kann man aber auch ändern, solange die Instanz nicht verschwindet während der Thread noch läuft. PS: Hätte FreeRTOS ein C++ kompatibles Threading-API (so wie das C++-eigene API std::thread), könne man das so machen (rein hypothetisch):
1 | class Task { |
2 | public:
|
3 | Task (QueueHandle& qh) : m_bar(0), m_queue (qh) {} |
4 | void threadFunction () { |
5 | // Hier die eigentliche Thread-Arbeit durchführen.
|
6 | // Auf m_queue kann zugegriffen werden.
|
7 | while (1) { |
8 | foo (); |
9 | }
|
10 | }
|
11 | private:
|
12 | // Hier beliebige weitere Funktionen & Daten für die interne Behandlung unterbringen
|
13 | void foo() { |
14 | ++m_bar; |
15 | }
|
16 | int m_bar; |
17 | |
18 | QueueHandle& m_queue; |
19 | };
|
20 | |
21 | Task myTask; |
22 | int main () { |
23 | // ...
|
24 | xTaskCreate (&Task::threadFunction, "asdf", 42, [&]() {myTask.threadFunction (); }, 0, nullptr); |
25 | |
26 | // Alternativ
|
27 | |
28 | xTaskCreate (&Task::threadFunction, "asdf", 42, std::bind(&Task::threadFunction, &myTask), 0, nullptr); |
29 | |
30 | }
|
Find ich schöner, aber geht halt mit so Gammel-C-Zeugs nicht ;-)
Oh, mir fällt doch eine etwas abgekürzte, funktionierende Variante ein:
1 | class Task { |
2 | public:
|
3 | Task (QueueHandle& qh) : m_bar(0), m_queue (qh) {} |
4 | void threadFunction () { |
5 | // Hier die eigentliche Thread-Arbeit durchführen.
|
6 | // Auf m_queue kann zugegriffen werden.
|
7 | while (1) { |
8 | foo (); |
9 | }
|
10 | }
|
11 | private:
|
12 | // Hier beliebige weitere Funktionen & Daten für die interne Behandlung unterbringen
|
13 | void foo() { |
14 | ++m_bar; |
15 | }
|
16 | int m_bar; |
17 | |
18 | QueueHandle& m_queue; |
19 | };
|
20 | |
21 | Task myTask; |
22 | int main () { |
23 | // ...
|
24 | xTaskCreate ([](void* p) {static_cast<Thread*> (p)->threadFunction (); }, "asdf", 42, &myTask, 0, nullptr); |
25 | }
|
Das funktioniert, weil Lambdas ohne Capture in Funktions-Pointer konvertiert werden können... Somit hält man das fiese C-API von FreeRTOS von der "Task" Klasse fern.
Dr. Sommer, Ich verneige mich. Danke für die tolle ausführliche Darstellung. Eine Frage noch, dann ist die Sache für mich klar. Die Klassendeklaration hat man doch eigentlich im Header und die Definition und Implementierung im *.cpp File. Ist das was du dargestellt hast im cpp oder im h file oder ein zusammengeschrubsel von beidem das nur dem Verständnis dienen soll? Die Initialisierungslisten sind doch im cpp file? Kannste bei Zeiten vielleicht noch eine Darstellung hier angeben, die in etwa zeigt, was richtigerweise im h und was im cpp file zu sein hat. Bisschen was verstehe ich schon von c++. Bin aber meilenweit von entfernt einen guten Stil zu haben, den ich mir aber aneignen wollte. Also, sei bitte so freundlich. Danke!
Fred schrieb: > Die Klassendeklaration hat man doch eigentlich im Header und die > Definition und Implementierung im *.cpp File. Ja ist richtig. Man kann das so machen wie gezeigt, dann sind alle Funktionen inline. Das ist leichter zu lesen zu Demonstrationszwecken aber verschwendet natürlich Programmspeicher; daher solltest du das umbauen wie gewohnt, mit Funktions Definitionen im Source File (inkl. Initialisierung). Ich kanns grad am Handy nicht machen, ist doch ne nette Übung für dich :P
Hi Dr. Sommer static_cast<Task*> (pvParameters)->threadFunction (); oder ((Task*)(pvParameters))->threadFunction (); Es geht ja beides sollte unteres vermieden werden? Warum?
fred schrieb: > Es geht ja beides sollte unteres vermieden werden? Warum? Ja in dem Fall ist beides das Gleiche. Die C-Cast-Variante kann viele verschiedene Arten von Casts durchführen je nachdem welche Typen vorliegen, und es passiert leicht dass etwas anderes durchgeführt wird als man eigentlich wollte. Mit static_cast macht man explizit klar was man will, und wenn das nicht zu den Typen passt gibt's eine Fehlermeldung anstatt dass unbemerkt etwas ungewolltes passiert. Siehe auch: https://stackoverflow.com/questions/1609163/what-is-the-difference-between-static-cast-and-c-style-casting
Beitrag #5070363 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.