www.mikrocontroller.net

Forum: Compiler & IDEs Ein Datentyp für alles


Autor: Moni (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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
typedef void* DataLimpet;

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


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


Was mache ich falsch?

Danke.

Liebe Gruß
Moni

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Moni (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Und wie löse ich dann dieses Dilemma?

Autor: MicroSD (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Moni schrieb:
> Und wie löse ich dann dieses Dilemma?

union aus allen benötigten Datentypen anlegen.

Autor: Moni (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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?

Autor: MicroSD (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
MicroSD schrieb:

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

Konkret:
typedef union {
  void *obj_p;
  void (*func_p)(void);
} generic_p;

Autor: Random ... (thorstendb) Benutzerseite
Datum:

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

Autor: Moni (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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...

Autor: Peter (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Moni (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Floh (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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ß?

Autor: Andreas Schweigstill (Firma: Schweigstill IT) (schweigstill) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Rolf Magnus (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Moni (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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?

Autor: Moni (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hab jetzt mein
typedef void* DataLimpet;
 durch
typedef union {
  void *obj_p;
  void (*func_p)(void);
} DataLimpet;
 ersetzt.

Jetzt bekomme ich etliche Compilerfehler; z.B.
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:
typedef union {
  int Integer;
  void *obj_p;
  void (*func_p)(void);
} DataLimpet;
, was aber nichts gebrachtg hat. ;(

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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:
>
typedef union {
>   int Integer;
>   void *obj_p;
>   void (*func_p)(void);
> } DataLimpet;
, was aber nichts gebrachtg hat. ;(

Definiere "nichts gebracht".  Das wäre genau die richtige Methode.

Autor: Oliver (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Das folgende tut es bei mir.
typedef union {
  int Integer;
  void *obj_p;
  void (*func_p)(void);
} DataLimpet; 

void func1(){}

void func(DataLimpet p1, DataLimpet p2, DataLimpet p3)
{
  int i = p1.Integer;
  float f = *((float*)(p2.obj_p));

  p3.func_p();

}


int main (void)
{  

  int i=3;
  float f=42.0;

  func((DataLimpet)i, (DataLimpet)((void*)&f), (DataLimpet)func1);

  return 0;
}

Ist aber irgedwie ziemlich ...

Oliver

Autor: Moni (Gast)
Datum:

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

Oliver schrieb:
> Ist aber irgedwie ziemlich ...

Ziemlich was?

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

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

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

Autor: Moni (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Und wenn ich so was habe:
typedef union {
  int Integer;
  void *obj_p;
  void (*func_p)(void);
} DataLimpet; 

int main (void)
{  
  DataLimpet x;
  int i=3;

  x = i;
}

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

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
x.Integer = 3;

Autor: Moni (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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...

Autor: Moni (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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?
int main (void)
{  
  int i=3;
  float f=42.0;
  DataLimpet help1;
  DataLimpet help3;
  DataLimpet help2;

  help1.Integer = i;
  help1.obj_p= ((void*)&f);
  help1.func_p= func1;

  func(help1, help2, help3);

  return 0;
}

Autor: Peter (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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:
void func1(void);

Andernfalls ist hier ein Typecast erforderlich:
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.

Autor: Moni (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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. ;)

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

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

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

Autor: Moni (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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!?

Autor: Rolf Magnus (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Yalu X. (yalu) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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:
#include <stdio.h>

typedef union {
  int integer;
  void *obj_p;
  void (*func_p)(void);
} DataLimpet; 

void func1(int i, float f) {
  printf("%d %f\n", i, f);
}

void func(DataLimpet p1, DataLimpet p2, DataLimpet p3)
{
  int i = p1.integer;
  float f = *(float*)p2.obj_p;

  ((void (*)(int, float))p3.func_p)(i, f);
}

int main (void)
{  
  float f = 42.0;

  // Funktionsaufruf mit drei Compound-Literals als Argumente
  func(
      (DataLimpet){.integer = 3                    },
      (DataLimpet){.obj_p   = &f                   },
      (DataLimpet){.func_p  = (void (*)(void))func1}
      );
  return 0;
}

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

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.