2012-05-04 18 views
5

Supongamos que tengo la siguiente plantilla de clase:tamaño de una estructura que contiene 1 Puntero

template<typename T> 
struct Wrapper { 
    T* t_; 
    static void check() { 
    static_assert(sizeof(Wrapper<T> == sizeof(T*), "Illegal assumption"); 
    } 
}; 

miré en el estándar C99 una en el estándar de C++ 03 y no puede encontrar una garantía para mi suposición expresa en el static_assert. Lo intenté en Visual C++ 2008 y 2010 (32 bits) y gcc en Linux (64 bits) usando varias opciones del compilador y encontré mi suposición confirmada.

Mi pregunta es:

  • ¿Es mi suposición razonable para Visual C++ 2008/2010/11 (ventanas)?
  • Para gcc 4. * (linux)?
  • ¿Para cualquier combinación de indicadores del compilador?
  • ¿Conoce algún compilador/plataforma donde esta suposición no sea cierta?

Supongo que un compilador podría agregar algo de relleno a la estructura, p. con fines de depuración. ¿Pero hay un compilador que realmente hace eso?

Editar: Así como se pide aquí es lo que quiero lograr:
tengo la función miembro con la siguiente firma:

Someclass* OtherClass::fn(); 

Quiero cambiar la firma de esta manera:

Wrapper<Someclass> OtherClass::fn(); 

Este envoltorio actúa como un puntero inteligente, es decir, se preocupa por la duración del puntero, por lo que lo libera cuando se sale del alcance. Como se llama a la función a través de un límite dll, quiero asegurarme de que el valor devuelto (que ahora es un tipo concreto, no solo un puntero tonto) sea en todas las circunstancias (es decir, configuración del compilador, etc.) del mismo tamaño que el puntero sería. El plan/esperanza es admitir todas las combinaciones de versiones de depuración/aplicación/dll.
Como podría preguntar: no, no puedo usar boost :: shared_ptr <>, std :: shared_ptr <>, std :: unique_ptr <> y similares, ya que no queremos exponer el impulso al usuario dll y aún no somos compatibles con C++ 11.

+1

¿Consideró las funciones virtuales/herencia? – PlasmaHH

+0

Entonces ... ¿quieres que probemos el código en todos esos compiladores y plataformas? –

+1

No, probablemente quiera saber si alguien ya hizo algo similar y/o si esto está implícito en algún otro lugar en la documentación estándar o del compilador. – Tibor

Respuesta

3

Si desea asumirlo y tiene una verificación en tiempo de compilación, continúe. Presumiblemente obtendrá algún beneficio de hacer esto.

No se garantiza nada sobre el relleno, pero normalmente el relleno se utiliza para obtener la alineación, de modo que las matrices del tipo tengan todos los miembros de la matriz (y cada miembro de la estructura) alineados correctamente. Normalmente, un puntero nativo ya tiene el tamaño correcto para alinearse, por lo que no es necesario rellenarlo, pero no está garantizado.

Esto no es algo que simplemente pueda verificar con gcc; depende de la arquitectura de destino, no solo del compilador.

+0

Solo quiero asegurarme de saber por qué me tiene que importar, en qué puedo confiar y qué tengo que omitir. – nriedlin

+0

Puede confiar en su activación afirmativa si su suposición no es cierta. No puede confiar en que nunca se active una nueva combinación de compilador (o versión) y destino. –

1

Mirando hacia el estándar encontré dos cosas interesantes en § 9.2 [class.mem]:

17/ Dos struct-diseño estándar (Cláusula 9) tipos son compatibles si el diseño- tener el mismo número de miembros de datos no estáticos y los correspondientes miembros de datos no estáticos (en orden de declaración) tienen tipos compatibles con el diseño (3.9).

Así, dos struct sólo con un puntero en ellos serían compatibles diseño (tanto si su afirmación es válida para este tipo, se cumple para todos los tipos).

20/ Un puntero a un objeto struct estándar-layout, adecuadamente convertidos usando un reinterpret_cast, apunta a su miembro inicial (o si ese miembro es un campo de bits, a continuación, a la unidad en la que reside) y viceversa. [Nota: por lo tanto, podría haber un relleno sin nombre dentro de un objeto de estructura de disposición estándar, pero no al principio, según sea necesario para lograr la alineación adecuada. -finalizar]

En la nota nos enteramos de que no puede haber relleno al principio de una estructura.


Dependiendo de su objetivo, 20/ podría ser suficiente. Quiere decir que esto funciona:

void check(T* t) { 
    Wrapper<T>* w = reinterpret_cast<Wrapper<T>*>(t); 
    assert(w->t_ == t); 
} 
1

yo diría que su suposición es razonable con la mayoría de los compiladores y banderas.

Para cualquier T, el compilador puede crear matrices de T, que deben ser contiguas, por lo que debe ser capaz de crear eso sin relleno. Como tal, cualquier relleno que ponga en su estructura sería puramente opcional, no algo que podría requerirse.

Por otro lado, estoy razonablemente seguro de que ninguno de los estándares C o C++ garantiza lo que está pidiendo. El más cercano a garantías sobre esto se derivaría del hecho de que en C++ es estructura estándar struct, que restringe el compilador de C++ para presentar los campos como un compilador de C, por lo que los miembros deben estar en orden ascendente sin relleno al comienzo - pero el relleno entre los miembros y/o después del último miembro aún está permitido.

Línea inferior: si tiene un puntero al primer miembro, puede convertir a puntero a struct (o viceversa) de forma segura. Estás solo si haces algo así como crear una matriz e indexar como si fuera una matriz de la otra, las posibilidades de que funcione son bastante buenas, pero estoy bastante seguro de que el estándar no lo hace garantizarlo

Cuestiones relacionadas