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.

{[11] {} En[22]}Núcleo.h el siguiente método es declarado:
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?

Author: Drise, 2018-03-12

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.

 22
Author: StoryTeller,
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