Datum:
Hi, also ich habe ne Idee aber bei der Umsetzung bin ich mir net ganz sicher ob es da nicht bessere gibt? Ich würde gern einen Array haben, der Daten verschiedener Datentypen (int,float..) halten kann. Evtl. wird es mi dem Code deutlicher was ich will:
typedef struct { int8_t a[3]; uint16_t b[3]; float c[3]; } ComplexDataType; |
// Template beispiel
template <class DATA_1,uint8_t size_1, class DATA_2,uint8_t size_2 , class DATA_3, uint8_t size_3>
class ComplexDataType
{
DATA_1 a[size_1];
DATA_2 b[size_2];
DATA_3 c[size_3];
};
|
Das ist noch nicht ganz meine Lösung, da ich die Größen der Arrays varrieren will und zudem es sein kann dass es stat float mal uint32_t sein soll. Ich dachte an sowas wie Template-Klassen (s. Beispiel). Das ganze soll letzendlich auf einem Controller laufen. Gibt es alternativen für mein Problem oder bedenken für die Template lösung? Gruß GoldenEyes
Datum:
Was ist überhaupt das Problem? Wie sollen wir wissen ob die Lösung richtig oder auch nur verbesserungswürdig ist, wenn wir garnicht wissen wofür das eine Lösung sein soll?
Datum:
Stell Dir vor, jemand kommt zu Dir, hält Dir eine Rohrzange und einen Maulschlüssel entgegen und wünscht von Dir zu wissen, was er verwenden soll. Da würdest auch sagen: Kommt darauf an, wofür!
Datum:
Srry. Also ich habe Daten von Sensoren (SensorData) und Steuerdaten (ControlData), die aus verschiedenen Datentypen bestehen sollen damit ich diese an die Regelung wie folgt übergeben kann:
+ // in Control.h ... void control(ComplexDataType ControlData, ComplexDataType SensorData) { .. if( XY) controlXY(ControlData.DATA_1, SensorData.DATA_1); else if( ZZ) controlZZ(ControlData.DATA_2, SensorData.DATA_2); .. } ... // in main Control::Control(ControlData,SensorData); |
Je nach dem welche Regelung (hier mit XY und ZZ bez.) gewünscht ist werden die entsprechenden Daten übergeben! Hoffe jetzt etwas klarheit verschafft zu haben!
Datum:
Hm. Nun haben wir noch mehr Teile der Lösung. Aber immer noch keine Angaben zum Problem. Ich habe aber auch keine Lust nochmal nachzubohren. Klare und verständliche Darstellung von Problemen ist Teil der Kunst und kein überflüssiges Übel. Um Dich nicht ganz ohne was zu lassen werfe ich mal das Stichwort "unions" in den Raum. Pass auf das es Dich nicht beisst. :-) Viel Erfolg.
Datum:
Ja, also das Problem habe ich schon im ersten Beitrag geschildert! Ich habe mich für die Templatevariante entschieden, dennoch Danke ich dir für deine Bemühungen. Unions sind mir nicht fremd, aber bringen tuht mir das nicht(zum. wüsste ich nicht wie). Gruß GoldenEyes
Datum:
Die Lösung für dein Problem lautet höchst wahrscheinlich: Basisklasse +
entsprechende Ableitungen dafür.
In den meisten Fällen, in denen du in C++ versucht bist zu schreiben
if( Typ der Daten ist nada )
mach was
else if( Typ der Daten ist juhu )
mach was
else if( Typ der Daten ist schnutzelbrutz )
mach aber sowas von ganz was anderem
lautet die C++ Lösung: Polymorphismus mit virtuellen Funktionen.
Datum:
template <class DATA_1,uint8_t size_1, class DATA_2,uint8_t size_2 , class DATA_3, uint8_t size_3>
class ComplexDataType
{ public:
DATA_1 a[size_1];
DATA_2 b[size_2];
DATA_3 c[size_3];
};
// egal wo ich den Complexen daten type benötige
ComplexDataType <int, 3, int, 3, int ,3> TEST1;
ComplexDataType <int, 3, float, 2, float ,3> TEST2;
ComplexDataType <unsigned int, 2, float, 1, unsigned char ,3> TEST3;
|
Um nochmal zu meinem Template zurück zukommen; Also so könnte ich mir jede beliebige (fast) Kombination von Daten erstellen oder übersehe ich etwas??? Mir geht es nur um den DatenType, die Funktion sind bei mir als Template realisiert, die jeweils die grundtypen unterstüzen! Gruß
Datum:
Karl Heinz Buchegger schrieb: > lautet die C++ Lösung: Polymorphismus mit virtuellen Funktionen. Dies hätte den nachteil dass ich für jede Kombination (sind zwar nicht so viele) eine funktion erstellen müsste (obgleich durch vererbung),Template ist da ehr das richtige.
Datum:
goldeneyes1987 schrieb: > Mir geht es nur um den DatenType, die Funktion sind bei mir als Template > realisiert, die jeweils die grundtypen unterstüzen! > Gruß Also das ganze könnte sogar so aussehen:
void winkel_regler (ComplexDataType* a); void abstands_regler (ComplexDataType* a); |
Jetzt könnten die Funktionen jeden beliebigen Datentype übergeben bekommen; Intern nutzt die Funktion nur das von TEST1 was sie benötigt! Beispiel:
ComplexDataType* <int, 3, int, 3, int ,1> TEST1; ... /* in TEST1 werden jetzt winkelpositionen(3Achsen), winkelgeschwindigkeiten(3Achsen) und abstand zu einem gegenstand gespeichert. */ ... winkel_regler(TEST1); abstands_regler(TEST1); .. // jeder Regler weiss worauf er zugreifen muss. // so kann ich alles Zustandsinformation zentral in einer Instanz speichern! |
Datum:
goldeneyes1987 schrieb: > Ich würde gern einen Array haben, der Daten verschiedener Datentypen > (int,float..) halten kann. > > Evtl. wird es mi dem Code deutlicher was ich will: > typedef struct > { > int8_t a[3]; > uint16_t b[3]; > float c[3]; > } ComplexDataType; nun ja, was du hast ist eben kein Array, das verschiedene Datentypen halten kann, sondern schlicht eine Struktur voller Arrays. Läuft irgendwie darauf hinaus, dass du eine "allwissende" Struktur erschaffen willst, von denen die jeweiligen Regler oder was du da baust, ein Stückchen auswerten. Mein erster Gedanke war in Richtung echter heterogener Arrays, wie sie jede moderne Skriptsprache kennt. Zum Beispiel in Python s = filter(lambda x: type(x)==str, ["1",1.0,1]) oder s = ["1",1.0,1].reject {|x| x.class != String} in Ruby In C gibt es das so nicht. Was auch gut ist. Nachbauen lässt sich das dennoch irgendwie über einen Container aus void* Zeigern. std::vector<void*> vielleicht. Oder vector von smart pointern. So elegant wie bei Skriptsprachen wird es ganz sicher nicht ;)
Datum:
Daniel schrieb: > Nachbauen lässt sich das dennoch irgendwie über einen Container > aus void* Zeigern. std::vector<void*> vielleicht. Oder vector von > smart pointern. So elegant wie bei Skriptsprachen wird es ganz sicher > nicht ;) Ja genau, und in C++ kann man void* mit hilfe von Templates ersetzen.
Datum:
goldeneyes1987 schrieb: > Ja genau, und in C++ kann man void* mit hilfe von Templates ersetzen die "Magie" der templates liegt eher in compile time Modifizierungen des Codes, um diesen die Schnittstellen typsicher zu machen. void* kann hingegen zu runtime zu allen erdenklichen Mittel greifen. Wie auch immer. Ich habe nochmal deine Postings gelesen und glaube die sauberste Lösung wäre die vom Karl Heinz. Sauber im Sinne möglichst wenig Querabhängigkeiten. Wenn alles in der grossen Datenstruktur steckt, weiß dein ReglerX was er benötigt, du wirst aber wahrscheinlich in einem Jahr dieses Wissen nicht mehr haben ;) Auch grosse switch/case respektive if/else Kaskaden müssen zentral gepflegt werden, wobei in meinen Augen das immer noch besser ist, als allen Reglern alle Daten zu geben. Bei gemeinsamen Abstammung können andere Regler ins System untergejübelt werden, wenn die Schnittstelle aus virtuellen Funktionen besteht.
Datum:
goldeneyes1987 schrieb: > goldeneyes1987 schrieb: >> Mir geht es nur um den DatenType, die Funktion sind bei mir als Template >> realisiert, die jeweils die grundtypen unterstüzen! >> Gruß > > Also das ganze könnte sogar so aussehen: >
> > void winkel_regler (ComplexDataType* a); > void abstands_regler (ComplexDataType* a); > |
> > Jetzt könnten die Funktionen jeden beliebigen Datentype übergeben > bekommen; > Intern nutzt die Funktion nur das von TEST1 was sie benötigt! Na, dann mach mal. Wenn du es hinbekommen hast, zeig mal. Das würde ich gerne sehen, wie du untempletierte Funktionen schreibst, die ein nicht weiter ausgeführtes Template als Argument nehmen und da auch noch sauber drauf zugreifen.
Datum:
> winkel_regler(TEST1); > abstands_regler(TEST1); > .. > // jeder Regler weiss worauf er zugreifen muss. > // so kann ich alles Zustandsinformation zentral in einer Instanz > speichern! Hier liegt schon der erste Denkfehler in einer OOP Welt. Es gibt keine Funktion winkel_regler Wohl aber gibt es ein Regler Objekt, instanziiert von einer Regler Klasse, welche selber weiß, welche Daten (+Datentypen) es benötigt und daher die entsprechenden Member besitzt. Wenn du OOP ernst nimmst, dann gibt es keine Funktionen mehr. Es gibt nur noch Objekte und diese Objekte * kümmern sich um sich selbst d.h. das Objektt selbst hält die Daten, die es zur Arbeit benötigt * verstehen Befehle die Befehle sind die Memberfunktionen. Solange du nicht von der Sichtweise abrückst, dass es da einen Satz Daten gibt und du schreibst Funktionen, die irgendwie diese Daten manipulieren, bist du noch nicht in OOP angekommen. Das ist kein Vorwurf. Gerade Leute, die mit funktionalen Programmiersprachen groß geworden sind, tun sich da schwer, die Denkweise umzustellen. Ich hab fast 3 Jahre gebraucht, bis ich aufgehört habe 'C mit Klassen' zu programmieren und den Umstieg nach OOP komplett und mit allen Konsequenzen vollzogen habe.
Datum:
Karl Heinz Buchegger schrieb: > Wenn du es hinbekommen hast, zeig mal. Das würde ich gerne sehen, wie du > untempletierte Funktionen schreibst, die ein nicht weiter ausgeführtes > Template als Argument nehmen und da auch noch sauber drauf zugreifen. Du hast natrülich recht, das geht so nicht! Aber so:
emplate <class DATATYPE_1, uint8_t size_1, class DATATYPE_2, uint8_t size_2, class DATATYPE_3, uint8_t size_3>
class ComplexData
{
public:
DATATYPE_1 angle[size_1];
DATATYPE_2 angle_velocity[size_2];
DATATYPE_3 position[size_3];
};
class Controller
{ template <class DATATYPE_1, uint8_t size_1, class DATATYPE_2, uint8_t size_2, class DATATYPE_3, uint8_t size_3>
void angle_control(ComplexData <DATATYPE_1,size_1,DATATYPE_2,size_2,DATATYPE_3,size_3> *c);
template <class DATATYPE_1, uint8_t size_1, class DATATYPE_2, uint8_t size_2, class DATATYPE_3, uint8_t size_3>
void position_control(ComplexData <DATATYPE_1,size_1,DATATYPE_2,size_2,DATATYPE_3,size_3> *c);
};
template <class DATATYPE_1, uint8_t size_1, class DATATYPE_2, uint8_t size_2, class DATATYPE_3, uint8_t size_3>
void Controller::angle_control(ComplexData <DATATYPE_1,size_1,DATATYPE_2,size_2,DATATYPE_3,size_3> *c)
{
// integration
c->angle[0] += c->angle_velocity*0.02;
.....
}
|
Zudem stimme ich auch dem zweiten Beitrag zu. Ich sehe auch das die Lösung wie ich sie hier oben beschrieben habe, bei einem wachsenden Datentype (angle, angle_velocity, position, accelertion, trans_velocity, ...) einen risen "Rattenschwanz" mit sich bringen würde! Ich habe momentan aber keine bessere oder überhaupt ne Lösung die letzendlich das löst!?!
Datum:
goldeneyes1987 schrieb: > // integration > c->angle[0] += c->angle_velocity*0.02; > ..... muss natrülich so aussehen
c->angle[0] += c->angle_velocity[0]*0.02; |
Datum:
Dessen benutzung würde wie folgt funktionieren:
// main.cpp ComplexData <int, 2, int, 2,float, 3> *TEST; Controller myTestController; myTestController.angle_control(TEST); // angle_control() mus als public dekl. sein |
Oder hat das ganze einen knackpunkt den ich übersehe???
Datum:
goldeneyes1987 schrieb: > Ich habe momentan aber keine bessere oder überhaupt ne Lösung die > letzendlich das löst!?! Was ist das Problem genau, welches du lösen willst. (Also nicht, welche Lösung schwebt dir vor und welche Probleme siehst du bei dieser5 Lösung, sondern aus größerer Entfernung: Was ist dein Problem?)
Datum:
Als die Software besteht aus mehreren Modulen, mindestens : Sensormodul Steuerungsmodul Zustandsmodul Regelungsmodul Das Sensormodul liefert mir einen Vektor mit Informationen, dessen Größe und Datentype variieren kann. Das Zustandsmodul nimmt sich diesen Vektor und errechnet daraus Zustandsgrößen, dessen Größe und Datentype nicht gleich sind. Das Regelungsmodul bekommt einen Vektor vom Steuerungsmoudl sowie die Zustandsgrößen. Eine "Schnittstellen"-Klasse CotrolRequest erhält alle Daten und reicht sie an die jeweiligen speziischen Regler weiter. Bis jetzt hatt ich das so geregelt:
myControlRequest.control(inputs, states, WINKELREGLER); |
also ich muss immer als benutzer sagen welchen Regler ich gerade ansprechen will da der Zustandsvektor (states) einem bestimmen datentype hat, was ich aber nicht will. Ich weiss nicht ob dies das Erklärt was ich versuche zu übermitteln? Aber ich hoffe es!
Datum:
Also bin zu dem schluß gekommen eine etwas pragmatischere Lösung anzustreben:
// Prinzipiell gibt es 4 Verschiedene Komplexe Datenstrukturen class Inputs{ public: uint16_t rc_command[5]; // vorerst nur rc_command, wird sich aber ändern! }; class Sensor{ public: int16_t angle_rate[3]; int16_t accelerration[3]; // vorerst ..... }; class States{ public: float angle[3]; int16_t angle_rate[3]; int16_t accelerration[3]; // vorerst ..... }; class Output{ public: uint16_t output[5]; // vorerst ..... }; class MyController{ public: void angle_control(Inputs* a, Sensor* b, States* c); void position_control(Inputs* a, Sensor* b, States* c); }; void MyController::angle_control(Inputs* a, Sensor* b, States* c) { b->angle[0] += b->angle_velocity[0]*0.02; } |
Jetzt können sich die Datentypen (Inputs,Sensor und co.) beliebig ändern und ohne Einfluß auf andere Member-Funktionen. Da z.B die Funktion angle_velocity(..) immer nur auf "angle" und "angle_velocity" zugreift. Hat jemand dennoch einen Einwand warum dies so nicht gemacht werden solte (im Kontext mit µC oder anderer Natur) ?? Gruß GoldenEyes
Datum:
Karl Heinz Buchegger schrieb: > Wenn du OOP ernst nimmst, dann gibt es keine Funktionen mehr. Es gibt > nur noch Objekte und diese Objekte > * kümmern sich um sich selbst > d.h. das Objektt selbst hält die Daten, die es zur Arbeit benötigt > * verstehen Befehle > die Befehle sind die Memberfunktionen. Gegenbeispiel: CLOS.