mikrocontroller.net

Forum: PC-Programmierung C - Funktionspointer weiter durchreichen


Autor: Max M. (maxmicr)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Generell geht es hier um den ESP8266, aber die Frage lässt sich denke 
ich allgemein auf C bzw. C++ übertragen.

Ich habe eine Klasse Wifi die eine Methode hat, mit der man das 
Wifi-Netzwerk scannen kann. Sobald die umgebenen Netzwerke gescannt 
worden sind, wird eine Callback-Funktion in Form eines Funktionspointers 
aufgerufen, der der Methode zuvor übergeben wurde.

D.h. in meiner main-Datei ergibt sich folgendes:
void scan_done_cb(void *arg, STATUS status) {
  // Do stuff after scanning is done
}

int main() {
 Wifi wifi = Wifi();
 wifi.request_scan(scan_done_cb);
}

In meiner Wifi-Klasse sieht das dann so aus:
void ICACHE_FLASH_ATTR Wifi::scan_wifi_networks(void (*scan_done_cb)(void*, STATUS)) {
    struct scan_config sc;
    sc.ssid = NULL;
    sc.bssid = NULL;
    sc.channel = 0;
    sc.show_hidden = 1;

    wifi_station_scan(&sc, &(*scan_done_cb)); // ???
}

Die mit den 3 Fragezeichen markierte Zeile habe ich bereits in drei 
Varianten ausprobiert, die alle einwandfrei kompiliert haben aber den 
Controller innerhalb von einer Sekunde (eben sobald die Methode 
aufgerufen wird) zum Absturzt bringen. Die drei Varianten sind:
wifi_station_scan(&sc, scan_done_cb);
wifi_station_scan(&sc, *scan_done_cb);
wifi_station_scan(&sc, &(*scan_done_cb));

Wie genau übergebe ich den Funktionspointer nun korrekt?

Autor: Dr. Sommer (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Höchstwahrscheinlich so:
wifi_station_scan(&sc, scan_done_cb);
Es wäre aber hilfreich, die Definition von wifi_station_scan zu sehen. 
Was ist request_scan ?

Der Absturz rührt vermutlich woanders her.

Autor: Max M. (maxmicr)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Dr. Sommer schrieb:
> Was ist request_scan ?

Achso, ups. Das sollte eigentlich scan_wifi_networks heißen, sry.

Dr. Sommer schrieb:
> Es wäre aber hilfreich, die Definition von wifi_station_scan zu sehen.

Anbei die Definition aus der Doku.

Autor: Jörg W. (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Max M. schrieb:
> Wie genau übergebe ich den Funktionspointer nun korrekt?

Die erste Variante, also einfach den Zeiger benennen (scan_done_cb).

Wenn dabei etwas abstürzt, ist was anderes foul.

Schöner schreibt sich der Typen-Krams natürlich mit einem typedef, das 
sieht weniger verwirrend aus.

Eine globale Funktion würde ich nach Möglichkeit nicht genauso benennen 
wie einen formalen Parameter.

Autor: Dr. Sommer (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Mach das doch so:
void ICACHE_FLASH_ATTR Wifi::scan_wifi_networks(scan_done_cb_t scan_done_cb)

Autor: Max M. (maxmicr)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Anbei noch die Definition für scan_done_cb_t aus der Doku.

Autor: A. K. (prx)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Allgemein: Man sollte lieber nicht davon ausgehen, dass ein void * immer 
auch einen function pointer aufnehmen kann.

Autor: Max M. (maxmicr)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Dr. Sommer schrieb:
> Mach das doch so:void ICACHE_FLASH_ATTR
> Wifi::scan_wifi_networks(scan_done_cb_t scan_done_cb)

Hm, klappt leider auch nicht:
void ICACHE_FLASH_ATTR Wifi::scan_wifi_networks(scan_done_cb_t scan_done_cb) {
    struct scan_config sc;
    sc.ssid = NULL;
    sc.bssid = NULL;
    sc.channel = 0;
    sc.show_hidden = 1;

    wifi_station_scan(&sc, scan_done_cb);
}
Fatal exception 28(LoadProhibitedCause):

Sobald ich die Zeile
wifi.scan_wifi_networks(scan_done_cb);

weglasse, stürzt der Controller nicht mehr ab.

Autor: Jörg W. (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
A. K. schrieb:
> Allgemein: Man sollte lieber nicht davon ausgehen, dass ein void * immer
> auch einen function pointer aufnehmen kann.

Davon darf man auch nicht ausgehen – aber ich sehe hier nicht, dass er 
das machen würde.

Wenn man einen "generischen" Funktionszeiger braucht, kann man
typedef void (*generic_func_p)(void);

benutzen und von / nach diesem casten.  Alle Funktionszeiger sind per 
definitionem per Cast ineinander überführbar, aber Casts zwischen 
Funktions- und Objektzeiger sind nicht statthaft. (Einfaches Beispiel, 
wo sowas total schief geht, ist ein AVR mit 256 KiB: der Funktionszeiger 
ist dort 24 Bit breit, der Objektzeiger nur 16 Bit.)

Schöner ist es natürlich immer ohne Cast; das ist hier aber der Fall.

: Bearbeitet durch Moderator
Autor: Dr. Sommer (Gast)
Datum:

Bewertung
1 lesenswert
nicht lesenswert
Max M. schrieb:
> Hm, klappt leider auch nicht:

Ja, ist auch kein Unterschied, nur schöner. Glaube nicht dass es am 
Funktionspointer liegt. Wenn du die Callback-Funktion komplett leer 
machst geht's bestimmt...

Autor: Max M. (maxmicr)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Dr. Sommer schrieb:
> Wenn du die Callback-Funktion komplett leer
> machst geht's bestimmt...

:D

Woher weißt du das?

P.S. Jetzt funktioniert es auch, hab mir die Doku nochmal angesehen und 
bemerkt, dass der Status überprüft wird.
Das hatte ich vergessen.

Die Callback-Funktion sieht so aus:
extern "C" void ICACHE_FLASH_ATTR scan_done_cb(void *arg, STATUS status) {
    if(status == OK) {
        char buf[100];
        struct bss_info *bssInfo = (struct bss_info*)arg;
        bssInfo = STAILQ_NEXT(bssInfo, next);

        os_printf("-------------- WIFI SCAN RESULTS --------------\r\n");
        while (bssInfo != NULL) {
            os_sprintf(buf, "%-32s %02X:%02X:%02X:%02X:%02X:%02X, ch %2d, auth %d, hid %d, rssi %d\n\r", 
                    bssInfo->ssid, 
                    bssInfo->bssid[0], bssInfo->bssid[1], bssInfo->bssid[2],
                    bssInfo->bssid[3], bssInfo->bssid[4], bssInfo->bssid[5],
                    bssInfo->channel,
                    bssInfo->authmode, bssInfo->is_hidden,
                    bssInfo->rssi);
            os_printf(buf);
            os_delay_us(200);
            bssInfo = STAILQ_NEXT(bssInfo, next);
        }
        os_printf("-------------- END --------------\r\n");
    } else
        os_printf("-------------- SCAN RESULT NOT OK --------------\r\n");
}

Autor: A. K. (prx)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Jörg W. schrieb:
> Davon darf man auch nicht ausgehen – aber ich sehe hier nicht, dass er
> das machen würde.

Stimmt. Schief geguckt.

Autor: Dr. Sommer (Gast)
Datum:

Bewertung
1 lesenswert
nicht lesenswert
Max M. schrieb:
> Woher weißt du das?

Weil ich vermutet habe, dass der Fehler im Callback liegt, aber der 
Callback an sich korrekt aufgerufen wurde.

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.

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