Forum: PC-Programmierung C - Funktionspointer weiter durchreichen


von Max M. (maxmicr)


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:
1
void scan_done_cb(void *arg, STATUS status) {
2
  // Do stuff after scanning is done
3
}
4
5
int main() {
6
 Wifi wifi = Wifi();
7
 wifi.request_scan(scan_done_cb);
8
}

In meiner Wifi-Klasse sieht das dann so aus:
1
void ICACHE_FLASH_ATTR Wifi::scan_wifi_networks(void (*scan_done_cb)(void*, STATUS)) {
2
    struct scan_config sc;
3
    sc.ssid = NULL;
4
    sc.bssid = NULL;
5
    sc.channel = 0;
6
    sc.show_hidden = 1;
7
8
    wifi_station_scan(&sc, &(*scan_done_cb)); // ???
9
}

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:
1
wifi_station_scan(&sc, scan_done_cb);
2
wifi_station_scan(&sc, *scan_done_cb);
3
wifi_station_scan(&sc, &(*scan_done_cb));

Wie genau übergebe ich den Funktionspointer nun korrekt?

von Dr. Sommer (Gast)


Lesenswert?

Höchstwahrscheinlich so:
1
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.

von Max M. (maxmicr)


Angehängte Dateien:

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.

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


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.

von Dr. Sommer (Gast)


Lesenswert?

Mach das doch so:
1
void ICACHE_FLASH_ATTR Wifi::scan_wifi_networks(scan_done_cb_t scan_done_cb)

von Max M. (maxmicr)


Angehängte Dateien:

Lesenswert?

Anbei noch die Definition für scan_done_cb_t aus der Doku.

von (prx) A. K. (prx)


Lesenswert?

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

von Max M. (maxmicr)


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:
1
void ICACHE_FLASH_ATTR Wifi::scan_wifi_networks(scan_done_cb_t scan_done_cb) {
2
    struct scan_config sc;
3
    sc.ssid = NULL;
4
    sc.bssid = NULL;
5
    sc.channel = 0;
6
    sc.show_hidden = 1;
7
8
    wifi_station_scan(&sc, scan_done_cb);
9
}
1
Fatal exception 28(LoadProhibitedCause):

Sobald ich die Zeile
1
wifi.scan_wifi_networks(scan_done_cb);

weglasse, stürzt der Controller nicht mehr ab.

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


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
1
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
von Dr. Sommer (Gast)


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

von Max M. (maxmicr)


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:
1
extern "C" void ICACHE_FLASH_ATTR scan_done_cb(void *arg, STATUS status) {
2
    if(status == OK) {
3
        char buf[100];
4
        struct bss_info *bssInfo = (struct bss_info*)arg;
5
        bssInfo = STAILQ_NEXT(bssInfo, next);
6
7
        os_printf("-------------- WIFI SCAN RESULTS --------------\r\n");
8
        while (bssInfo != NULL) {
9
            os_sprintf(buf, "%-32s %02X:%02X:%02X:%02X:%02X:%02X, ch %2d, auth %d, hid %d, rssi %d\n\r", 
10
                    bssInfo->ssid, 
11
                    bssInfo->bssid[0], bssInfo->bssid[1], bssInfo->bssid[2],
12
                    bssInfo->bssid[3], bssInfo->bssid[4], bssInfo->bssid[5],
13
                    bssInfo->channel,
14
                    bssInfo->authmode, bssInfo->is_hidden,
15
                    bssInfo->rssi);
16
            os_printf(buf);
17
            os_delay_us(200);
18
            bssInfo = STAILQ_NEXT(bssInfo, next);
19
        }
20
        os_printf("-------------- END --------------\r\n");
21
    } else
22
        os_printf("-------------- SCAN RESULT NOT OK --------------\r\n");
23
}

von (prx) A. K. (prx)


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.

von Dr. Sommer (Gast)


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.

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.