Hallo zusammen,
in meinem Programm habe ich drei ähnliche Strukturen:
1
struct
2
{
3
BYTELen;
4
BYTEIndex[48];
5
BYTEData[48][16];
6
7
}MailBox1;
8
9
10
struct
11
{
12
BYTELen;
13
BYTEIndex[32];
14
BYTEData[32][16];
15
16
}MailBox2;
17
18
struct
19
{
20
BYTELen;
21
BYTEIndex[16];
22
BYTEData[16][16];
23
24
}MailBox3;
Im Laufzeit wird nur eine Struktur von den Drei benutzt (hängt von einer
Hw Adresse ab), die Daten kommen vom Master und müssen in der richtigen
MailBox kopiert, bearbeitet und anschließend weiter gesendet.
Wie kann ich am besten /elegantesten eine universelle Funktion
programmiere, die alle Typen "gleich" behandelt ohne eine switch /case
oder doppelten Code?
Ich habe an void* Ptr gedacht, aber das hilft nicht viel, da ich die
ganze Zeit den richtigen Strukturtyp kennen muss => also doch eine
switch/case.
Martin schrieb:> Wie kann ich am besten /elegantesten eine universelle Funktion> programmiere, die alle Typen "gleich" behandelt ohne eine switch /case> oder doppelten Code?
So gar nicht.
Was noch gehen würde, ist in deiner Mailbox Pointer auf die
tatsächlichen Arrays zu haben. Dann hast du nur eine 'struct Mailbox'
und je nach tatsächliche benötigter Instanz, zeigen die Pointer dann auf
unterschiedlich große Arrays.
1
struct
2
{
3
BYTELen;
4
BYTE*Index;
5
BYTE(*Data)[][16];
6
}MailBox2;
(Der Pointer auf das 2D Array musste IMHO so vereinbart werden, aber
100% sicher bin ich mir da jetzt auch nicht, was die Syntax angeht. Zur
Not kann man das immer noch als einfaches 1D Array entsprechender Größe
ansehen und sich selbst um die Indizierung kümmern.
Ohne Fallunterscheidung geht es dann arithmetisch ab, indem Len (oder
was immer Basis für die Fallunterscheidung ist) in die Berechnung des
Index von Bytes[] eingeht. Ob das freilich sinnvoller als eine
Fallunterscheidung ist, ist eine andere Frage.
Mit Fallunterscheidung würde ich dir statt void* eine union als Herz
legen. Dafür sind die da.
A. K. schrieb:> Mit Fallunterscheidung würde ich dir statt void* eine union als Herz> legen. Dafür sind die da.
Die union ist dann (hier) aber so groß, wie die größte struct.
Da kann man dann gleich nur eine struct nehmen.
DirkB schrieb:> Die union ist dann (hier) aber so groß, wie die größte struct.> Da kann man dann gleich nur eine struct nehmen.
Eine Union macht in diesem Fall keinen wirklichen Sinn, da die Struktur
MailBox1 alle Daten aufnehmen kann, die in MailBox2 und MailBox3
gespeichert werden können. Dazu einfach MailBox1 erweitern und die
entsprechende Funktion anpassen:
1
struct
2
{
3
BYTELen;
4
BYTEIndex[48];
5
size_tIndexSize;
6
BYTEData[48][16];
7
size_tDataSize;/* gilt nur fuer die erste Dimension */
8
}MailBox1;
Eine Union würde hingegen Sinn machen, wenn nur wenige Variablen des
Typs MailBox1, aber viele von MailBox3 benötigt würden und der Speicher
optimal ausgenutzt werden müsste (ich hoffe, ich hab mich nicht
verrechnet):
Ich denke, A.K. meinte eine union aus 3 verschiedenen Pointern anstelle
eines einzigen void*
Das eliminiert zwar nicht den Zugriff über switch-case Strukturen, ist
aber immer noch besser als ein void* in einer Funktionsargumentliste.
Karl Heinz schrieb:> Ich denke, A.K. meinte eine union aus 3 verschiedenen Pointern anstelle> eines einzigen void*
Nein. Ich verstehe die Aufgabe so, dass der Controller im Rahmen seiner
Programmausführung auf eine der 3 Varianten fixiert ist. Aber welche das
ist liegt erst zu Laufzeit fest. Oben steht dazu etwas von einer
Hardware-Adresse, was immer das sein soll, die darüber entscheidet. Kann
man sich vielleicht auch als DIP-Switch vorstellen.
Wenn diese Struktur einfach vorkommt handelt es sich um die klassische
Aufgabe von Unions. Der zu reservierende Platz entspricht natürlich der
grössten davon, aber das liegt in der Natur der Aufgabe.
Anders wird es, wenn diese Struktur dynamisch verwaltet werden muss oder
als Element eines Arrays verwendet wird.
--
Zum eigentlichen Problem: Das artet schnell in Kaffeesatzlesen aus, weil
zu wenig über die Verarbeitung bekannt ist. Ohne Fallunterscheidung zu
arbeiten lohnt nur, wenn sich einzig die Positionen der Felder
unterscheiden, nicht aber die Verarbeitung. Dann aber könnte ein Pointer
(oder ein Basis-Index) dienlich sein, um in Bytes[] (mein Beispiel oben)
den Anfang von Data[] zu kennzeichnen. Nur dieser Pointer muss dann
fallbasiert einmal gesetzt werden.
A. K. schrieb:> Karl Heinz schrieb:>> Ich denke, A.K. meinte eine union aus 3 verschiedenen Pointern anstelle>> eines einzigen void*>> Nein.
Dann muss ich den anderen recht geben, dass eine union eine schlechte
Lösung ist und ich hätte meinen Einwand dahingehend am Vormittag nicht
zurück ziehen brauchen.
Denn dann erhebt sich die Frage: Wozu überhaupt 3 verschiedene
Strukturen, wenn alle sowieso letzten Endes eingepfercht in die union
den gleichen Platz verbrauchen.
Karl Heinz schrieb:> Denn dann erhebt sich die Frage: Wozu überhaupt 3 verschiedene> Strukturen, wenn alle sowieso letzten Endes eingepfercht in die union> den gleichen Platz verbrauchen.
Das war ja auch mein erster Ansatz mit der arithmetischen Variante. Oder
wie grad eben skizziert mit Pointer:
und dann mit Data[n][k] statt MailboxX.Data[n][k] arbeiten.
Die Union ergibt dann Sinn, wenn nicht nur das Layout verschieden ist,
sondern sich auch die Verarbeitung der Daten signifikant unterscheidet.
Das geht nicht klar aus der Aufgabe hervor.
A. K. schrieb:> Karl Heinz schrieb:>> Denn dann erhebt sich die Frage: Wozu überhaupt 3 verschiedene>> Strukturen, wenn alle sowieso letzten Endes eingepfercht in die union>> den gleichen Platz verbrauchen.>> Das war ja auch mein erster Ansatz mit der arithmetischen Variante.
Hab ich gesehen.
Daher dachte ich ja auch, du hättest in der Antwort einfach nur das Wort
Pointer vergessen
> Mit Fallunterscheidung würde ich dir statt void* eine union als Herz legen.
denn ein
1
typedefunionMailBoxPtr_
2
{
3
MailBox1*pMail1;
4
MailBox2*pMail2;
5
MailBox3*pMail3;
6
}MailBoxPtr;
7
8
9
voidfoo(uint8_tType,MailBoxPtrtheMail)
10
{
11
...
12
}
ist immer noch besser als ein
1
voidfoo(uint8_tType,void*theMail)
2
{
3
...
4
}
in welches ich jeden dahergelaufenen Pointer reinstopfen kann,
1
foo(1,"Hallo World");
ohne dass es den Compiler auch nur im geringsten juckt.
Mit einer Pointer union könnte man wenigstens noch ein bischen
Typsicherheit retten, wenn man auch um die Fallunterscheidung nicht
rumkommt.
Karl Heinz schrieb:> in welches ich jeden dahergelaufenen Pointer reinstopfen kann
Das ist aus meiner Sicht die beste Lösung. Und eine union braucht es
dazu nicht, wenn man in der Fallunterscheidung mit Distanzen rechnet.
Das ist effizient und schneller als mit unions und types zu
unterscheiden.
Martin schrieb:> BYTE Data[48][16];
Würde mich interessieren, warum Du so komplex denkst. Linear ist viel
schneller. Der Compiler macht letzten Endes auch nichts anderes daraus.
Also warum nicht:
1
typedefstruct{
2
3
char*prev,*next,*data;
4
}Mailbox_t;
Und per malloc bei Bedarf den notwendigen Speicher entsprechend
anfordern.