C - Funktionspointer weiter durchreichen

OP #5758611
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?
Gast #5758617
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.
Moderator Persönliche Seite #5758624
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.
OP #5758636
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.
Moderator Persönliche Seite #5758637
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.
OP #5758647
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
}

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