2010-11-16 16 views
5

Ho una classe che ha solo membri statici.Chiamare i puntatori alle funzioni C++ dalle librerie C

Vorrei registrare una delle sue funzioni membro (VerifyClean nel codice seguente) da chiamare all'uscita, utilizzando la funzione di libreria "atexit".

Il C++ FQA dice che devo specificare "C" extern per la funzione che voglio registrare in questo modo, come nell'esempio seguente.

class Example 
{ 
public: 
    static void Initialize(); 
    static void DoDirtyStuff {++dirtLevel;} 
    static void CleanUpStuff {--dirtLevel;} 
private: 
    static void VerifyClean(); 
    // DOESN'T COMPILE: extern "C" static void VerifyClean(); 
    static int dirtLevel; 
} 

int Example::dirtLevel; 

extern "C" void Example::VerifyClean() // DO I NEED extern "C" HERE? 
{ 
    assert(dirtLevel == 0); 
} 

void Example::Initialize() 
{ 
    dirtLevel = 0; 
    atexit(&VerifyClean); 
} 

Devo davvero usare "C" extern?

La risposta cambia se sostituisco "atexit" con una funzione non di libreria (implementata in plain C)?

Se la funzione VerifyClean era pubblica e ho deciso di chiamarla direttamente dal codice C++, avrei avuto errori di collegamento o arresti anomali del runtime? Lo chiedo perché la dichiarazione non menziona affatto la "C" extern, quindi il normale codice C++ potrebbe gestire la chiamata della funzione in modo errato. Funziona correttamente con il mio sistema MS Visual Studio 2005.

+7

Perché avere una classe con solo funzioni statiche ? E l'FQA non è certo un buon posto per conoscere il C++. –

+6

"FQA considerato nocivo" – jkerian

+2

Il FQA C++ è una risorsa molto più affidabile per l'apprendimento del C++ rispetto alle FAQ. –

risposta

7

È possibile per un compilatore utilizzare diverse convenzioni di chiamata per codice C e C++; tuttavia, in pratica, questo non accade quasi mai.

Se si desidera solo che funzioni e non si preoccupi di supportare i compilatori oscuri, non preoccuparsi di extern "C". Non è necessario in qualsiasi compilatore ampiamente utilizzato.

Se si vuole essere assolutamente pedante, o la necessità di supportare un compilatore pedante, scrivere un involucro:

extern "C" static void ExampleVerifyClean() 
{ 
    Example::VerifyClean(); 
} 

void Example::Initialize() 
{ 
    dirtLevel = 0; 
    atexit(&ExampleVerifyClean); 
} 
+1

+1. Spiega che probabilmente non vale la pena preoccuparsi ('atexit (& VerifyClean)' probabilmente funzionerà nella pratica), e quindi fornirà la soluzione tecnicamente corretta. – aschepler

+0

Non è richiesta anche la "C" extern nel caso in cui il compilatore C++ abbia manomesso il nome della funzione? Oppure la maggior parte dei compilatori C++ evita il mangling quando non è necessario (ad esempio quando non c'è sovraccarico o le funzioni non sono all'interno di una classe)? –

1

errori di collegamento.

C++ esegue quello che viene chiamato il nome mangling, che genera un nome di funzione link-time con informazioni sul tipo.

extern C trasforma lo in uno in un identificatore più semplice.

edit:

Se tutto è stato compilato da un compilatore C++, non sarà un problema. Ma se si ha un file oggetto compilato da un compilatore C e uno compilato da un compilatore C++, si avranno alcuni problemi.

Mi sembra di ricordare le DLL che richiedono una specifica extern "C", ma che la memoria è forse 10 anni, a questo punto


Va bene.

ho montata su un banco di prova di una funzione che ha avuto una firma

int foo (float, float)

e compilato sotto 3 diversi invocazioni GCC -

gcc test_c.c -S 
g++ test.cpp -S 

Queste due chiamate hanno generato identificativi diversi nell'assieme. Il C++ aveva stravolto il nome nel suo solito approccio di modifica del tipo. (Ovviamente compilatori possono farlo in modo diverso)

Poi, ho avvolto foo in Esterno "C" e invocato G ++ nuovamente ...

g++ test.cpp -S 

Quale dunque rimosso il mutilato C++ nome, lasciando una pianura C nome senza maglie.

Mentre ci sono altre sottigliezze coinvolte qui, ad es., l'ordine degli argomenti spinti in pila, riposa il caso su questo punto, in base ai dati.

+2

La domanda non ha nulla a che fare con il nome di mangling o errori del linker; il problema è che i compilatori possono utilizzare diverse convenzioni di chiamata per le procedure C e i metodi statici C++, quindi è * possibile * che il codice senza 'extern' si bloccherà in fase di runtime. –

+1

+1 per essere downvoted senza motivo da alcuni downstrip drive-by moron, guidati dall'ormone. a proposito, "extern" C "" non disattiva il nome di mangling, ma * tipicamente * si traduce in un maneggismo molto più semplice, solo il prefisso dei caratteri di sottolineatura. applausi, –

+1

@ John: la domanda ha a che fare con il nome mangling, e anche il problema delle possibili convenzioni di chiamata che menzioni. lo scopo del manomissione dei nomi è di avere una sorta di controllo del tipo di tempo di collegamento debole (si può considerare la parte relativa alla convenzione di chiamata del tipo). alcuni compilatori si lamentano anche per il codice nella stessa unità di compilazione, anche se per quanto ne so per tutti i compilatori esistenti, l'avviso può essere disattivato. evviva, –

0

Senza "C" extern, il nome della funzione verrà manomesso dal compilatore, quindi il nome della funzione potrebbe risultare diverso da quello che ci si aspetta. Devi chiamare la funzione usando il suo nome storpiato (come usare GetProcAddress in windows) o avrai l'errore linker. Diversi compilatori lo hanno distrutto in modo diverso, quindi è meglio se continui a utilizzare la parola chiave extern.

+0

Il nome mangled potrebbe non essere nemmeno un identificatore C valido. – dan04

+0

Vero, ma usando GetProcAddress (windows dll) possiamo chiamare una funzione con nome storpiato SE sappiamo qual è il nome storpiato. – arifwn

0

si potrebbe usare questo:

class yourname 
{ 
    public: 
    ... 
    static void _cdecl AtExitCall(); 
}; 

int main() 
{ 
    ataexit(yourname::AtExitCall); 
} 
Problemi correlati