Forum: Compiler & IDEs Ein Datentyp für alles


von Moni (Gast)


Lesenswert?

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
typedef void* DataLimpet;

Bei Pointern auf int, char, structs, usw. scheint es zu gehen:
1
Test(DataLimpet limpet)
2
{
3
  unsigned char* value;
4
  value = ((unsigned char*)limpet);
5
}


Aber wenn im void* ein Funktionspointer steckt, mosert der Compiler
mit "invalid type conversion":
1
typedef void (*Callback)(void);
2
Test(DataLimpet limpet)
3
{
4
  Callback value;
5
  value = ((Callback)limpet);
6
}


Was mache ich falsch?

Danke.

Liebe Gruß
Moni

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

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.

von Moni (Gast)


Lesenswert?

Und wie löse ich dann dieses Dilemma?

von MicroSD (Gast)


Lesenswert?

Moni schrieb:
> Und wie löse ich dann dieses Dilemma?

union aus allen benötigten Datentypen anlegen.

von Moni (Gast)


Lesenswert?

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?

von MicroSD (Gast)


Lesenswert?

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.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

MicroSD schrieb:

> Den Typ gibt es: eine union aus Daten- und Funktionspointer.

Konkret:
1
typedef union {
2
  void *obj_p;
3
  void (*func_p)(void);
4
} generic_p;

von Random .. (thorstendb) Benutzerseite


Lesenswert?

So ne union hat weitere Vorteile, wenn du alle benötigten Datentypen 
reinpackst, ersparst du dir das Casten, was die Lesbarkeit erhöht.

von Moni (Gast)


Lesenswert?

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

von Peter (Gast)


Lesenswert?

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.

von Moni (Gast)


Lesenswert?

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.

von Floh (Gast)


Lesenswert?

Moni schrieb:
> Die Funktion, die die den unbekannten
> Datentyp mitbekommt, nicht.

Was soll die Funktion mit einem "Etwas" machen, dessen Typ sie nicht 
weiß?

von Andreas S. (Firma: Schweigstill IT) (schweigstill) Benutzerseite


Lesenswert?

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.

von Karl H. (kbuchegg)


Lesenswert?

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.

von Rolf Magnus (Gast)


Lesenswert?

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.

von Moni (Gast)


Lesenswert?

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?

von Moni (Gast)


Lesenswert?

Hab jetzt mein
1
typedef void* DataLimpet;
 durch
1
typedef union {
2
  void *obj_p;
3
  void (*func_p)(void);
4
} DataLimpet;
 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:
1
typedef union {
2
  int Integer;
3
  void *obj_p;
4
  void (*func_p)(void);
5
} DataLimpet;
, was aber nichts gebrachtg hat. ;(

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

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
typedef union {
2
>   int Integer;
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.

von Oliver (Gast)


Lesenswert?

Das folgende tut es bei mir.
1
typedef union {
2
  int Integer;
3
  void *obj_p;
4
  void (*func_p)(void);
5
} DataLimpet; 
6
7
void func1(){}
8
9
void func(DataLimpet p1, DataLimpet p2, DataLimpet p3)
10
{
11
  int i = p1.Integer;
12
  float f = *((float*)(p2.obj_p));
13
14
  p3.func_p();
15
16
}
17
18
19
int main (void)
20
{  
21
22
  int i=3;
23
  float f=42.0;
24
25
  func((DataLimpet)i, (DataLimpet)((void*)&f), (DataLimpet)func1);
26
27
  return 0;
28
}

Ist aber irgedwie ziemlich ...

Oliver

von Moni (Gast)


Lesenswert?

Bei mir nicht. Bei mir bring der Compiler immer den Fehler
1
cast to type "DataLimpet" is not allowed

Oliver schrieb:
> Ist aber irgedwie ziemlich ...

Ziemlich was?

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Moni schrieb:
> Bei mir nicht. Bei mir bring der Compiler immer den Fehler
>
1
cast to type "DataLimpet" is not allowed

Hau endlich deine <zensiert> Casts raus und lern, wie man eine union
richtig benutzt!

von Moni (Gast)


Lesenswert?

Und wenn ich so was habe:
1
typedef union {
2
  int Integer;
3
  void *obj_p;
4
  void (*func_p)(void);
5
} DataLimpet; 
6
7
int main (void)
8
{  
9
  DataLimpet x;
10
  int i=3;
11
12
  x = i;
13
}

Kommt vom Compiler folgende Fehlermeldung:
1
a value of type "int" cannot be assigned to an entity of type "DataLimpet"

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

x.Integer = 3;

von Moni (Gast)


Lesenswert?

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

von Moni (Gast)


Lesenswert?

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?
1
int main (void)
2
{  
3
  int i=3;
4
  float f=42.0;
5
  DataLimpet help1;
6
  DataLimpet help3;
7
  DataLimpet help2;
8
9
  help1.Integer = i;
10
  help1.obj_p= ((void*)&f);
11
  help1.func_p= func1;
12
13
  func(help1, help2, help3);
14
15
  return 0;
16
}

von Peter (Gast)


Lesenswert?

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

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

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
void func1(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.

von Moni (Gast)


Lesenswert?

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

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Moni schrieb:
> Verwende Code Composer Studio. Weiß nicht, ob das mit GCC arbeitet.

Wenn nicht, dann bist du hier im falschen Forum. ;-)

von Moni (Gast)


Lesenswert?

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

von Rolf Magnus (Gast)


Lesenswert?

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.

von Yalu X. (yalu) (Moderator)


Lesenswert?

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
typedef union {
4
  int integer;
5
  void *obj_p;
6
  void (*func_p)(void);
7
} DataLimpet; 
8
9
void func1(int i, float f) {
10
  printf("%d %f\n", i, f);
11
}
12
13
void func(DataLimpet p1, DataLimpet p2, DataLimpet p3)
14
{
15
  int i = p1.integer;
16
  float f = *(float*)p2.obj_p;
17
18
  ((void (*)(int, float))p3.func_p)(i, f);
19
}
20
21
int main (void)
22
{  
23
  float f = 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
  return 0;
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.

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.