2010-10-03 21 views
6

En C, no es un error lanzar punteros hacia y desde void *.Relajar void * casting en C++

Un obstáculo importante en la migración a C++ es la necesidad de lanzar punteros cuando regrese de funciones relacionadas con punteros genéricos como malloc y funciones declaradas en mi propio código como void *block_get(Blkno const blkno);.

Sin embargo, mi código está destinado a ser compilado por C y compiladores de C++ con éxito. Si proporciono moldes explícitos en todas partes por el bien de C++, deben ser moldes de estilo C y es posible que esté enmascarando errores debido a la fundición de tipos de puntero ay desde los tipos de puntero de ambos idiomas.

Mi error de referencia es la siguiente:

struct Cpfs *cpfs = calloc(1, sizeof(*cpfs)); 

que a MSVC produce:

error C2440 error de 2: 'inicialización': no ​​se puede convertir de 'void *' a 'CPFS *' e: \ src \ CPF \ cpfs.c 179

Evidentemente no puedo usar new o static_cast la que me gustaría usar, naturalmente, si ya no estaba usando C. ¿Cuál es la mejor manera de proporcionar seguridad máxima de tipo que rodea void * para cada idioma con una mínima verborrea?

+4

¿Por qué necesita hacer un puerto al subconjunto común de C y C++? Como has descubierto, limita severamente qué C (o qué C++) puedes usar. Casi todos los entornos que tienen C++ disponible también tienen C disponible y también le permiten vincular archivos de objetos C++ y C juntos. No puedo ver qué ganancia obtienes transfiriendo al subconjunto común. –

+0

Para * tipo de seguridad en C * (que es un poco difícil), haga una función para cada estructura cuyo trabajo es asignar y devolver un puntero a una nueva estructura. Del mismo modo, realice una función para liberar la estructura. – rwong

+1

Me gustaría saber la razón para intentar escribir un programa que compila para dos idiomas diferentes. Eso es típicamente algo dejado para acertijos. – GManNickG

Respuesta

0

Tal vez algo como esto? (No probados, sin compilador disponibles, que no utilizan las macros muy a menudo):

#ifdef __cplusplus 
    #define pointer_cast(type, pointer) reinterpret_cast<type>(pointer) 
#else 
    #define pointer_cast(type, pointer) (type)(pointer) 
#endif 
+0

¿Por qué no evitar por completo el molde inútil cuando '__cplusplus' no está definido? –

+0

Terminé creando una macro VOID_CAST que se lanza solo cuando es necesario. –

5

Sugeriría simplemente usar moldes de estilo C, o envolver el molde en una macro que se expande a nada (en C) o static_cast en C++.

+3

Un 'reinterpret_cast' de' void * 'no es el mejor elenco. 'static_cast' es suficiente y menos probable que genere silenciosamente una fuente no válida a un tipo de puntero. –

+0

@Charles Bailey: Ok, editado. – Hasturkun

0

La única solución que sé es que hacer una conversión explícita:

struct Cpfs *cpfs = (Cpfs*)calloc(1, sizeof(*cpfs)); 

Aquí los compiladores están satisfechos. También recuerde, que para compiladores más antiguos malloc puede devolver char *.

hth

Mario

0

hacer una función de sustitución de asignador que se puede definir de manera diferente para C y C++ se basa: - Algo como esto en un archivo de cabecera:

#ifdef __cplusplus 
template<typename TypeT> 
TypeT* MyAlloc(TypeT** pOut,size_t cb){ 
    *pOut = static_cast<TypeT*>(malloc(cb)); //aint c++ pretty. 
    return *pOut; 
} 
#else 
    extern void* MyAlloc(void** ppv, size_t cb); 
#endif 

Ahora tiene, en compilaciones de C++, una función que puede inferir el tipo de cosas que trata, y en compilaciones C, es una función normal que devuelve un vacío *.

El único problema es la necesidad de pasar el puntero para asignar: el compilador de C++ no intenta deducir un parámetro de plantilla basado únicamente en el tipo de retorno de una función afaik.Por lo que se podría llamar agnóstico como esto: -

int *p; 
if(MyAlloc(&p,sizeof(int)*n)){ 
    ... 
+0

"// aint C++ bastante". Sí, cuando usas 'new'. – GManNickG

+1

'new' es aún peor ya que los compiladores de C++ no deducen los parámetros de la plantilla de clase de la llamada del constructor. Así que tuve que escribir algo como esto el otro día: 'SomeClass * pWrapper = new SomeOtherClass (this, & SomeOtherClass :: method) method;' –

+0

C++ nunca es bonito. –

2

Si su compilador soporta decltype(), puede utilizar un poco de magia macro para no tener que repetir de forma explícita el nombre del tipo (y, gracias a sizeof, el tamaño del elemento):

#ifdef __cplusplus 
#define my_calloc(VAR, COUNT) \ 
    static_cast<decltype(VAR)>(std::calloc(COUNT, sizeof *VAR)) 
#else 
#define my_calloc(VAR, COUNT) calloc(COUNT, sizeof *VAR) 
#endif 

Ejemplo de uso:

#ifdef __cplusplus 
#include <cstdlib> 
#else 
#include <stdlib.h> 
#endif 

struct Cpfs *cpfs = my_calloc(cpfs, 42); 

La solución más limpia sería probablemente sólo tiene que utilizar un compilador C y vincular los archivos de objetos, aunque ...

+0

decltype se parece a typeof, usar un compilador c no es una opción, msvc no tiene soporte para c99.C++ lo compila mejor que un compilador c de medio punto –

+1

@Matt: puede usar MinGW para compilar la parte C de su proyecto y aún usar MSVC para la parte C++ (consulte, por ejemplo, http://stackoverflow.com/questions/2096519/ from-mingw-static-library-a-visual-studio-static-library-lib) – Christoph

+0

@Matt Joiner: programar en la intersección de C99 y C++ no es sustancialmente diferente de solo programar C89, es simplemente más incómodo y molesto. Estás escribiendo en una versión a medias de C tal como está, usar un compilador de C a medias no es peor, y al menos C89 es un estándar. –