2009-02-26 21 views
22

¿Cuál es la diferencia entre una función de miembro estática y una función de vinculación externa "C"? Por ejemplo, cuando utilizo "makecontext" en C++, necesito pasar un puntero para funcionar. Google recomienda usar un enlace "C" externo para ello, porque "makecontext" es C. Pero descubrí que usar estáticos funciona también. ¿Soy sólo suerte o ...static vs extern "C"/"C++"

class X { 
    public: 
    static void proxy(int i) {} 
} 
makecontext(..., (void (*)(void)) X::proxy, ...); 

vs

extern "C" void proxy(int i) {} 
makecontext(..., (void (*)(void)) proxy, ...); 

EDIT: ¿Puede mostrar un compilador o la arquitectura, donde la versión miembro estático no funciona (y que no es un error en el compilador) ?

+0

'Lo siento, pero todavía no estoy convencido' ... ¿de qué? el hecho de que el estándar es más autoritativo que el comportamiento coincidente de implementación definida de algunos compiladores? –

+0

Esta es una publicación anterior (hace 8 años) Mi punto era en ese momento algo así como mi humilde opinión, si cada implementación existente difiere del estándar, entonces tal vez debería preguntarse si es el estándar el que está mal. Estaba buscando ejemplos de plataformas donde no funciona. –

+0

Bastante justo.Por supuesto, no es raro que los estándares C o C++ contengan consecuencias imprevistas que todos los compiladores ignoran. Este es mi favorito en este momento: http://stackoverflow.com/a/42335543/2757035 Pero en este caso, creo que el estándar está diciendo claramente lo que significa, y las implementaciones pueden cambiar su comportamiento en cualquier momento si hay alguna ventaja. –

Respuesta

33

Sí, tienes suerte :) La "C" externa es un enlace de lenguaje para el lenguaje C que cada compilador de C++ tiene que soportar, además de extern "C++" que es el predeterminado. Los compiladores pueden ser compatibles con otros enlaces de idiomas. GCC, por ejemplo, admite "Java" externo, que permite la interfaz con el código Java (aunque eso es bastante engorroso).

extern "C" le dice al compilador que su función es invocable por código C. Eso puede, pero no debe, incluir la convención de llamadas apropiada y el cambio de nombre de lenguaje C apropiado (a veces llamado "decoración"), entre otras cosas, dependiendo de la implementación. Si tiene una función miembro estática, la convención de llamada es la de su compilador C++. A menudo son los mismos que para el compilador de C de esa plataforma, así que dije que tienes suerte. Si usted tiene una API C y se pasa un puntero de función, mejor siempre poner uno a una función declarada con extern "C" como

extern "C" void foo() { ... } 

A pesar de que el tipo de puntero de función no contiene la especificación de vinculación, sino más bien se parece a

void(*)(void) 

la vinculación es una parte integral del tipo - que no se puede expresar directamente sin un typedef:

compilador
extern "C" typedef void(*extern_c_funptr_t)(); 

el Comeau C++, en modo estricto , emitirá un error, por ejemplo, si intenta asignar la dirección de la función externa "C" de arriba a (void(*)()), ya que este es un puntero a una función con enlace C++.

+1

Para agregar a la respuesta de litb, debe leer sobre las convenciones de llamadas en Wikipedia - http://en.wikipedia.org/wiki/X86_calling_conventions. la extern C implica la convención de llamada cdecl; su compilador usa el mismo para funciones de miembros estáticos. Otros compiladores también podrían elegir cualquier otro. –

+0

@Comeau compiler, ¿es un error o advertencia que emite? –

+0

Helltone, pruébalo http://www.comeaucomputing.com/tryitout/ dice: "ComeauTest.c", línea 4: error: un valor de tipo "void (*)() C" no se puede usar para inicialice una entidad de tipo "void (*)()" ' –

4

extern "C" desactiva el nombre del compilador de C++ mangling (que se requiere para la sobrecarga).

Si declara que una función en A.cpp es static, no puede ser encontrada por B.cpp (es un remanente de C, y tiene el mismo efecto de poner una función dentro de un espacio de nombres anónimo).

+0

Esto no responde mi pregunta –

+0

También puede cambiar la convención de llamadas. –

+0

Nota: el OP estaba hablando de _métodos_estáticos_ ("funciones de miembro"), no _global_ funciones con vinculación estática. –

5

Tenga en cuenta que extern C es la forma recomendada de interoperabilidad C/C++. Here es el maestro hablando de eso. Para agregar a la respuesta de eduffy: tenga en cuenta que las funciones estáticas y las variables en el espacio de nombres global están en desuso. Use un espacio de nombre anónimo al menos.

Volver a extern C: si no usa extern C, tendrá que saber el nombre exacto y el uso del mismo. Eso es mucho más un dolor.

+0

Nota: el OP era hablando de _métodos_estáticos_ ("funciones de miembro"), no _global_ funciones con enlace estático. –

2

La mayor parte de lo que hace extern "C" depende en gran medida del compilador. Muchas plataformas cambian el nombre de la convención de invocación y llamada en función de la declaración, pero nada de eso está especificado en el estándar. Realmente, lo único que requiere el estándar es que el código en el bloque se pueda llamar desde las funciones C.En cuanto a su pregunta específica, la norma dice:

Two function types with different language linkages are distinct types even if they are otherwise identical.

Esto significa extern "C" void proxy(int i) {} y /*extern "C++"*/void proxy(int i) {} tienen diferentes tipos, y como resultado punteros a estas funciones tendría diferentes tipos así. El compilador no falla su código por la misma razón que no fallaría una gran pieza de trabajo como:

int *foo = (int*)50; 
makecontext(..., (void (*)(void)) foo, ...); 

Este código podría trabajar en alguna plataforma, pero eso no significa que vaya a trabajar en otro plataforma (incluso si el compilador cumplía completamente con las normas). Está aprovechando el funcionamiento de su plataforma en particular, lo que podría estar bien si no le preocupa escribir códigos portátiles.

En cuanto a las funciones estáticas de los miembros, no es necesario que tengan un puntero this para que el compilador pueda tratarlas como una función no miembro. De nuevo, el comportamiento aquí es específico de la plataforma.

+0

Buena respuesta, pero te pierdes totalmente el punto de mi pregunta. Mi pregunta se refiere a la diferencia entre la función externa "C" y una función * miembro estático *. –

2

En términos generales

clases de almacenamiento:

clases de almacenamiento se utilizan para indicar la duración y el alcance de una variable o identificador.

Duración:

Duración indica el tiempo de vida de una variable.

Alcance:

Ámbito indica la visibilidad de la variable.

estático de clase de almacenamiento:

La clase de almacenamiento estático se utiliza para declarar un identificador que es una variable local, ya sea a una función o un archivo y que existe y conserva su valor después pasa el control de donde estaba declarado. Esta clase de almacenamiento tiene una duración que es permanente. Una variable declarada de esta clase conserva su valor de una llamada de la función a la siguiente. El alcance es local. Una variable se conoce solo por la función en la que se declara o, si se declara globalmente en un archivo, solo se conoce o se ve por las funciones dentro de ese archivo. Esta clase de almacenamiento garantiza que la declaración de la variable también inicialice la variable a cero o todos los bits desactivados.

Externo clase de almacenamiento:

La clase de almacenamiento externo se utiliza para declarar una variable global que se dará a conocer a las funciones en un archivo y capaz de ser conocido a todas las funciones en un programa. Esta clase de almacenamiento tiene una duración que es permanente. Cualquier variable de esta clase conserva su valor hasta que la cambie otra asignación. El alcance es global. Una variable puede ser conocida o vista por todas las funciones dentro de un programa.