Hallo
Ich stehe mal wieder auf dem Schlauch und bräuchte mal einen Tipp.
Und zwar möchte ich gerne zu den Variablen sensor1 bis 3 die
dazugehörigen Strings auswählen. Das folgende Beispiel klappt auch
soweit. Sobald aber z.B. sensor1 einen anderen Datentyp als die
restlichen Einträge hat, geht das natürlich nicht mehr, weil das
array_Data ja als uint32_t definiert wurde.
Gibt es einen ähnlich eleganteren Weg, der mit unterschiedlichen
Datentypen programmiert werden kann? Für mich ist der Vorteil hier, dass
man über eine Zählvariable direkt die passenden Paare (Wert und Text)
bekommt.
Bestimmt gibt es eine Möglichkeit mit Strukturen zu arbeiten. Aber da
müsste man ja die einzelnen Members wieder explizit hinschreiben
(struc.memberXYZ). Sonst fällt mir echt nichts ein...
Ich fürchte auch, Du denkst in zuviel Abstraktionen.
Schreibe mal den Code direkt.
Es wird nicht klar, was genau Du denn in array_Data haben möchtest.
Und auch nichtz der Unterschied zwischen der Sensor-Instanz (sensor1..3)
und dessen Index (auch sensor1..3).
Enum ist eigentlich nur eine Konstantendefinition, in deinem Beispiel,
das Gleiche, wie
#define sensor1 0
#define sensor2 1
#define sensor3 1
Nicht als Array zu verstehen, könnte aber den Index für ein Array
liefern.
Für den Typ, bräuchte dann aber jedes Arrayelement ein weiteres Feld.
Peter schrieb:> Hallo>> Ich stehe mal wieder auf dem Schlauch und bräuchte mal einen Tipp.>> Und zwar möchte ich gerne zu den Variablen sensor1 bis 3 die> dazugehörigen Strings auswählen.
sensor1 bis 3 sind keine Variablen, sondern Enumeratoren. Sie belegen
keinen Speicher und existieren nur im Quellcode. Daher ergibt es auch
keinen Sinn, ihre Adresse zu nehmen.
> Das folgende Beispiel klappt auch soweit.
Das glaube ich dir nicht. Dein Code enthält mehrere Fehler.
> Sobald aber z.B. sensor1 einen anderen Datentyp als die> restlichen Einträge hat, geht das natürlich nicht mehr, weil das> array_Data ja als uint32_t definiert wurde.
sensor1 bis 3 sind alle vom Typ enum dataSequence.
Kann man natürlich alles machen; unten als Fingerübung in C.
Ansonsten kann ich nur bekräftign, was Achim geschrieben hat: Das sieht
schwer aus nach Über-Abstaktion.
In C++ ließe sich das alles kompakter formulieren, aber das Problem der
Über-Abstraktion löst auch C++ nicht (statt der switch/case hätte man
dann vtables).
1
#include<stdint.h>
2
#include<inttypes.h>
3
#include<stdlib.h>
4
#include<stdio.h>
5
6
typedefenum
7
{
8
TYP_A,
9
TYP_B,
10
TYP_C,
11
N_TYPES
12
}sensor_typ_t;
13
14
// 3 verschiedene Sensor-Typen, wobei die 1. Komponente bei allen Typen
15
// gleich ist, um damit spaeter Union sensor_t unterscheiden zu koennen.
16
17
typedefstruct
18
{
19
constsensor_typ_ttyp;
20
constchar*name;
21
uint32_tdata;
22
}sensorA_t;
23
24
typedefstruct
25
{
26
constsensor_typ_ttyp;
27
constchar*name;
28
intdata[2];
29
}sensorB_t;
30
31
typedefstruct
32
{
33
constsensor_typ_ttyp;
34
uint8_tdata[4];
35
constchar*name;
36
}sensorC_t;
37
38
// Union fuer alle 3 Sensor-Typen: Typ des Sensors laesst sich
Text und Sensor-Datentyp gehören zusammen?!
Also eine struct "Sensor_struct"
Der Sensor-Typ kann unterschiedlich sein?
Also eine Union "Sensor_union".
Die Sensor_struct besteht also aus einem Char-Array und einer
Sensor_union".
Daraus dann ein Array "Sensor_array" erstellen.
Peter schrieb:> So lässt es sich aber bei mir compilieren und richtig ausführen
Dann lass "s[3]" einfach weg und arbeite direkt mit den enums oder dem
Index ....
Peter schrieb:
Johann L. schrieb:> In C++ ließe sich das alles kompakter formulieren, aber das Problem der> Über-Abstraktion löst auch C++ nicht (statt der switch/case hätte man> dann vtables).
Nein, denn man muss nicht notwendigerweise Laufzeitpolymorphie (s.o.
vtables) verwenden.
Er möchte ja einfach "nur" generischen Code haben. Und das würde man in
C++ ganz einfach mit statischer Polymorphie lösen.
Es steht zwar in der Fragestellung nicht explizit drin, doch ich vermute
mal stark, dass es eine reine C-Frage ist. Und da C nun mal eine
nicht-generische Sprache ist, kann man das Problem nur mit
poor-mans-polymorphism lösen.
Wilhelm M. schrieb:> Johann L. schrieb:>>> In C++ ließe sich das alles kompakter formulieren, aber das Problem der>> Über-Abstraktion löst auch C++ nicht (statt der switch/case hätte man>> dann vtables).>> Nein, denn man muss nicht notwendigerweise Laufzeitpolymorphie (s.o.> vtables) verwenden.>> Er möchte ja einfach "nur" generischen Code haben. Und das würde man in> C++ ganz einfach mit statischer Polymorphie lösen.>> Es steht zwar in der Fragestellung nicht explizit drin, doch ich vermute> mal stark, dass es eine reine C-Frage ist. Und da C nun mal eine> nicht-generische Sprache ist, kann man das Problem nur mit> poor-mans-polymorphism lösen.
Hat man Dir in der Schule oft das Pausenbrot geklaut?
Sry, der Wissensstand vom TO kommt klar raus.
Grüsse,
René
Wilhelm M. schrieb:> Es steht zwar in der Fragestellung nicht explizit drin, doch ich vermute> mal stark, dass es eine reine C-Frage ist. Und da C nun mal eine> nicht-generische Sprache ist, kann man das Problem nur mit> poor-mans-polymorphism lösen.
Bei C-Structs gibt es keine Vererbung, aber das ist ein Vorteil, kein
Nachteil, denn man kann stattdessen Aggregation nutzen:
Ungetestet:
In go ist das noch etwas schöner. Um von der Liste wieder auf den
derived typ zugreifen zu können braucht man einen upcast, aber das ist
auch bei c++ nicht anders.
DPA schrieb:> Wilhelm M. schrieb:>> Es steht zwar in der Fragestellung nicht explizit drin, doch ich vermute>> mal stark, dass es eine reine C-Frage ist. Und da C nun mal eine>> nicht-generische Sprache ist, kann man das Problem nur mit>> poor-mans-polymorphism lösen.>> Bei C-Structs gibt es keine Vererbung, aber das ist ein Vorteil, kein> Nachteil, denn man kann stattdessen Aggregation nutzen:
Von Vererbung hatte ich gar nicht gesprochen.
> In go ist das noch etwas schöner. Um von der Liste wieder auf den> derived typ zugreifen zu können braucht man einen upcast,
Du meinst hier einen "down-cast" oder besser eine "cross-cast", da es ja
keine Vererbung und damit kein Liskov gibt, wie Du oben selbst sagst.
Willst Du nun Deine Liste verwenden, muss Du nun (wie Johann es oben mal
exemplarisch gemacht hat) zu Fuß anhand eines Diskriminators (etwa per
switch) entscheiden, um ein type-dispatching durchzuführen. Dann sind
wir bei dem oben von mir genannten poor-mans-polymorphism.
Wilhelm M. schrieb:> Willst Du nun Deine Liste verwenden, muss Du nun (wie Johann es oben mal> exemplarisch gemacht hat) zu Fuß anhand eines Diskriminators (etwa per> switch) entscheiden, um ein type-dispatching durchzuführen. Dann sind> wir bei dem oben von mir genannten poor-mans-polymorphism.
Der springende Punkt ist doch aber gerade, dass das bei C++ ebenso ist,
solange die abgeleiteten Typen unterschiedliche Funktionens sets haben.
Bei Funktionen, die nur die Basisklasse benötigen, muss man ebenfalls in
beiden fällen nicht casten. Nur wenn man mal eine Funktion überladen
muss, dann hat man einen Cast mehr. Dafür spart man sich die ganzen
Probleme, die Vererbung mit sich bringen würde. Keine sich
überschneidenden Namensräume zu haben finde ich vereinfacht vieles.
DPA schrieb:> Wilhelm M. schrieb:>> Willst Du nun Deine Liste verwenden, muss Du nun (wie Johann es oben mal>> exemplarisch gemacht hat) zu Fuß anhand eines Diskriminators (etwa per>> switch) entscheiden, um ein type-dispatching durchzuführen. Dann sind>> wir bei dem oben von mir genannten poor-mans-polymorphism.>> Der springende Punkt ist doch aber gerade, dass das bei C++ ebenso ist,> solange die abgeleiteten Typen unterschiedliche Funktionens sets haben.
Nein, wenn sie überschrieben sind, macht es der Compiler per vtable.
> Bei Funktionen, die nur die Basisklasse benötigen, muss man ebenfalls in> beiden fällen nicht casten.
Da ich dann nur den statischen Typ verwende, benutze ich auch keine
Laufzeitpolymorphie. Dann brauch ich das ganze Konstrukt nicht.
> Nur wenn man mal eine Funktion überladen> muss, dann hat man einen Cast mehr.
Genau, sagte ich ja oben. Bei C++ braucht man (wegen vtable) keinen
down-cast.
> Dafür spart man sich die ganzen> Probleme, die Vererbung mit sich bringen würde.
Die wären?
> Keine sich> überschneidenden Namensräume zu haben finde ich vereinfacht vieles.
Wo kommt das in dem Beispiel vor?
Wie ich oben schon sagte, kann man es auch (oft besser) mit stat.
Polymorphie lösen.
Wilhelm M. schrieb:>> Der springende Punkt ist doch aber gerade, dass das bei C++ ebenso ist,>> solange die abgeleiteten Typen unterschiedliche Funktionens sets haben.>> Nein, wenn sie überschrieben sind, macht es der Compiler per vtable.
Nein, unterschiedliche Funktions sets, nicht identische. Du bringst
genau den fall, den ich spezifisch ausgeschlossen hatte.
>> Bei Funktionen, die nur die Basisklasse benötigen, muss man ebenfalls in>> beiden fällen nicht casten.>> Da ich dann nur den statischen Typ verwende, benutze ich auch keine> Laufzeitpolymorphie. Dann brauch ich das ganze Konstrukt nicht.
Genau, hier verstehen wir uns wieder. Ich würde noch behaupten, dass die
fälle, wo man direckt schon die Funktion nutzen kann, weil man die
richtigen Typen schon hat, etwa 80% aller Fälle ausmacht.
>> Nur wenn man mal eine Funktion überladen muss, dann hat man einen Cast>> mehr.>> Genau, sagte ich ja oben. Bei C++ braucht man (wegen vtable) keinen> down-cast.
Aber nur in diesem Spezialfall, wenn man im übergeordneten Typ eine
Funktion hat, die man auch überladen kann. Sonst muss man entweder eine
für den Typ unsinnige einführen, oder doch wieder je nach Typ einen
anderen downcast machen. In dem nicht unüblichen fall hat man logisch
dann häufig eine Funktion die die Objekte verarbeiten, und nicht eine
Funktion die eine logische eigenschaft des Objekts wäre, womit die
lösung mit überladung zu bestenfalls unschönen Architekturen führt.
>> Dafür spart man sich die ganzen Probleme, die Vererbung mit sich bringen>> würde.>> Die wären?
Siehe nächsten Satz. Ausserdem hat man damit eine unnötige Variation
etwas zu tun, das auch ohne genausogut geht. Und man kann schnell
unnötige oder problematische Vererbungen machen, wenn man später merkt
dass z.B. ein Kreis doch keine spezielle Ellipse ist und umgekehrt. Ohne
Vererbung würde man garnicht erst auf die Idee kommen, das eine könne
teil des anderen sein.
>> Keine sich überschneidenden Namensräume zu haben finde ich vereinfacht>> vieles.>> Wo kommt das in dem Beispiel vor?
Wozu muss es?
> Wie ich oben schon sagte, kann man es auch (oft besser) mit stat.> Polymorphie lösen.
Finde ich nicht. Ist übrigens auch nicht im Beispiel.
DPA schrieb:> Aber nur in diesem Spezialfall, wenn man im übergeordneten Typ eine> Funktion hat, die man auch überladen kann.
Ich denke, Du meinst: überschreiben.
> Sonst muss man entweder eine> für den Typ unsinnige einführen, oder doch wieder je nach Typ einen> anderen downcast machen.
Wenn Du das machen "musst", deutet das fast immer auf eine falsche
Modellierung hin. Denn Du benutzt dann eben nicht das Interface. Kann
man aber per NVI lösen.
>>> Dafür spart man sich die ganzen Probleme, die Vererbung mit sich bringen>>> würde.>>>> Die wären?>> Siehe nächsten Satz. Ausserdem hat man damit eine unnötige Variation> etwas zu tun, das auch ohne genausogut geht.
Verstehe nicht, was Du meinst.
> Und man kann schnell> unnötige oder problematische Vererbungen machen, wenn man später merkt> dass z.B. ein Kreis doch keine spezielle Ellipse ist und umgekehrt. Ohne> Vererbung würde man garnicht erst auf die Idee kommen, das eine könne> teil des anderen sein.
Konformanz-Verletzungen gibt auch anderswo.
>>> Keine sich überschneidenden Namensräume zu haben finde ich vereinfacht>>> vieles.>>>> Wo kommt das in dem Beispiel vor?>> Wozu muss es?
Wenn Du es erwähnst.
Zweig schrieb:> Wilhelm M. schrieb:>> Wie ich oben schon sagte, kann man es auch (oft besser) mit stat.>> Polymorphie lösen.>> wie würde das denn aussehen?
Mal ein einfaches Beispiel:
Wilhelm M. schrieb:> Verstehe nicht, was Du meinst.
Ich bezihe mich auf die 2 Sätze des Zitats, die du auseinander genommen
hast. Der zweite ist eine folge des anderen.
Zweig schrieb:> Wilhelm M. schrieb:>> Mal ein einfaches Beispiel:>> Und wo ist da das enum und wie spreche ich einen von den drei Sensoren> an.> ?
Im OP stand ja auch nichts von Sensorfunktionen (außer den Namen
auszugeben und einen Wert). Also, was möchtest Du denn machen?
Wilhelm M. schrieb:> Im OP stand ja auch nichts von Sensorfunktionen (außer den Namen> auszugeben und einen Wert). Also, was möchtest Du denn machen?
Ich hätte gerne mal ein Beispiel bei dem es z.B 3 unterschiedliche
SenorTypen gibt.
Klar gleiche Interface aber unterschiedliche Implementierung.
Da fehlt mir ein Ansatz wie ich z.B über einen enum einer dieser
Sensoren Typen auswähle, eine Instanz bilde und fortan nur noch mit
diesem interagiere.
Ist sowas auch ohne vtable möglich?
Zweig schrieb:> Wilhelm M. schrieb:>> Im OP stand ja auch nichts von Sensorfunktionen (außer den Namen>> auszugeben und einen Wert). Also, was möchtest Du denn machen?>> Ich hätte gerne mal ein Beispiel bei dem es z.B 3 unterschiedliche> SenorTypen gibt.> Klar gleiche Interface aber unterschiedliche Implementierung.> Da fehlt mir ein Ansatz wie ich z.B über einen enum einer dieser> Sensoren Typen auswähle, eine Instanz bilde und fortan nur noch mit> diesem interagiere.
Genau das steht doch oben: Du hast da drei unterschiedliche Sensortypen,
sie werden instanziiert, und dann wird über sie iteriert.