La conversione di un metodo C ++ in una funzione C con un argomento pointer è accettabile?

15

Uso C ++ su ESP-32. Quando registro un timer devo farlo:

timer_args.callback = reinterpret_cast<esp_timer_cb_t>(&SoundMixer::soundCallback);
timer_args.arg = this;

Qui il timer chiama soundCallback .

E la stessa cosa quando si registra un'attività:

xTaskCreate(reinterpret_cast<TaskFunction_t>(&SoundProviderTask::taskProviderCode), "SProvTask", stackSize, this, 10, &taskHandle);

Quindi il metodo viene avviato in un'attività separata.

GCC mi avvisa sempre di queste conversioni, ma funziona esattamente come previsto.

È accettabile nel codice di produzione? C'è un modo migliore per farlo?

    
posta val 12.08.2018 - 12:17
fonte

2 risposte

47

Un reinterpret_cast è sempre fishy a meno che tu non sappia esattamente cosa stai facendo. Qui, il tuo codice funziona solo a causa della convenzione di chiamata di GCC per i metodi C ++, ma questo ha un strong odore di comportamento non definito. In particolare non dovresti assumere che le funzioni dei membri siano in qualche modo compatibili con i normali puntatori di funzione.

L'approccio abituale sarebbe quello di definire invece una funzione compatibile con C con la firma appropriata, che internamente chiama il metodo C ++. Ad esempio:

extern "C" static void my_timer_callback(void* arg) {
  static_cast<SoundMixer*>(arg)->soundCallback();
}

Questo cast va bene perché stiamo ricominciando da void* al tipo di oggetto puntato.

dettagli:

  • extern "C" specifica il collegamento linguistico di questo funzione. Il collegamento linguistico influisce sul mangling dei nomi e sulla convenzione di chiamata della funzione. Le funzioni membro non possono avere il collegamento del linguaggio C. Il collegamento linguistico è in gran parte ortogonale rispetto al collegamento interno / esterno.

  • Per una richiamata la funzione può essere "privata", cioè avere un collegamento interno. Il codice C non fa mai riferimento alla callback per nome. Il frammento di codice sopra riportato specifica il collegamento interno tramite la parola chiave static (non un metodo statico!). In alternativa, la funzione potrebbe essere stata inserita in uno spazio dei nomi anonimo.

    Non sono completamente sicuro delle interazioni tra extern "C" e static (collegamento interno). Per esempio. [dcl.link] dice che "Tutti i tipi di funzione, i nomi di funzione con collegamento esterno ei nomi di variabili con linkage esterni hanno un collegamento linguistico." Interpreto questo in modo che il tipo di my_timer_callback abbia il collegamento al linguaggio C , ma che la sua funzione name non lo è.

  • Un static_cast è appropriato qui perché conosciamo il tipo reale di arg ma non possiamo esprimerlo all'interno del sistema di tipi. Al contrario, reinterpret_cast è appropriato quando vogliamo reinterpretare un pattern di bit, ad es. un puntatore a un tipo numerico.

  • Le funzioni non sono oggetti ordinari e le funzioni membro ancora meno. È possibile reinterpretare il cast tra i tipi di puntatore a funzione purché la funzione sia invocata solo attraverso il suo tipo reale (e analogamente per i puntatori di funzioni dei membri). Se è possibile lanciare i puntatori di funzione ad altri tipi (ad esempio puntatori di oggetti o puntatori void) è definito dall'implementazione ( sfondo ). Sui cast di POSIX tra i puntatori di funzione e void* sono permessi in modo che dlsym() possa funzionare. Altri cast che coinvolgono i puntatori di funzione (membro) non sono definiti. In particolare, i cast tra funzioni membro e puntatori di funzione non sono possibili.

risposta data 12.08.2018 - 12:46
fonte
-2

Personalmente, l'approccio più compatibile, facile da implementare e facile da capire che ho trovato è semplicemente fornire una funzione "wrapper", compatibile con l'interfaccia C prevista, che internamente chiama il metodo (e, nel caso sia non statico, istanziare o utilizzare un'istanza esistente per farlo). Potrebbe essere visto come una sorta di variazione del modello di disegno dell'adattatore.

    
risposta data 12.08.2018 - 21:31
fonte

Leggi altre domande sui tag