2009-12-15 21 views
12

He encontrado un código de trabajo (con compiladores XLC8 y MSFT9), que contiene un archivo C++ con una función definida con enlace C y un argumento de referencia. Esto me molesta, ya que las referencias son solo C++. La función en cuestión se llama desde el código C, donde se declara que toma un argumento de puntero al mismo tipo en lugar del argumento de referencia.C++ argumento de referencia y enlace C

ejemplo simplificado:

archivo

C++:

extern "C" void f(int &i) 
{ 
    i++; 
} 

archivo C:

void f(int *); 

int main() 
{ 
    int a = 2; 
    f(&a); 
    printf("%d\n", a); /* Prints 3 */ 
} 

Ahora, la palabra en la calle es que la mayoría de los compiladores de C++, bajo el capó, implementar referencias como un puntero. ¿Es así y solo por pura suerte la razón por la que funciona este código o dice algo en la especificación C++ cuál es el resultado cuando defines una función con un argumento de referencia y un enlace C? No he podido encontrar ninguna información sobre esto.

+0

Por lo que puedo ver sección 7.5 de la El estándar C++ (especificación de vinculación) no dice nada sobre esto. –

Respuesta

4

Mi copia de n3000.pdf (de here), tiene esto que decir en la sección 7.5 — especificaciones de vinculación:

. El enlace de C++ a los objetos definidos en otros lenguajes y a los objetos definidos en C++ de otros idiomas es definido en la implementación y dependiente del idioma. Solo cuando las estrategias de disposición de objetos de dos implementaciones de lenguaje son similares se puede lograr tal vinculación.

Como C y C++ son idiomas diferentes, esto significa que no puede confiar en esta "característica" de los compiladores comunes.

fuerte es nota 5 en la misma sección (énfasis mío):

Si dos declaraciones declaran funciones con el mismo nombre y parámetro de tipo-list (8.3.5) para ser miembros de el mismo espacio de nombres o declarar objetos con el mismo nombre a ser miembros del mismo espacio de nombres y las declaraciones dan los nombres enlaces de idiomas diferentes, el programa está mal formado; no se requiere diagnóstico si las declaraciones aparecen en diferentes unidades de traducción.

Por lo tanto, yo diría que lo que hizo no se garantiza que funcione de acuerdo a la norma, y ​​el compilador no es necesaria para imprimir un diagnóstico para el ejemplo que has dado, porque las declaraciones están en diferentes unidades de traducción.

FYI, "funciona para mí" con gcc y g ++ versión 4.2.1 en Snow Leopard.

+0

Cita enteramente correcta, pero tenga en cuenta que también tiene un lado positivo: ya que su implementación está definida de todos modos, si funciona, funciona. – MSalters

+0

Sí, pero tal vez funciona porque funciona con esa versión particular del compilador? Intenté consultar la documentación de gcc pero no pude encontrar ninguna información. Por lo que yo entiendo, la pregunta es más una pregunta "teórica", tratando de averiguar si el estándar permite tales declaraciones. Dudo si el OP quiere hacer algo así en código real, dado que es muy fácil evitar hacerlo. –

+0

Bueno, el código está en producción y funciona, pero me gustaría mantener los estándares del código en conformidad ya que las nuevas plataformas o compiladores pueden ser apuntados en el futuro. – olovb

11

En muchos casos, pero no en todos, se puede implementar una referencia con un puntero 'autorreferencial'. Que cualquier compilador particular trate una función con un enlace C y un parámetro de referencia de esta manera no está garantizado en el estándar C++, y debe tratarlo como un detalle de implementación.

No es difícil escribir una función de reenvío que toma un puntero y llama a su función, si es necesario hacer esto sin depender de los detalles de implementación:

void real_f(int& n) { 
    n++; 
} 
extern "C" void f(int* p) { // called from C code 
    real_f(*p); 
} 
3

Un de referencia es un nombre alternativo para un objeto. Técnicamente, no hay nada en C que pueda correlacionar directamente con una referencia de C++.

La implementación obvia de una referencia es como un puntero (constante) que se desreferencia cada vez que se utiliza. Entonces, dependiendo de cómo su compilador implemente referencias, su código podría funcionar. Pero no es correcto

La solución es escribir una función C que reciba un objeto real o un puntero a ese objeto y llamar a la función C++ desde aquí.

+1

No está garantizado, pero no es necesariamente incorrecto. La forma en que otros lenguajes llaman a las funciones de C++ (incluidas las funciones '' CTERN 'extern") está fuera del alcance del estándar de C++. Y en lo que respecta a C, la función ha sido declarada y llamada correctamente. Este es un problema de implementación, y si usted puede, debe o no depender de él variará según los requisitos del proyecto. –

0

Eso es lo que Bjarne Stroustrup tiene que decir acerca de las referencias:.

"mayoría de las referencias son implementos se implementan utilizando una variable puntero, que es una referencia por lo general ocupa una palabra de la memoria Sin embargo, una referencia que se usa puramente localmente puede, y a menudo lo es, eliminarse mediante el optimizador ".

Por ejemplo:

struct S 
{ 
    int a; 
    int b[100]; 
}; // just an example 

void do_something(const vector<S>& v) 
{ 
    for (int i=0; i<v.size(); ++i) 
    { 
     int(&p)[100] = v[i].b; 
     for (int j=0; j<100; ++j) 
      cout <<p[j]; 
    } 
} 

En este caso, p no necesita ser almacenada en la memoria (tal vez sólo existe en un registro, tal vez que desaparece en las instrucciones).

+0

Quiere decir 'int (& p) [100] = v [i] .b;'; también s /, /;/v.size(). –

+0

@Roger: Gracias, he editado mi publicación :) –

+4

Sería bueno si dieras una cita más completa. * ¿Dónde * dijo Stroustrup eso? (Sitio web o título del libro, edición y número de página) Por otro lado, las referencias * ya sabemos * a menudo se implementan como punteros. Es por eso que el código en la pregunta funciona como lo hace hoy. No veo el punto que intentabas hacer con esta respuesta, especialmente con respecto a los parámetros de las funciones con enlace C. –

1

Creo que ya obtuvo algunas buenas respuestas, por lo que solo señala algo extra en su pregunta.

Mi humilde opinión es que extern simplemente crea un símbolo C.

lo tanto, su ejemplo sigue siendo "parece funcionar" (gcc/g ++) aun cuando agrego un objeto de clase - que tenía que probar ir a creer:

class A {}; 

extern "C" void f(int &i, A a) { 
    i++; 
}