2009-08-20 8 views
6

Quiero reinterpretar el lanzamiento de un puntero a una variable void *. El tipo del puntero a la función será del tipo Class* (*)(void*).reinterpret_cast a void * no funciona con los punteros a la función

A continuación se muestra el código de ejemplo,

class Test 
{ 
    int a; 
}; 

int main() 
{ 
    Test* *p(void **a); 
    void *f=reinterpret_cast<void*>(p);  
} 

El código anterior funciona bien con los compiladores de Visual Studio/x86. Pero con el compilador ARM, da error de compilación. No sé por qué.

Error: #694: reinterpret_cast cannot cast away const or other type qualifiers

leí la explicación en Casting a function pointer to another type

que estaba preocupado por la explicación a continuación.

Casting between function pointers and regular pointers (e.g. casting a void (*)(void) to a void*). Function pointers aren't necessarily the same size as regular pointers, since on some architectures they might contain extra contextual information. This will probably work ok on x86, but remember that it's undefined behavior.

cómo hacer este tipo de conversiones de void (*)(void*) -> void* efectiva, para que al menos se compila casi el mismo en la mayoría de los compiladores?

+0

Debería al menos poner en una afirmación que 'sizeof (void *)> = sizeof (Test ** (*) (void **))'. Si esta afirmación falla, entonces obviamente no hay forma de almacenar el puntero de función en un 'void *'. Si la afirmación no falla, aún no garantiza nada, pero al menos es posible que el elenco funcione. –

+0

Tenga en cuenta que p en realidad tiene el tipo Test ** (void **), y es una función, no un puntero de función. Poner un espacio en "' Test * * '" no afecta el análisis. – MSalters

Respuesta

7

reinterpret_cast no se puede utilizar para lanzar un puntero para funcionar a void*. Si bien hay algunas cosas adicionales que un molde C puede hacer que no están permitidas por la combinación de estática, reinterpretación y const castts, esa conversión no es una de ellas.

En C el molde está permitido, pero su comportamiento no está definido (es decir, incluso el viaje de ida y vuelta no garantiza que funcione).

Algunas funciones POSIX necesitan que la conversión sea útil.

He jugado con varios compiladores He aquí:

  • ninguno prevenir el elenco C, incluso en el modo de cumplimiento más alto. Algunos dan una advertencia dependiendo de la advertencia y el nivel de conformidad, otros no avisaron lo que intenté.
  • reinterpret_cast fue un error con algunos compiladores incluso en el nivel más relajado, mientras que otros lo aceptaron en todos los casos sin dar nunca una advertencia.

En el último borrador disponible para C++ 0X, el reinterpret_cast entre punteros de función y punteros a objetos es soportado de forma condicional.

Tenga en cuenta que si tiene sentido o no dependerá más del objetivo que el compilador: un compilador portátil como gcc tendrá un comportamiento impuesto por la arquitectura de destino y posiblemente ABI.

Como otros tienen hacer la observación,

Test* *p(void **a); 

define una función, no un puntero a la función. Pero la función del puntero a la función de conversión implícita se hace para el argumento reinterpret_cast, por lo que reinterpret_cast get es Test** (*p)(void** a).

Gracias a Richard que me hace volver a tratar el tema más a fondo (para el registro, me equivoqué al pensar que el puntero para funcionar como puntero a objeto fue un caso donde el molde C permitió algo no autorizado por C++ moldes combinaciones)

+0

C fundido funcionó perfectamente. Gracias. :) – vprajan

+0

(Casi -1) No creo que esto sea correcto, ¿pueden proporcionar una referencia para esto? Tengo entendido que el elenco estilo C se define en términos de los nuevos estilos de elenco. Por lo tanto, si no es posible hacerlo con reinterpret_cast, tampoco es posible con un molde de estilo C. –

0

no debería Test* *p(void **a); ser Test* (*p)(void **a)?

Tal vez ese es su problema?

+0

Prueba * * p (void ** a) también se está resolviendo para escribir Test * (*) (void **) ... así que creo que ambas representaciones son válidas. Estoy seguro de esto porque lo intenté de esa manera también ... – vprajan

3

Si usted está buscando para almacenar diferentes tipos de puntero de función en una lista a continuación, puede convertirse a un tipo común puntero de función:

class Test { 
    int a; 
}; 

int main() 
{ 
    Test* *p(void **a); 
    void (*f)()=reinterpret_cast<void (*)()>(p); 
} 

Ésta es válida para hacerlo a través de reinterpret_cast (5.2.10/6):

A pointer to a function can be explicitly converted to a pointer to a function of a different type. The effect of calling a function through a pointer to a function type (8.3.5) that is not the same as the type used in the definition of the function is undefined. Except that converting an rvalue of type "pointer to T1" to the type "pointer to T2" (where T1 and T2 are function types) and back to its original type yields the original pointer value, the result of such a pointer conversion is unspecified.

7

reinterpret_cast sólo puede ser utilizado para

  • complemento constness
  • convertir un puntero a un tipo entero suficientemente grande para contener y volver
  • convertir un puntero a una función a un puntero a una función de tipo diferente
  • convertir un puntero a un objeto a un puntero a un objeto de diferente tipo
  • convertir un puntero a una función miembro a un puntero a una función miembro de tipo diferente
  • convertir un puntero a un objeto miembro a un puntero a un objeto miembro de diferente tipo
  • y reinterpret_cast<T&>(x) es equivalente a *reinterpret_cast<T*>(&x) (usando el builtin & y *) siempre que el segundo lanzamiento sea posible usando las reglas anteriores.

(Véase la Sección 5.2.10 de la norma)

Esto significa en particular que un elenco de un puntero a la función para void * no es posible, pero se puede echarlo a void(*)().


EDIT (2017): La respuesta de arriba es solo correcta para C++ 03. En C++ 11 a C++ 17, se define la implementación si se permiten las conversiones entre los punteros de función y void *. Este suele ser el caso en los sistemas compatibles POSIX porque dlsym() se declara para devolver void *, y se espera que los clientes lo reinterpret_cast al tipo de puntero de función correcto.

Ver cppreference.com para consultar la lista completa de conversiones permitidas.

2

Otros señalaron que no se puede hacer eso fundido (fuertemente hablando, echando a void* cualquier cosa usando reinterpret_cast tampoco está permitido -. Pero en silencio tolerados por los compiladores static_cast está pensado para ser utilizado aquí).

que suelo hacer lo siguiente, que está haciendo un juego de palabras tipo, y es la forma recomendada para hacerlo, de acuerdo con la página de manual de dlopen (que se trata de hacer lo contrario - la fundición devoid*a una función puntero). Tomar la dirección del puntero a la función le dará un puntero a los datos: Puntero a un puntero a la función. Esto le permitirá lanzarlo al void*. Finge que apunta a un void* (en lugar de un puntero a la función), y luego lo lee.

Test* (*pF)(void **a); 
void *p = *(void**)(void*)&pF; 

El reparto intermediario para void* hace que sea equivalente a usar dos static_cast 's internamente, y hace que sea reservado sobre GCC advertencia acerca de un juego de palabras tipo. Usando C++ Estilo lanza, esto parece una combinación de dos static_cast 's

void *p = *static_cast<void**>(static_cast<void*>(&pF)); 

He encontrado que el uso de esta técnica, GCC da cuenta de forma automática cuando el los tipos izquierdo y derecho difieren en tamaño, y escupir una advertencia en ese caso. Huelga decir que al igual que con todas las técnicas que tratan de evitar esta limitación, este es un comportamiento indefinido.


Si usted tiene una función, y quiere un señalador void* a él, usted puede hacerlo todo en una sola línea, pero eso es un poco engorroso en la sintaxis. Aquí es cómo esto se puede hacer, pero yo no lo recomiendo si usted tiene problemas para leerlo - sin embargo es posible utilizarlo dentro de una macro

// using the function declaration you provided 
Test** pF(void **a); 
void *p = *(void**)(void *) &(Test** (* const&)(void **a))&pF; 

El truco para empezar a ser capaz de hacer el tipo, pero es transformar el puntero de la función temporal en una referencia de valor l a const, que puede tomar la dirección de, y luego proceder como se indicó anteriormente.

El uso de moldes de estilo C++ explícitos static_cast, esto se ve mucho más complicado, porque hay que tener en cuenta la constness. El estilo de C fundido se trató automáticamente con eso. ¡Que te diviertas!

int main() { 
    Test** pF(void **a); 
    void *p = *static_cast<void* const*>(
     static_cast<void const*>(
     &static_cast<Test** (* const&)(void **a)>(&pF))); 
} 
+0

Gracias por esto - el código hace que mis ojos se rastreen, pero parece funcionar correctamente y evita advertencias/errores incluso con un máximo de niveles de advertencia en gcc y clang. – snogglethorpe

Cuestiones relacionadas