Tengo una clase que quiero compartir entre C y C++, donde C solo puede obtenerla como un puntero. Sin embargo, debido a que es una clase interna, no se puede declarar hacia adelante. En cambio, esto es lo que hace nuestro código actual en un archivo de encabezado común:
#ifdef __cplusplus class Container { public: class Object { public: int x; }; }; typedef Container::Object * PObject; #else typedef void* PObject; #endif Parece que esto viola la regla de una definición (ODR), porque C y C++ ven una definición diferente usando #ifdef . Pero debido a que esto es un puntero, no estoy seguro de si esto crea un problema real o no. C solo usa el puntero para pasarlo a las funciones de C++, no hace nada directamente con él. Por ejemplo, este código en el archivo de encabezado común:
#ifdef __cplusplus extern "C" { #endif int object_GetX(PObject pObject); #ifdef __cplusplus } #endif /* __cplusplus */Así es como lo implementamos en el archivo C++:
int object_GetX(PObject pObject) { return pObject->x; }Mis preguntas son:
En primer lugar, los punteros de alias de tipo suelen ser una receta para los problemas.
no lo hagas
En segundo lugar, la clase "interna" es un concepto usado en exceso, por lo que mi primer reflejo sería considerar si es realmente necesario.
Si es necesario, puede definir un tipo vacío opaco y derivar de él para alguna seguridad de tipo:
En un encabezado compartido:
struct OpaqueObject; #ifdef __cplusplus extern "C" { #endif int object_GetX(OpaqueObject* pObject); #ifdef __cplusplus } #endif /* __cplusplus */En un encabezado de C++:
struct OpaqueObject {}; class Container { public: class Object : public OpaqueObject { public: int x; }; };Implementación:
int object_GetX(OpaqueObject* pObject) { return static_cast<Container::Object*>(pObject)->x; }¿Violar la ODR de esta manera causaría algún problema real?
¿"Problemas reales"? Quizás. Tal vez no.
No existe una "regla ODR mixta C/C++". Las reglas están "contenidas" dentro de un lenguaje de programación, las especificaciones están escritas para un lenguaje de programación. C++ tiene sus propias reglas y C++ ihas regla ODR. C tiene sus propias reglas y C no sabe nada sobre ODR. Otros lenguajes de programación tienen sus propias reglas.
No está violando ODR en C ++: C ++ ve una sola definición de Contenedor en todas partes.
El material en extern "C" usa la convención de llamadas C, que podría suponer que también se adhiere al lenguaje C. En este caso, simplemente está violando la regla https://port70.net/~nsz/c/c11/n1570.html#6.5.2.2p9 . Digamos que esto es como ODR en C para funciones.
Si no, estás violando algo entre idiomas, eso sería ABI. Pasas el valor de un puntero void * del lado C y tu función lee un valor del puntero Container::Object * del lado C++. Eso podría estar permitido por el ABI específico que usan sus compiladores, podría no estar especificado. El enfoque seguro sería asumir que no está permitido.
¿Hay otra forma conveniente de compartir la clase con C?
El lenguaje C también ofrece verificación de tipo estático al igual que C++, pero al usar void * básicamente lo está desactivando. No mezcle C ++ con C. Solo tenga un nombre único diferente para el lado C, estará mucho menos confundido.
Además, pObject vs PObject ? ¡Tu turno debe ser rápido!
// object.hpp #ifdef __cplusplus class Container { public: class Object { public: int x; }; }; typedef Container::Object PObject; extern "C" { #endif struct C_PObject { void *pnt; }; int object_GetX(struct C_PObject obj); // ... // object_c.cpp PObject *object_from_c(struct C_PObject obj) { return reinterpret_cast<PObject *>(obj.pnt); } extern "C" int object_GetX(struct C_PObject obj) { return object_from_c(obj)->x; }