Forum: Compiler & IDEs Array mit verschiedenen Datentypen


von goldeneyes1987 (Gast)


Lesenswert?

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:
1
typedef struct
2
{
3
    int8_t a[3];
4
    uint16_t b[3];
5
    float c[3];
6
} ComplexDataType;
1
// Template beispiel
2
3
template <class DATA_1,uint8_t size_1, class DATA_2,uint8_t size_2 , class DATA_3, uint8_t size_3>
4
class ComplexDataType
5
{
6
    DATA_1 a[size_1];
7
    DATA_2 b[size_2];
8
    DATA_3 c[size_3];
9
10
};
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

von Noname (Gast)


Lesenswert?

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?

von Noname (Gast)


Lesenswert?

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!

von goldeneyes1987 (Gast)


Lesenswert?

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:
1
+
2
3
// in Control.h
4
5
...
6
void control(ComplexDataType ControlData, ComplexDataType SensorData)
7
{
8
..
9
if( XY) controlXY(ControlData.DATA_1, SensorData.DATA_1);
10
else if( ZZ) controlZZ(ControlData.DATA_2, SensorData.DATA_2);
11
..
12
}
13
...
14
15
16
// in main
17
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!

von Noname (Gast)


Lesenswert?

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.

von goldeneyes1987 (Gast)


Lesenswert?

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

von Karl H. (kbuchegg)


Lesenswert?

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.

von goldeneyes1987 (Gast)


Lesenswert?

1
template <class DATA_1,uint8_t size_1, class DATA_2,uint8_t size_2 , class DATA_3, uint8_t size_3>
2
class ComplexDataType
3
{ public:
4
    DATA_1 a[size_1];
5
    DATA_2 b[size_2];
6
    DATA_3 c[size_3];
7
8
};
9
10
// egal wo ich den Complexen daten type benötige
11
12
13
ComplexDataType <int, 3, int, 3, int ,3> TEST1;
14
ComplexDataType <int, 3, float, 2, float ,3> TEST2;
15
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ß

von goldeneyes1987 (Gast)


Lesenswert?

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.

von goldeneyes1987 (Gast)


Lesenswert?

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:
1
void winkel_regler (ComplexDataType* a);
2
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:
1
ComplexDataType* <int, 3, int, 3, int ,1> TEST1;
2
...
3
/*
4
in TEST1 werden jetzt winkelpositionen(3Achsen), winkelgeschwindigkeiten(3Achsen) und abstand zu einem gegenstand gespeichert.
5
*/
6
...
7
winkel_regler(TEST1);
8
abstands_regler(TEST1);
9
..
10
// jeder Regler weiss worauf er zugreifen muss.
11
// so kann ich alles Zustandsinformation zentral in einer Instanz speichern!

von Daniel (Gast)


Lesenswert?

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 ;)

von goldeneyes1987 (Gast)


Lesenswert?

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.

von Daniel (Gast)


Lesenswert?

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.

von Karl H. (kbuchegg)


Lesenswert?

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:
>
1
> 
2
> void winkel_regler (ComplexDataType* a);
3
> void abstands_regler (ComplexDataType* a);
4
>
>
> 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.

von Karl H. (kbuchegg)


Lesenswert?

> 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.

von goldeneyes1987 (Gast)


Lesenswert?

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:
1
emplate <class DATATYPE_1, uint8_t size_1,  class DATATYPE_2, uint8_t size_2, class DATATYPE_3, uint8_t size_3>
2
class ComplexData
3
{
4
public:
5
  DATATYPE_1   angle[size_1];
6
  DATATYPE_2   angle_velocity[size_2];
7
  DATATYPE_3   position[size_3];
8
};
9
10
class Controller
11
{  template <class DATATYPE_1, uint8_t size_1,  class DATATYPE_2, uint8_t size_2, class DATATYPE_3, uint8_t size_3>
12
  void angle_control(ComplexData <DATATYPE_1,size_1,DATATYPE_2,size_2,DATATYPE_3,size_3> *c);
13
    template <class DATATYPE_1, uint8_t size_1,  class DATATYPE_2, uint8_t size_2, class DATATYPE_3, uint8_t size_3>
14
  void position_control(ComplexData <DATATYPE_1,size_1,DATATYPE_2,size_2,DATATYPE_3,size_3> *c);
15
16
};
17
18
template <class DATATYPE_1, uint8_t size_1,  class DATATYPE_2, uint8_t size_2, class DATATYPE_3, uint8_t size_3>
19
void Controller::angle_control(ComplexData <DATATYPE_1,size_1,DATATYPE_2,size_2,DATATYPE_3,size_3> *c)
20
{
21
  // integration
22
  c->angle[0] += c->angle_velocity*0.02;
23
  .....
24
}
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!?!

von goldeneyes1987 (Gast)


Lesenswert?

goldeneyes1987 schrieb:
> // integration
>   c->angle[0] += c->angle_velocity*0.02;
>   .....

muss natrülich so aussehen
1
 c->angle[0] += c->angle_velocity[0]*0.02;

von goldeneyes1987 (Gast)


Lesenswert?

Dessen benutzung würde wie folgt funktionieren:
1
// main.cpp
2
3
ComplexData <int, 2, int, 2,float, 3> *TEST;
4
Controller myTestController;
5
myTestController.angle_control(TEST); // angle_control() mus als public dekl. sein

Oder hat das ganze einen knackpunkt den ich übersehe???

von Karl H. (kbuchegg)


Lesenswert?

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?)

von goldeneyes1987 (Gast)


Lesenswert?

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:
1
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!

von goldeneyes1987 (Gast)


Lesenswert?

Also bin zu dem schluß gekommen eine etwas pragmatischere Lösung 
anzustreben:
1
// Prinzipiell gibt es 4 Verschiedene Komplexe Datenstrukturen
2
3
class Inputs{
4
public:
5
         uint16_t rc_command[5];
6
         // vorerst nur rc_command, wird sich aber ändern!
7
};
8
9
class Sensor{
10
public:
11
   int16_t angle_rate[3];
12
   int16_t accelerration[3];
13
         // vorerst .....
14
};
15
class States{
16
public:
17
   float angle[3];
18
   int16_t angle_rate[3];
19
   int16_t accelerration[3];
20
         // vorerst .....
21
};
22
23
class Output{
24
public:
25
         uint16_t output[5];
26
         // vorerst .....
27
};
28
29
30
class MyController{
31
public:
32
  void angle_control(Inputs* a, Sensor* b, States* c);
33
        void position_control(Inputs* a, Sensor* b, States* c);
34
};
35
36
void MyController::angle_control(Inputs* a, Sensor* b, States* c)
37
{
38
    b->angle[0] += b->angle_velocity[0]*0.02;
39
}
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

von Florian (Gast)


Lesenswert?

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.

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
Noch kein Account? Hier anmelden.
Lade...