Hallo!
Ich möchte einer Funktion einen Zeiger auf einen beliebigen Datentyp
übergeben und dort ablegen können.
void* habe ich gedacht, ist das richtige.
Nun habe ich mein typedef
1
typedefvoid*DataLimpet;
Bei Pointern auf int, char, structs, usw. scheint es zu gehen:
1
Test(DataLimpetlimpet)
2
{
3
unsignedchar*value;
4
value=((unsignedchar*)limpet);
5
}
Aber wenn im void* ein Funktionspointer steckt, mosert der Compiler
mit "invalid type conversion":
Moni schrieb:> Was mache ich falsch?
Du versuchst, einen Objekt- mit einem Funktionszeiger zu mischen.
Das geht nicht, zumindest nicht portabel, egal wie du dich drehst
und wendest.
Ich brauche ja nur ein Datentyp, der so groß ist, dass er sowohl Objekt-
mit einem Funktionszeiger beinhalten kann. Der Rest ist doch dann nur
Casten, oder nicht?
Moni schrieb:> Ich brauche ja nur ein Datentyp, der so groß ist, dass er sowohl Objekt-> mit einem Funktionszeiger beinhalten kann.
Den Typ gibt es: eine union aus Daten- und Funktionspointer.
Der Nachteil ist aber, dass in da, wo die Union angelegt wird, alle
möglichen Funktionspointer bekannt sein müssen. Und das wollte ich
vermeiden, da diese ganze Funktion gekapselt sein sollte....
Einziger Workaround: Ich muss zwei...drei Arten Funktionspointer
festlegen und das war's...
Die Frage ist wozu sollte man es brauchen, etweder braucht ein Daten
oder Funktionszeiger aber du musst doch eh wissen was drin ist um es zu
verwenden.
Die Funktion, die diese "Datenklette" meiner Funktion mitgibt und wieder
zurückbekommt muss es wissen. Die Funktion, die die den unbekannten
Datentyp mitbekommt, nicht. Es kann auch NULL mitgegeben werden.
Moni schrieb:> Die Funktion, die die den unbekannten> Datentyp mitbekommt, nicht.
Was soll die Funktion mit einem "Etwas" machen, dessen Typ sie nicht
weiß?
Moni schrieb:> Ich brauche ja nur ein Datentyp, der so groß ist, dass er sowohl Objekt-> mit einem Funktionszeiger beinhalten kann. Der Rest ist doch dann nur> Casten, oder nicht?
Diese Annahme gilt nur dann, wenn sich beide Zeigerarten in demselben
Adressraum befinden. Streng genommen muss man nicht nur zwischen Daten-
und Programm-Adressraum unterscheiden, so es kann sogar mehrere
Daten-Adressräume geben.
Bei den meisten Universalprozessoren sind die Adressräume identisch,
selbst wenn es sich um Harvard-Architekturen handelt, da sie auf dem
externen Businterface wieder zusammengeführt werden.
Es gibt jedoch auch Prozessoren, bei denen dies nicht der Fall ist.
Floh schrieb:> Moni schrieb:>> Die Funktion, die die den unbekannten>> Datentyp mitbekommt, nicht.>> Was soll die Funktion mit einem "Etwas" machen, dessen Typ sie nicht> weiß?
Denk zb an qsort.
Der reicht auch seinen unbekannten zu sortierenden Datentyp einfach nur
an die vom Benutzer zu schreibende Compare-Funktion durch.
Moni schrieb:> Der Nachteil ist aber, dass in da, wo die Union angelegt wird, alle> möglichen Funktionspointer bekannt sein müssen. Und das wollte ich> vermeiden, da diese ganze Funktion gekapselt sein sollte....>> Einziger Workaround: Ich muss zwei...drei Arten Funktionspointer> festlegen und das war's...
Du kannst einen void(*)(void) auf jeden beliebigen Funktionszeiger
casten. Du kannst nur nicht zwischen Objekt- und Funktionszeiger hin und
her casten, und void* gehört nunmal zu den Objektzeigern.
Andreas Schweigstill schrieb:> Diese Annahme gilt nur dann, wenn sich beide Zeigerarten in demselben> Adressraum befinden. Streng genommen muss man nicht nur zwischen Daten-> und Programm-Adressraum unterscheiden, so es kann sogar mehrere> Daten-Adressräume geben.>> Bei den meisten Universalprozessoren sind die Adressräume identisch,> selbst wenn es sich um Harvard-Architekturen handelt, da sie auf dem> externen Businterface wieder zusammengeführt werden.>> Es gibt jedoch auch Prozessoren, bei denen dies nicht der Fall ist.
Es geht hier um einen kleinen MSP430. Und wie stelle ich fest, welchen
Adressraum ich verwende? Und wo liegen da die Unterschiede?
Rolf Magnus schrieb:> Du kannst einen void(*)(void) auf jeden beliebigen Funktionszeiger> casten. Du kannst nur nicht zwischen Objekt- und Funktionszeiger hin und> her casten, und void* gehört nunmal zu den Objektzeigern.
Heiß auf deutsch, dass ich nicht in meiner Union nur einen
void(*)(void)-Funktionspointer und einen void-Benötige und das wars?
ersetzt.
Jetzt bekomme ich etliche Compilerfehler; z.B.
1
argument of type "int" is incompatible with parameter of type
Wenn ich eben nur ein Integer übergebe. (Missbrauch des Pointers. Dann
steht eben keine Adresse in dem Pointer, sondern eine Zahl)
Hab dann die union erweitert:
Moni schrieb:> Wenn ich eben nur ein Integer übergebe. (Missbrauch des Pointers. Dann> steht eben keine Adresse in dem Pointer, sondern eine Zahl)
Jaja, "all the world is a Vax" und "wo ein `int' reinpasst, passt
immer auch ein Zeiger rein, und umgekehrt".
So hat man in den 1970er Jahren des letzten Jahrtausends programmiert.
Mit aus solchen Schlampereien vergleichbaren Bugs hat man ganze
Marssonden explodieren lassen ... Die Zeiten sind vorbei.
> Hab dann die union erweitert:>
1
typedefunion{
2
>intInteger;
3
>void*obj_p;
4
>void(*func_p)(void);
5
>}DataLimpet;
, was aber nichts gebrachtg hat. ;(
Definiere "nichts gebracht". Das wäre genau die richtige Methode.
Jörg Wunsch schrieb:> Hau endlich deine <zensiert> Casts raus und lern, wie man eine union> richtig benutzt!
Ja wie denn!?
Die Beispiele, die bis jetzt gepostet wurden, sind ja auch nicht
richtig/funktionsfähig...
Jörg Wunsch schrieb:> x.Integer = 3;
Geht! - Aber wie geht es bei den Funktionen als Übergabeparameter?#
Muss ich mir dann immer eine Hilfvariable anlegen?
Karl Heinz Buchegger schrieb:> Denk zb an qsort.> Der reicht auch seinen unbekannten zu sortierenden Datentyp einfach nur> an die vom Benutzer zu schreibende Compare-Funktion durch.
aber gibt überhaupt kein Grund funktionszeiger zu sortieren. Ich denke
man kann sie dort auch nicht übergeben
Moni schrieb:> Geht! - Aber wie geht es bei den Funktionen als Übergabeparameter?> Muss ich mir dann immer eine Hilfvariable anlegen?
In Standard-C ja, GCC könnte das auch per typecast bei der
Argumentübergabe lösen. Aber die Hilfsvariable kostet kein
Brot, und wenn du schon Schwierigkeiten hast, einen union-Zugriff
an sich zu verstehen, dann würde ich dir nicht zum GCCismus raten.
> help1.obj_p= ((void*)&f);
Der Cast ist überflüssig, zumindest in C (C++ sieht das anders).
Das zweite Klammerpaar ist auch überflüssig.
> help1.func_p= func1;
Funktioniert nur, wenn func1 auch wirklich so deklariert ist:
1
voidfunc1(void);
Andernfalls ist hier ein Typecast erforderlich:
1
help3.func_p=(void(*)(void))func1;
Die Klammeritis kann man durch ein sinnvolles typedef entflechten.
> func(help1, help2, help3);
Auf irgendeinem Weg sollte func() allerdings jetzt noch wissen,
in welcher der drei unions welcher Inhalt tatsächlich drin steckt.
Jörg Wunsch schrieb:> In Standard-C ja, GCC könnte das auch per typecast bei der> Argumentübergabe lösen
Verwende Code Composer Studio. Weiß nicht, ob das mit GCC arbeitet.
Auf jeden Fall sollte der Code portierbar bleiben, von daher muss ich
dann wohl die Hilfsvariable verwenden.
Jörg Wunsch schrieb:> Der Cast ist überflüssig, zumindest in C (C++ sieht das anders).> Das zweite Klammerpaar ist auch überflüssig.
War eigentlich nur Copy & Paste.
lcher der drei unions welcher Inhalt tatsächlich drin steckt.
Jörg Wunsch schrieb:> Andernfalls ist hier ein Typecast erforderlich:
Ok. Notiert.
Jörg Wunsch schrieb:> Auf irgendeinem Weg sollte func() allerdings jetzt noch wissen,> in welcher der drei unions welcher Inhalt tatsächlich drin steckt.
Das weiß sie nicht. Streng genommen befindet sich func in einem anderen
C-File und speichert dort meine Datenklette lokal ab.
Eine dort befindliche func2() ruft eine weitere funcX auf und gibt ihr
wieder meine Datenklette mit. Die weiß aber dann, was drin
steckt/stecken muss. ;)
Jörg Wunsch schrieb:> Wenn nicht, dann bist du hier im falschen Forum. ;-)
Ja, dass ist hier (im Forum) etwas doof strukturiert.
Code Composer Studio würde dann ja weder in das Forum "µC & Elektronik"
passen, denn es ist keine Hardware. Auch nicht in die "Codesammlung",
denn es ist eben kein Codeprojekt, dass man publiziert. Ebensowenig
gehört es in die "PC-Programmierung".
In diesem Forum (GCC) habe ich schon IAR und Code Composer Studio
gelesen, also habe ich mich hier mit meiner Frage "heimisch" gefühlt. ;)
Vielleicht wäre das ein Tipp ein Forum: µC-Programmierung oder Embedded
Programmierung einzuführen!?
Moni schrieb:> Jörg Wunsch schrieb:>> Wenn nicht, dann bist du hier im falschen Forum. ;-)>> Ja, dass ist hier (im Forum) etwas doof strukturiert.> Code Composer Studio würde dann ja weder in das Forum "µC & Elektronik"> passen, denn es ist keine Hardware.
Wieso sollte es die sein müssen? Laut Beschreibung ist das Forum "Für
alle Fragen rund um Mikrocontroller und Elektronik", und da gehört auch
die Programmierung eines µC dazu. Das ist also quasi das allgemeinste
Forum, das man verwenden kann, wenn man eine Frage hat, die in kein
spezifischeres passt.
> In diesem Forum (GCC) habe ich schon IAR und Code Composer Studio> gelesen, also habe ich mich hier mit meiner Frage "heimisch" gefühlt. ;)
Wenn du da eine Frage postest, ohne einen Compiler anzugeben, mußt du
aber davon ausgehen, daß jeder stillschweigend von GCC ausgeht.
Moni schrieb:> Geht! - Aber wie geht es bei den Funktionen als Übergabeparameter?#> Muss ich mir dann immer eine Hilfvariable anlegen?
Wenn dein Compiler C99 unterstützt (das sollte nach 12 Jahren fast
jeder), kannst du die expliziten Hilfsvariablen durch so genannte
Compound-Literals einsetzen:
1
#include<stdio.h>
2
3
typedefunion{
4
intinteger;
5
void*obj_p;
6
void(*func_p)(void);
7
}DataLimpet;
8
9
voidfunc1(inti,floatf){
10
printf("%d %f\n",i,f);
11
}
12
13
voidfunc(DataLimpetp1,DataLimpetp2,DataLimpetp3)
14
{
15
inti=p1.integer;
16
floatf=*(float*)p2.obj_p;
17
18
((void(*)(int,float))p3.func_p)(i,f);
19
}
20
21
intmain(void)
22
{
23
floatf=42.0;
24
25
// Funktionsaufruf mit drei Compound-Literals als Argumente
26
func(
27
(DataLimpet){.integer=3},
28
(DataLimpet){.obj_p=&f},
29
(DataLimpet){.func_p=(void(*)(void))func1}
30
);
31
return0;
32
}
Speicher oder Ausführungszeit wird dadurch wahrscheinlich nicht gespart,
da der Compiler trotzdem (anonyme) Variablen auf dem Stack anlegt. Der
Vorteil besteht hauptsächlich darin, dass man sich keine Namen für die
temporären Union-Variablen ausdenken muss.
PS: Vielleicht hat Jörg das gemeint, als er schrieb:
> GCC könnte das auch per typecast bei der Argumentübergabe lösen.
Streng genommen handelt es sich bei den Compound-Literals aber nicht um
Typecasts, auch wenn der linke Teil syntaktisch gleich aussieht.