Ein Datentyp für alles

Gast #2085966
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
Gast #2086070
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...
Gast #2086083
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.
(Firma: Schweigstill IT) Persönliche Seite #2086097
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.
Gast #2086335
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.
Gast #2086848
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?
Gast #2086927
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. ;(
Moderator Persönliche Seite #2086976
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.
Gast #2087091
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
Gast #2087333
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"
Gast #2087343
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
}
Moderator Persönliche Seite #2087385
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.
Gast #2087404
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. ;)
Gast #2087440
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!?
Gast #2088392
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.
Moderator #2089221
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.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren