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:
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
classTask{
2
public:
3
Task(QueueHandle&qh):m_bar(0),m_queue(qh){}
4
voidthreadFunction(){
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
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