Proporcionar una API de C a su biblioteca de C++ y alias estrictos
Un patrón común al proporcionar una API de C es reenviar declarar algunos tipos opacos en su encabezado público que se pasan a sus métodos API y luego reinterpret_cast
a sus tipos de C++ definidos una vez dentro de la unidad de traducción (y por lo tanto de vuelta en C++ land).
Usando LLVM como ejemplo:
En Los Tipos .h este tipof se declara:
typedef struct LLVMOpaqueContext *LLVMContextRef;
LLVMOpaqueContext
no se hace referencia en ningún otro lugar del proyecto.
LLVMContextRef LLVMContextCreate(void);
Que se define en el núcleo .cpp :
LLVMContextRef LLVMContextCreate() {
return wrap(new LLVMContext());
}
wrap
(and unwrap
) está definido por una macro en CBindingWrapping.h :
#define DEFINE_SIMPLE_CONVERSION_FUNCTIONS(ty, ref) \
inline ty *unwrap(ref P) { \
return reinterpret_cast<ty*>(P); \
} \
\
inline ref wrap(const ty *P) { \
return reinterpret_cast<ref>(const_cast<ty*>(P)); \
}
Y utilizado en LLVMContext.h :
DEFINE_SIMPLE_CONVERSION_FUNCTIONS(LLVMContext, LLVMContextRef)
Así que vemos que la API C básicamente toma un puntero a un LLVMOpaqueContext
y lo convierte en un objeto llvm::LLVMContext
para realizar cualquier método que se invoque en él.
Mi pregunta es: ¿no es esto una violación de las estrictas reglas de aliasing? Si no, ¿por qué no? Y si es así, ¿cómo se puede llegar legalmente a este tipo de abstracción en el límite de la interfaz pública?
1 answers
No es una violación estricta de alias. Para empezar, el alias estricto consiste en acceder a un objeto a través de un glvalue del tipo incorrecto.
En tu pregunta, creas un LLVMContext
, y luego usas un LLVMContext
lvalue para acceder a él. No hay aliasing ilegal allí.
El único problema que puede surgir es si la conversión del puntero no devuelve el mismo puntero. Pero eso tampoco es un problema, ya que reinterpret_cast
está garantizado para devolver el mismo puntero en una conversión de ida y vuelta. Siempre y cuando el el tipo de puntero que convertimos hacia y desde es a datos adecuadamente alineados (es decir, no más estrictos que el tipo original).
Si es o no una buena o mala manera de hacer las cosas es discutible. Yo personalmente no me molestaría con LLVMOpaqueContext
y devolver un struct LLVMContext*
. Sigue siendo un puntero opaco, y no importa que el encabezado C lo declare con struct
mientras que la definición de tipo es con class
. Los dos son intercambiables hasta el punto de la definición del tipo.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-03-12 11:36:35