Hallo zusammen,
ich habe eine Design Frage, ich habe in C eine Struktur, welche
beinhaltet wieder mehrere Arrays mit anderen Strukturinstanzen und
Funktionszeiger.
Beispiel:
1
typedefvoid(*ABCFuncType)(void);
2
typedefstructConfigStructConfigStructType;
3
typedefstructMyExt1StructMyExt1StructType;
4
typedefstructMyExt2StructMyExt2StructType;
5
typedefstructMyExt3StructMyExt3StructType;
6
7
structMyExt1Struct
8
{
9
inta;
10
intb;
11
ABCFuncTypeRoutine;
12
};
13
14
structMyExt2Struct
15
{
16
inta;
17
intb;
18
intc;
19
ABCFuncTypeRoutine;
20
};
21
22
structMyExt3Struct
23
{
24
inta;
25
intb;
26
booleanxyz;
27
booleanaaa;
28
ABCFuncTypeRoutine;
29
};
30
31
structConfigStruct
32
{
33
constvoid*MyStructType;
34
intlength;
35
};
Die Struktur ConfigStruct wird an eine Funktion z.B.
MainFct(ConfigStructType config) übergeben, wo die Daten weiter
verarbeitet werden. Die Funktion kann nur einen Parameter, nämlich diese
Struktur annehmen.
Jetzt ist so, dass diese Funktion (d.h. MainFct) nicht weiß, ob
MyStructType ist von Typ: MyExt1StructType, MyExt2StructType oder
MyExt3StructType.
Meiner Meinung nach ich habe drei Möglichkeiten:
1) In ConfigStruct ein ENUM hinzufügen, damit man weiß wie man casten
soll => Nachteil kostet Laufzeit, da ständig gecastet werden muss :/
2) Anstatt 3 Strukturen, alle mögliche attribute in nur einer Struktur
packen => Nachteil kostet Speicher, was auch knapp ist.
Zur Info: deswegen 3 Strukturen, da die Attributen
Konfigurationsabhängig sind. Von der Struktur: MyStructType werden ca.
200-300 Instanzen angelegt ;)
Kennt Ihr vielleicht eine Lösung für solche Probleme?
Grüße
Union?
Die kann dann auch das Ziel eines Pointers sein.
Ein cast der Art "seh denn Speicherbereich an Adresse X mal als wie
Struktur Y strukturiert an", kosten übrigens genau nix.
Es muß ja nicht jede Instanz eine union sein. Es reicht ja, wenn der
*MyStructType auf eine solche zeigt. Wenn dann jede Struktur mit dem von
dir schon angedachten enum STRUCT_TYPE beginnt, dann kann man einfach
entscheiden welche "Sicht" auf die Daten die richtige ist.
Falls die "Routine" für die Verarbeitung der Daten zuständig ist, würde
ich diese in allen structs an den Anfang stellen. Eine struct mit nur
diesem Funktions-Pointer wäre dann das einzige, was über ein "Objekt"
bekannt sein muß. Nennt man auch "interface".
Wenn du noch nichts mit objektorientierten Sprachen zu tun hattest, dann
stehst du kurz davor diese Techniken selbst zu "erfinden". Bei mir war
das sehr oft der Weg etwas zu lernen. Indem man selbst vom Problem
ausgehend die Lösung erarbeitet. Thumbs Up!
@Bastler: wie meinst Du es genau? castem muss ich sowieso. Könntest Du
mir ein Beispiel skizzieren? ;)
Mit der Lösung zwei meinte ich folgendes:
1
typedefstructMyExt3StructMyExt3StructType;
2
3
structMyExt3Struct
4
{
5
booleanxyz;
6
booleanaaa;
7
8
};
9
10
structMyExt1Struct
11
{
12
inta;
13
intb;
14
constMyExt3StructType*MyExt3typ;
15
ABCFuncTypeRoutine;
16
};
MyExt3typ ist entweder NULL oder wenn die Attributen vorhanden sein
sollten, dann wird eine Adresse zugewiesen. Wie schon gesagt, auch wenn
NULL stehen sollte, dann kostet es auch viel Speicher....
Lösung 1 hat gewissen Charm ;) so eine Art von Polymorphismus...
BTW. Mit objektorientierten Sprachen hatte ich zu tun gehabt: Java und
C#
Ok, nur daß man in Java und C# wenig mit der technischen Implemantierung
zu tun hat. Daß ein Objekt aus einem Pointer auf ein Array von
Methoden-Pointern und nachfolgend, nach dem Pointer, den
Member-Variablen besteht, erfährt man da nicht.
Nach deinem Beispiel gibt es 3 verschiedene Strukturen. Alle beinhalten
einen Functions-Pointer. Wie diese benutzt werden sollen ist nicht
bekannt. Kennt die Hauptfunktion den Aufbau der Strukturen oder reicht
sie diese nur transparent an den enthaltenen Funktions-Pointer weiter?
Falls ersteres muß es zum Unterscheiden der Strukturen irgendein
Kennzeichen geben. Getrixt könnte das natürlich auf der Pointer sein,
falls für jede Struktur eine andere Funktion benutzt wird.
Leider ist aber der entscheidende Teil der Problemstellung nicht
beschrieben, vermutlich weil "schon gelöst" und nun müssen nurmehr die
Probleme dieser "Lösung" ausgeräumt werden. Erinnert mich an die Arbeit
und die ruht bei mir bis Montag ;-)
ups, mein Fehler, Funktionspointer beinhaltet nur die ConfigStruct. Die
Hauptfunktion kennt die Strukturtypen, aber wie schon gesagt bei der
Übergabe weiß nicht für welche hat sich der "Konfigurator" entschieden
;) in der ConfigStruct stehen mehrere Funktionspointer, die aufgerufen
werden und in dieser Funktionen werden Daten von MyStructType
verarbeitet, d.h. diese Funktionen müssen auch den Typ von der Struktur
kennen. Alle Funktionen müssen auch Reentrant sein.
Hmmmm....viellciht sollte ich doch eine große Struktur, mit allen
Optionen/Attributen, zur Verfügung stellen? dann muss ich die NULL
Pointer in kauf nehmen. Auch wegen Erweiterbarkeit, d.h. es kann sein,
dass neue Attributen gebraucht werden, dann müsste ich x-Verschiedene
Strukturtypen (alle Kombinationen) zur Verfügung stellen. pro Null
Pointer und Struktur verliere ich 32 Bits. In jeder Funktion zuerst via
Switch-Case den Typ zu ermitteln und dann noch zu casten würde
vielleicht doch zu viel Laufzeit kosten......??
Mit OOD/OOR würde ich es mit Vererbung lösen.
Danke für Deine Antworten!!!
frageer_10 schrieb:> In jeder Funktion zuerst via> Switch-Case den Typ zu ermitteln und dann noch zu casten würde> vielleicht doch zu viel Laufzeit kosten......??
Bauchgefühl funktioniert bei sowas nicht, Messen und Profiling schon.
> Mit OOD/OOR würde ich es mit Vererbung lösen.
C++ und als einziges Feature an dieser Stelle Polymorphie benutzen? Wenn
man Polymorphie braucht und von Hand nachprogrammiert, ist der Overhead
wahrscheinlich (mindestens) der gleiche wie wenn man den Compiler das
erledigen lässt.
@Tom: ich muss embedded C nutzen.
Meine letzte Frage ist,
Lösung 1) mit verschiedenen Structs, abhängig von der Konfiguration.
Oder doch Lösung 2) mit einer großen Struktur und bei zusätzlichen
Attributes kommen Zeigern auf Extended-Strukturinstanzen, welche bei der
nicht Benutzung auf NULL Zeiger zeigen?
Was meint Ihr?
frageer_10 schrieb:> Nachteil kostet Laufzeit, da ständig gecastet werden muss :/
Mal das assembler-Listing ansehen. Casten sollte eigentlich gar keine
Laufzeit kosten (wie sollte denn ein Cast im Assembler Deiner Meinung
nach aussehen?)