2010-11-16 16 views
5

Tengo una clase que solo tiene miembros estáticos.Llamando punteros a funciones C++ de las bibliotecas C

Me gustaría registrar una de sus funciones miembro (VerifyClean en el código a continuación) para llamar al salir, usando la función de biblioteca "atexit".

El C++ FQA dice que debo especificar extern "C" para la función que deseo registrar de esta manera, como en el siguiente ejemplo.

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); 
} 

¿De verdad tengo que usar extern "C"?

¿La respuesta cambia si reemplazo "atexit" con una función que no es de biblioteca (implementado en el plano C)?

Si la función VerifyClean fuera pública y decidiera llamarla directamente desde el código C++, ¿obtendría errores de enlace o bloqueos en el tiempo de ejecución? Pregunto esto porque la declaración no menciona en absoluto el término "C", por lo que el código normal de C++ podría manejar la llamada de función incorrectamente. Esto funciona bien en mi sistema MS Visual Studio 2005.

+7

¿Por qué tener una clase con funciones sólo estáticas ? Y el FQA no es un buen lugar para aprender sobre C++. –

+6

"FQA considerado nocivo" – jkerian

+2

El C++ FQA es un recurso mucho más confiable para aprender C++ que las preguntas frecuentes. –

Respuesta

7

Es posible para que un compilador use diferentes convenciones de llamada para el código C y C++; sin embargo, en la práctica, esto casi nunca ocurre.

Si solo quieres que funcione y no te importa respaldar compiladores poco claros, no te molestes con extern "C". No es necesario en ningún compilador ampliamente utilizado.

Si usted quiere estar absolutamente pedante, o la necesidad de apoyar un compilador pedante, escribir un envoltorio:

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

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

+1. Explica que probablemente no valga la pena preocuparse ("atexit (& VerifyClean)" probablemente funcione en la práctica), y luego da la solución técnicamente correcta. – aschepler

+0

No se requiere extern "C" en caso de que el compilador de C++ haya destruido el nombre de la función? ¿O la mayoría de los compiladores C++ evitan manipular cuando no tienen que hacerlo (por ejemplo, cuando no hay sobrecarga o las funciones no están dentro de una clase)? –

1

errores de enlace.

C++ realiza lo que se denomina creación de nombres, que genera un nombre de función de tiempo de enlace con información de tipo.

extern C convierte ese en en un identificador más simple.

edición:

Si todo está siendo compilado por un compilador de C++, no será un problema. Pero si tiene un archivo de objeto compilado por un compilador de C y otro compilado por un compilador de C++, tendrá algunos problemas.

Me parece recordar DLL que requieren una especificación extern "C", pero que la memoria es quizás 10 años de edad en este punto


bien.

me prepararon rápidamente un caso de prueba con una función que tenía una firma

int foo (float, float)

y compilado bajo 3 invocaciones gcc diferentes -

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

Estas dos invocaciones produjeron identificadores diferentes en el conjunto. El C++ había destrozado el nombre en su enfoque habitual de modificación de tipo. (Por supuesto compiladores pueden hacer esto de manera diferente)

Entonces, envolví foo en Extern "C" y se invoca G ++ de nuevo ...

g++ test.cpp -S 

Qué entonces removido el mangled C++ nombre, dejando una llanura C nombre sin nombre.

Si bien hay otras sutilezas involucradas aquí, p., el orden de los argumentos empujó a la pila, yo descanso mi caso en este punto, basado en datos.

+2

La pregunta no tiene nada que ver con el nombre de manipulación o errores del enlazador; el problema es que los compiladores pueden usar diferentes convenciones de llamadas para los procedimientos C y los métodos estáticos de C++, por lo que es * posible * que el código sin 'extern' falle durante el tiempo de ejecución. –

+1

+1 por haber sido votado negativamente sin motivo alguno por algún idiota, impulsor de drive-by impulsado por hormonas. por cierto, 'extern" C "' no desactiva el mangle del nombre, pero * típicamente * resulta en un mangle mucho más simple, simplemente prefijando guiones bajos. cheers, –

+1

@John: la pregunta tiene que ver con el cambio de nombre, y también con el tema de la posible convención de llamadas que mencionas. El propósito del cambio de nombre es tener una especie de comprobación de tipo de tiempo de enlace débil (puede considerar llamar a la parte de convención del tipo). algunos compiladores se quejan incluso por el código en la misma unidad de compilación, aunque, por lo que sé, para todos los compiladores existentes, la advertencia puede desactivarse. cheers, –

0

Sin extern "C", el nombre de la función se arruinó por el compilador, por lo que el nombre de la función podría terminar diferente de lo esperado. Debe llamar a la función utilizando su nombre destruido (como usar GetProcAddress en Windows) o obtendrá un error de enlazador. Diferentes compiladores lo dañaron de manera diferente, así que es mejor si sigues usando la palabra clave extern.

+0

El nombre destrozado podría no ser ni siquiera un identificador de C válido. – dan04

+0

Es cierto, pero usando GetProcAddress (windows dll) podemos llamar a una función con nombre mutilado SI sabemos cuál es el nombre del nombre. – arifwn

0

puede utilizar esta:

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

int main() 
{ 
    ataexit(yourname::AtExitCall); 
} 
Cuestiones relacionadas