static vs extern “C” / “C ++”

¿Cuál es la diferencia entre una función de miembro estático y una función de vinculación “C” externa? 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. ¿Tengo suerte o …?

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

vs

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

EDITAR: ¿Puede mostrar un comstackdor o architecture donde la versión de miembro estático no funciona (y no es un error en el comstackdor)?

Sí, tienes suerte 🙂 La “C” externa es un enlace de lenguaje para el lenguaje C que todo comstackdor de C ++ debe soportar, además de “C ++” externo que es el predeterminado. Los comstackdores 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 comstackdor 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 comstackdor C ++. A menudo son los mismos que para el comstackdor de C de esa plataforma, así que dije que tienes suerte. Si tiene una API C y pasa un puntero a la función, mejor siempre ponga una a una función declarada con extern “C” como

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

Aunque el tipo de puntero a función no contiene la especificación del enlace sino que se ve como

 void(*)(void) 

El enlace es una parte integral del tipo; simplemente no se puede express directamente sin un typedef:

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

El comstackdor 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 a (void(*)()) , ya que este es un puntero a una función con enlace C ++.

Tenga en cuenta que esa extern C es la forma recomendada de interoperabilidad C / C ++. Aquí está 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.

De vuelta a la extern C : si no usas la C externa, tendrás que saber el nombre exacto y destruirlo y usarlo. Eso es mucho más un dolor.

extern "C" deshabilita el cambio de nombre del comstackdor de C ++ (que se requiere para la sobrecarga).

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

La mayor parte de lo que hace la extern "C" depende en gran medida del comstackdor. Muchas plataformas cambian el nombre de la convención de invocación y llamada en función de la statement, 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, el estándar dice:

Dos tipos de funciones con enlaces de idiomas diferentes son tipos distintos, incluso si son idénticos.

Esto significa que el extern "C" void proxy(int i) {} y /*extern "C++"*/void proxy(int i) {} tienen diferentes tipos, y como resultado, los punteros a estas funciones también tendrían diferentes tipos. El comstackdor no falla su código por la misma razón por la cual no fallaría un gran trabajo como:

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

Este código podría funcionar en alguna plataforma, pero eso no significa que funcionará en otra plataforma (incluso si el comstackdor cumple con la norma). 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 se requiere que tengan un puntero para que el comstackdor pueda tratarlas como una función no miembro. De nuevo, el comportamiento aquí es específico de la plataforma.

Generalmente hablando

Clases de almacenamiento:

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

Duración:

La duración indica la vida útil de una variable.

Alcance:

El scope indica la visibilidad de la variable.

Clase de almacenamiento estático:

La clase de almacenamiento estático se usa para declarar un identificador que es una variable local, ya sea para una función o un archivo, y que existe y conserva su valor después de que el control pasa desde donde fue 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 scope 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 statement de la variable también inicialice la variable a cero o todos los bits desactivados.

Clase de almacenamiento externo:

La clase de almacenamiento externo se usa para declarar una variable global que las funciones de un archivo conocerán y todas las funciones de un progtwig podrán conocer. 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 scope es global. Una variable puede ser conocida o vista por todas las funciones dentro de un progtwig.