mikrocontroller.net

Forum: Compiler & IDEs Menu LCD Pointer - Funktionsaufruf


Autor: Stier (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

bin an einem Menü mit Pointern dran, dass switchen im Menü funktioniert 
schon sehr gut. Was bei mir noch nicht klappt ist, dass er die 
Funktionen nicht aufruft.
...
typedef struct def_menustruktur {
    const int8_t *text;
    int8_t prev;
    int8_t next;
    int8_t u_prev;
    int8_t u_next;
    void (*funktion)(void);
} menustruktur;
...
void menu_verlassen(void);

const uint8_t m_100[] PROGMEM = "1. Menu";
const uint8_t m_200[] PROGMEM = "2. Menu";
const uint8_t m_210[] PROGMEM = "2.1 Menu";
const uint8_t m_220[] PROGMEM = "2.2 Menu";
const uint8_t m_300[] PROGMEM = "3. Verlassen";

menustruktur test_m[] PROGMEM = {
    {m_100, 4, 1, 0, 0},
    {m_200, 0, 4, 3, 2},
        {m_210, 3, 3, 2, 2},
        {m_220, 2, 2, 3, 3},
    {m_300, 1, 0, 4, 4, menu_verlassen}
}

void menu_verlassen(void) {
    menuAktiv = false;
    lcd_clear();
}


Funktionsaufruf mache ich so:
...
state = pgm_read_word(&test_m[state].prev);
...
pgm_read_word(&test_m[state].funktion)

Wo ist mein Denkfehler?

Autor: Klaus Wachtler (mfgkw)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Mit einer vernünftigen Beschreibung und ORIGINAL-Quelltext
könnte man aus dem Problem vielleicht noch etwas machen.

Autor: Stier (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
naja ich möchte mit

pgm_read_word

die Funktion aufrufen die in m_300[] hinterlegt ist.

Autor: Klaus Wachtler (mfgkw)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
pgm_read_word(&test_m[state].funktion) liefert dir bestenfalls
einen Zeiger auf eine Funktion, ruft sie aber sicher nicht auf.
Dazu fehlt noch der cast auf den richtigen Typ (Zeiger auf
Funktion, die <parameterliste> bekommt und <Rückgabetyp> liefert)
sowie die runden Kalmmern, die ein Funktionsaufruf braucht.

Autor: Klaus Wachtler (mfgkw)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Außer bei dem letzten Eintrag fehlen übrigens die Zeiger auf
die Funktionen, und überhaupt ist der Quelltext doof zu lesen.
Mies formatiert, unvollständig, nicht kompilierbar.

Autor: Stier (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert

Autor: Klaus Wachtler (mfgkw)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Na und?
Die Bruchstücke dort funktionieren für sich alleine auch nicht.
Was soll mir der Link sagen?

Du musst dich schon mit C beschäftigen, wenn du mit
Funktionszeigern arbeiten willst, und nicht nur irgendwelche
Fragmente zusammenkopieren.

Autor: Stier (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Klaus Wachtler schrieb:
> Mies formatiert, unvollständig, nicht kompilierbar.

Mies formatiert?
unvollständig -> ja ist richtig, gut erkannt
nicht kompilierbar -> richtig, da er ja nicht vollständig ist

hat schon einen Grund warum ich nicht alles gepostet habe!

Klaus Wachtler schrieb:
> Was soll mir der Link sagen?
Klaus Wachtler schrieb:
> ORIGINAL-Quelltext

Autor: Klaus Wachtler (mfgkw)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Wenn in den paar Zeilen von dir schon 2 Semikolon fehlen, von
verwendeten Variablen keine Definition oder Initialisierung zu
sehen ist, und als Fehlerbeschreibung nur ein "geht nicht",
dann brauche ich doch gar nicht groß Fehler zu suchen.

Wenn es zu groß ist zum Posten, dann liegt es an dir, das
Ganze auf ein kleines übersichtliches Beispiel zu
reduzieren, zu dem man dann Fragen beantworten kann.

Abgesehen davon, daß meine Aussage nach wie vor steht:
in der letzten gezeigten Zeile hast du nur ein Word,
das erst gecastet werden muß und dann mit Klammern erst
einen Funktionsaufruf gibt.
Wenn dann auch noch ein Semikolon dahinter spendiert wird...

Tip:
So etwas kann man am PC wesentlich einfacher erst mal üben.
Wenn man dann die Zeiger auf Funktionen verstanden hat,
packt man es in einen MC, in den man nicht so gut reinschauen
kann zw. Debuggen.

Das spart dir und uns viel Zeit.
Es gibt hier etliche Leute, die gerne helfen. Mich eingeschlossen.
Aber seine Zeit hat keiner gestohlen, also kannst du auch den
Hintern hochkriegen und nicht einfach irgendwas zusammenkopieren
und hier dann fertigstellen lassen.

Den Hauptfehler habe ich dir doch schon gesagt.

Autor: Klaus Wachtler (mfgkw)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Stier schrieb:
> Mies formatiert?

naja, es gibt viel schlimmere.

Autor: Stier (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
(uint8_t)pgm_read_word(&test_m[state].funktion);

was du mit Funktionsklammern meinst verstehe ich nicht, wenn du das 
meinst:

(uint8_t)pgm_read_word(&test_m[state].funktion());

ist das Falsch.

Autor: Klaus Wachtler (mfgkw)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
ja, das ist falsch.
Du musst erst in den Typ "Zeiger auf Funktion ohne Parameer und
void-Rückgabewert" casten, dann Klammern drumrum und das Ganze
dann mit einem Klammerpaar dahinter aufrufen.

Autor: Stier (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
(void)pgm_read_word(&test_m[state].funktion)();

das geht auch nicht! Und was du sonst noch meinen könntest, fehlt mir 
nix mehr ein!

Autor: Klaus Wachtler (mfgkw)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich habe jetzt keine Lust, einen AVR dafür auszupacken.
Aber auf einem PC kann man das wie gesagt auch schön simulieren.
Da sind Zeiger nur halt 4 Byte groß und deshalb nehme ich eine uint32_t:
#include <stdio.h>
#include <stdint.h>


void f()
{
  printf( "hi\n" );
}

int main( int nargs, char **args )
{
  uint32_t    fi = (uint32_t)f;

  ((void(*)())fi)(); // Aufruf von f über den Zeiger bzw. die ganze Zahl fi

  return 0;
}

fi ist jetzt uneleganterweise eine ganzzahlige Variable, das
entspricht bei dir dem uint16_t-Ausdruck
pgm_read_word(&test_m[state].funktion).

fi ist also formal nur eine ganze Zahl (auch wenn ich weiß,
daß sich dahinter in Wirklichkeit die Adresse einer Funktion
verbirgt).

Aus dieser ganzen Zahl muß jetzt für den Compiler erstmal
ein Zeiger auf eine Funktion gemacht werden.
Dazu schreibt man den richtigen Typ in Klammern davor,
gemeinhin als cast bekannt (ähnlich wie ich bei der Initialisierung
von fi aus der Adresse der Funktion f mit (uint32_t) davor
eine ganze Zahl gemacht hatte, nur jetzt halt in die andere
Richtung).

Was ist der richtige Typ?
Das wäre in Worten "Zeiger (also: *) auf eine Funktion
(deshalb die runden Klammern am Ende), die nichts bekommt
(deshalb die runden Klammern am Ende leer) und nichts
zurückliefert (deshalb void vornedran)".
In C:
  void(*)()
Das wäre also der Typ, in den gecastet werden muss.
Das erste Klammerpaar um * ist nötig wegen der
Auswertungsreihenfolge, aus void und * würde der Compiler
sonst einen Typ void* machen und mit dem Rest dann nicht mehr
klar kommen.

Dieser Typ in Klammern vor den uint16_t- bzw. uint32_t-
Ausdruck macht aus der ganzen Zahl den Zeiger auf die Funktion:
  (void(*)())fi

Das ist insoweit erstmal nur die Adresse der Funktion, aber noch
kein Aufruf.
Dazu muß man das bisherige Gebilde klammern (wieder wegen der 
Auswertungsreihenfolge):
  ((void(*)())fi)

und -jetzt kommt's- mit einem Klammerpaar dahinter die
Funktion aufrufen:
  ((void(*)())fi)()

Sieht etwas kryptisch aus, aber ist halt C.

Autor: Klaus Wachtler (mfgkw)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Analog:
    exit;
ruft die Funktion exit nicht auf, sondern nimmt nur ihre Adresse
und macht nichts damit.

So dagegen:
    exit();
wird sie auch aufgerufen.

Autor: Stier (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
wow danke für deine Erklärung. Finde deine Formierung aber auch nicht 
gerade Optimal!

Autor: Stier (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
mal eine andere Frage:

Wieso vergleicht er bei mir nicht zwei uint8_t miteinander?
typedef struct {
    uint8_t sec;
    uint8_t minute;
    uint8_t hour;
} time_t;

time_t time0;
time_t time1;

time0.minute = 5;
time0.hour = 2;
time1.minute = 5;
time0.hour = 2;

if(time0.hour == time1.hour && time0.minute == time1.minute) {
    // tue was
}


Autor: Stier (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
ok eigener Fehler funktioniert doch

Autor: Klaus Wachtler (mfgkw)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
erstens macht man für eine neue Frage einen neuen Thread auf,
zweitens gefällt die meine Formierung nicht, und
drittens soll das letzte time0.hour = 2 wohl eher time1.hour = 2 heißen?

Autor: Stier (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Klaus Wachtler schrieb:
> erstens macht man für eine neue Frage einen neuen Thread auf,
> zweitens gefällt die meine Formierung nicht, und
> drittens soll das letzte time0.hour = 2 wohl eher time1.hour = 2 heißen?

1. Wollte ich das Forum hier nicht zu müllen
2. Genau deine Formierung find ich nicht toll, aber was hat es mit der 
Frage zu tun?
3. Sollte es

Autor: Stier (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
menustruktur test_m[] PROGMEM = {
    {m_100, 4, 1, 0, 0},
    {m_200, 0, 4, 3, 2},
        {m_210, 3, 3, 2, 2},
        {m_220, 2, 2, 3, 3},
    {m_300, 1, 0, 4, 4, test(bla)}
}

int main( int nargs, char **args )
{
  uint32_t    fi = (uint32_t)f;

  ((void(*)())fi)(); // Aufruf von f über den Zeiger bzw. die ganze Zahl fi

  return 0;
}

wie genau funktioniert es, wenn ich Parameter an der Funktion habe? test 
hat parameter bla, wie genau ändert sich der Aufruf der Funktion?

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

Bewertung
0 lesenswert
nicht lesenswert
Stier schrieb:
>
> menustruktur test_m[] PROGMEM = {
>     {m_100, 4, 1, 0, 0},
>     {m_200, 0, 4, 3, 2},
>         {m_210, 3, 3, 2, 2},
>         {m_220, 2, 2, 3, 3},
>     {m_300, 1, 0, 4, 4, test(bla)}
> }
> 
> int main( int nargs, char **args )
> {
>   uint32_t    fi = (uint32_t)f;
> 
>   ((void(*)())fi)(); // Aufruf von f über den Zeiger bzw. die ganze Zahl
> fi
> 
>   return 0;
> }
> 
>
> wie genau funktioniert es, wenn ich Parameter an der Funktion habe? test
> hat parameter bla, wie genau ändert sich der Aufruf der Funktion?

Was dir bis jetzt noch keiner gesagt hat:
Mach dir um Himmels Willen einen typedef für den Datentyp des 
Funktionspointers. Ansonsten bringt dich die komplexe Syntax um!

Die wichtigsten Dinge über Funktionspointer findest du hier
http://www.mikrocontroller.net/articles/FAQ#Funktionszeiger
zusammengestellt. Und natürlich auch ausführlich in deinem C-Buch.

Autor: Stier (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Leider stehe ich etwas auf dem Schlauch:
typedef struct def_menustruktur {
    const int8_t *text;
    int8_t prev;
    int8_t next;
    int8_t u_prev;
    int8_t u_next;
    void (*funktion)(void);
} menustruktur;

Hier definiere ich das die Funktionen bei

{m_300, 1, 0, 4, 4, test}

keine Argumente enthält. Doch einige Funktionen brauchen ein Argument. 
Ich kann ja nicht void (*funktion)(void); einfach ändern.

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

Bewertung
0 lesenswert
nicht lesenswert
Stier schrieb:

> Hier definiere ich das die Funktionen bei
>
> {m_300, 1, 0, 4, 4, test}
>
> keine Argumente enthält. Doch einige Funktionen brauchen ein Argument.
> Ich kann ja nicht void (*funktion)(void); einfach ändern.

Das kannst du nicht nur, das musst du sogar.
Im verlinkten Artikel steht ausdrücklich: Alle Funktionen müssen 
dieselbe Signatur haben.

Wenn also einige Funktionen ein Argument brauchen, dann musst du deinen 
Funktionspointer darauf auslegen. Das bedeutet allerdings dann auch, 
dass alle Funktionen ein Argument annehmen müssen, auch die die 
eigentlich keines brauchen. Das ist aber auch kein großes Problem, wenn 
sie ein Argument mitbekommen und einfach nichts damit tun.

Autor: Stier (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Karl heinz Buchegger schrieb:
> Wenn also einige Funktionen ein Argument brauchen, dann musst du deinen
> Funktionspointer darauf auslegen. Das bedeutet allerdings dann auch,
> dass alle Funktionen ein Argument annehmen müssen, auch die die
> eigentlich keines brauchen. Das ist aber auch kein großes Problem, wenn
> sie ein Argument mitbekommen und einfach nichts damit tun.

ok habs nun verstanden

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

Bewertung
0 lesenswert
nicht lesenswert
Stier schrieb:
> Karl heinz Buchegger schrieb:
>> Wenn also einige Funktionen ein Argument brauchen, dann musst du deinen
>> Funktionspointer darauf auslegen. Das bedeutet allerdings dann auch,
>> dass alle Funktionen ein Argument annehmen müssen, auch die die
>> eigentlich keines brauchen. Das ist aber auch kein großes Problem, wenn
>> sie ein Argument mitbekommen und einfach nichts damit tun.
>
> ok habs nun verstanden

Alternativ kann man sich natürlich auch immer noch die Dinge 
zurechtcasten wie man sie braucht, aber ich warne ausdrücklich davor. 
Sowas führt ganz schnell ins Chaos und zu schwer zu findenden Fehlern.

Eine andere Möglichkeit wäre noch zb. den Mechanismus mit dem an main 
eine beliebige Anzahl an Argumenten übergeben wird zu kopieren. 
Inwiefern das bei einer Handvoll Funktionen mit einer überschaubaren 
Anzahl an Parametern sinnvoll ist, sei jetzt erst mal dahingestellt.

Und dann gibt es ja auch noch globale Variablen mit denen man einen 
Argument Passing Mechanimus inszenieren könnte. Aber auch hier 
wiederrum: Sobald Menüs rekursiv andere Menüs für Teilaufgaben benutzen 
wirds schnell unübersichtlich und fehleranfällig.

Und da Menüsysteme nichts zeitkritisches sind, ist es auch normalerweise 
völlig unerheblich ob eine Funktion jetzt ein paar Parameter bekommt, 
die sie eigentlich gar nicht benötigt und nur deshalb hat, weil alle 
Funktionen gleich aussehen müssen.

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.