5

Ho una libreria C che utilizza una struttura di puntatori di funzione per i callback. I callback saranno chiamati dal codice C.Quali tipi di funzioni C++ possono essere posizionati in un puntatore a funzione C?

extern "C" { 
typedef struct callbacks_t { 
    void (*foo) (const char*); 
    int (*bar) (int); 
} callbacks_t; 
}// extern C 

Quale genere di funzioni C++ posso tranquillamente luogo in quei puntatori a funzione di essere chiamato dalla libreria C? Funzioni membro statico? Funzioni del modello completamente specificate? Lambda non catturante?

g ++ a quanto pare mi consente di utilizzare tutto quanto sopra, ma metto in dubbio la sicurezza quando vengono utilizzate diverse convenzioni di chiamata e binding di linguaggio per le funzioni C e C++.

+1

_ "ma metto in dubbio la sicurezza quando vengono utilizzate diverse convenzioni di chiamata e binding di linguaggio per le funzioni C e C++" _ Non ci sono differenze come queste quando tutto è stato compilato in modo coerente per l'ambiente di destinazione. –

+3

Le funzioni membro statiche, le funzioni modello specializzate e i lambda non catturanti non possono ricevere una convenzione di chiamata "C" esterna così formalmente che non è buona. Ma in pratica dipende dal compilatore.E come problema di in-practice, il problema principale non è che non funzionerà, ma che alcuni compilatori possono produrre stupidi avvertimenti sul formale. –

+2

@ Cheersandhth.-Alf, quello non dovrebbe importare. La libreria C dovrebbe essere in grado di chiamare una funzione il cui nome viene manomesso dal compilatore C++ senza problemi. –

risposta

3

Ho una libreria C che utilizza una struttura di

Purtroppo, quando si definisce un puntatore a funzione a un tipo stdcall, gcc si ignora puntatori di funzioni per le richiamate. I callback saranno chiamati dal codice C.

biblioteca AC capisce solo C. Così si può solo passare indietro pensa che sono esplicitamente sostenuto e compreso da C.

Dal momento che non v'è definizione di convenzioni di chiamata per tutti i tipi di funzioni in C++ non è possibile in modo esplicito passare le funzioni C++. È possibile solo restituire la funzione C (quelle dichiarate esplicitamente con extern "C") e garantire che siano compatibili.

Come tutti i comportamenti non definiti questo può sembrare funzionare (come il passaggio di normali funzioni C++ o membri statici). Ma come tutti i comportamenti indefiniti è permesso lavorare. Semplicemente, puoi garantire che sia effettivamente corretto o portatile.

extern "C" { 
    typedef struct callbacks_t { 
     void (*foo) (const char*); 
     int (*bar) (int); 
    } callbacks_t; 

    // Any functions you define in here. 
    // You can set as value to foo and bar. 

}// extern C 

Quale genere di funzioni C++ posso tranquillamente mettere in quei puntatori a funzione di essere chiamato dalla libreria C?

Funzioni membro statico?

No. Ma questo succede a lavorare su molte piattaforme. Questo è un bug comune e morde le persone su alcune piattaforme.

Funzioni di modello completamente specificate?

No.

non cattura lambda?

No.

g ++ apparentemente mi permette di usare tutto quanto sopra,

Sì. Ma supponiamo che tu stia passando ad un oggetto costruito con il compilatore C++ (che sarebbe legale). Poiché il codice C++ utilizzerà la convenzione di chiamata corretta. Il problema è quando si passano queste cose in una libreria C (viene in mente pthreads).

+0

Alcuni anni fa hai menzionato un compilatore C++ con una convenzione di chiamata per membri statici diversa dalla convenzione di chiamata C: http://stackoverflow.com/questions/1738313/c-using-class-method -as-a-function-pointer-type/1738425 # comment-1618920 So che è stato tanto tempo fa, ma penso che qualsiasi dettaglio che potresti essere in grado di ricordare sarebbe molto interessante. –

+1

Questo è stato durante il mio periodo a VERITAS (4 lavori fa). Ero responsabile del sistema di build che ha costruito il software su 25 varianti di compilatore/sistema operativo. Sfortunatamente non riesco a ricordare quale fosse. Ma questa esperienza è fondamentalmente ciò che mi fa stare lontano da tutto ciò che è vicino a un comportamento non specificato o indefinito. –

2

In generale, a meno che non si usi il cast, è necessario affidarsi a g ++.

Mentre è vero che nessuno dei tipi di funzione che si menzionano può essere esportato per l'uso da C, questo non è ciò che si sta chiedendo. Stai chiedendo quali funzioni puoi passare come puntatore a funzione.

Per rispondere a ciò che puoi passare, penso che sia più costruttivo capire cosa non puoi passare. Non è possibile passare nulla che richiede argomenti aggiuntivi non esplicitamente indicati nell'elenco degli argomenti.

Quindi, nessun metodo non statico. Hanno bisogno di un "questo" implicito. C non saprà passarlo. Quindi di nuovo, il compilatore non ti lascerà.

Nessun lambda di cattura. Richiedono un argomento implicito con l'attuale corpo di lambda.

Ciò che è possibile passare sono i puntatori di funzioni che non richiedono un contesto implicito. A dire il vero, sei andato avanti e li hai elencati:

  • Puntatore funzione. Non importa se si tratta di una funzione standard o di un modello, purché il modello sia completamente risolto. Questo non è un problema. Qualsiasi sintassi scritta che risulta in un puntatore a funzione risolverà completamente il modello automaticamente.
  • Lambdons non di cattura. Questo è un work-around speciale introdotto da C++ 11 quando introduce lambda. Poiché è possibile farlo, il compilatore esegue la conversione esplicita richiesta per farlo accadere.
  • Metodi statici. Dal momento che sono statici, non ricevono un implicito this, quindi sono a posto.

L'ultimo si espande. Molti meccanismi di callback C ottengono un puntatore a funzione e un vuoto * opaq. La seguente è una standard ed abbastanza sicuro di poter essere utilizzate con una classe C++:

class Something { 
    void callback() { 
    // Body goes here 
    } 

    static void exported_callback(void *opaq) { 
    static_cast<Something*>(opaq)->callback(); 
    } 
} 

E poi fare:

Something something; 

register_callback(Something::exported_callback, &something); 

A cura di aggiungere: L'unico motivo per cui funziona è perché il C++ la convenzione di chiamata e la convenzione di chiamata C sono identiche quando non vengono passati argomenti impliciti. C'è una differenza nel manomissione dei nomi, ma non è rilevante quando si passa un puntatore a funzione, poiché l'unico scopo del manomissione dei nomi è di consentire al linker di trovare l'indirizzo della funzione corretta.

Se avessi provato quel trucco con una callback che si aspettava, ad esempio, una convenzione di chiamata stdcall o pascal, questo schema sarebbe caduto in superficie.

Questo, tuttavia, non è univoco per i metodi statici, lambda e le funzioni del modello. Anche una funzione standard fallirebbe in tali circostanze.

#define stdcall __attribute__((stdcall)) 
typedef stdcall void (*callback_type)(void *); 

risultati in::

test.cpp:2:45: warning: ‘stdcall’ attribute ignored [-Wattributes] 
typedef stdcall void (*callback_type)(void *); 
+1

Questa è una visione molto pratica. Non è sbagliato in quanto tale. Ma in quello formale non è possibile combinare le convenzioni di chiamata con un comportamento ben definito. Penso che dovresti menzionare anche il formale. Vale la pena saperlo. –

+0

Hai ragione. Ho modificato la mia risposta di conseguenza. –

+1

Questo è tutto sbagliato. Non è possibile passare alcuna funzione C++ a un codice C tramite un puntatore a funzione. Questo perché le convenzioni di chiamata per entrambe le lingue non sono specificate dallo standard. L'unica cosa che puoi passare da C++ a C e garantire che funzioni è la funzione C (dichiarata esplicitamente con 'extern" C "'). Si romperà forse forse non come tutti i comportamenti indefiniti che potrebbero sembrare funzionare perfettamente sulla tua piattaforma. –

Problemi correlati