de wchar_t a unsigned char

Iniciado por pikoc, 20 Mayo 2015, 10:57 AM

0 Miembros y 1 Visitante están viendo este tema.

pikoc

Hola:
Tengo una cadena wchar_t y tengo que pasarla a unsigned char, no me lo permite de ninguna manera, buscando en San Google, he encontrado esto:

Citarunsigned char * dirNameA = reinterpret_cast<unsigned char *>(dirName.c_str());

That might not work because c_str returns a const wchar_t *so you can also try:

unsigned char * dirNameA = reinterpret_cast<unsigned char *>(
                              const_cast<wchar_t *>(dirName.c_str())
                          );

Que lo he adaptado a mi código de la siguiente manera:

Citarunsigned char  valor = reinterpret_cast<unsigned char >(const_cast<wchar_t >(princ));

Pero sigo sin conseguir nada... bueno, más que no conseguir nada, me da el siguiente error:

error C2440: 'const_cast' : no se puede realizar la conversión de 'wchar_t' a 'wchar_t'

Alguien me puede ayudar?

Peregring-lk

#1
No he visto tu código original, pero seguramente, el ejemplo que has pillado sería equivalente al siguiente:

Código (cpp) [Seleccionar]

const wchar_t* wstr = /* lo que sea */; // Cadena const.
unsigned char* ustr = reinterpret_cast<unsigned char*>(const_cast<wchar_t*>(wstr));


Sin embargo, tú tendrás lo siguiente:

Código (cpp) [Seleccionar]

wchar_t* wstr = /* lo que sea */; // Cadena no-const.
unsigned char* ustr = reinterpret_cast<unsigned char*>(const_cast<wchar_t*>(wstr));


El error está en el `const_cast`. El `const_cast` sirve para quitar/poner el calificativo `const` a un puntero o una referencia. Ejemplos:

Código (cpp) [Seleccionar]

int a = 3;

// Ok, const_cast recibe un puntero (int*) y lo convierto a puntero-a-constante (const int*).
const int* ptr_a = const_cast<const int*>(&a);
const int* ptr2_a = &a; // Ok, la conversion a const tambien funciona de forma implicita.

// Aqui el `const_cast` es absolutamente necesario. Quitarle `const` a un puntero/referencia nunca
// es implicito (ponerselo si), y hay que forzarlo con `const_cast`.
int* ptr3_a = const_cast<int*>(ptr_a);

// Este es el error de tu codigo! `ptr3_a` no es constante, y el tipo de salida del casting, tampoco (int*),
// pero `const_cast` requiere que uno de los dos sea constante y el otro no! Si los dos son iguales
// te da un error de compilacion.
int* ptr4_a = const_cast<int*>(ptr3_a);


No he puesto ejemplos con referencias porque no es tu caso. ¿Cuál es tu problema? Que tu `const_cast<wchar_t*>(wstr)` está diciendo: "quítale el `const` a `wstr`", pero `wstr` resulta que no es `const`, así que no hay nada que quitar, y de ahí el error de compilación. De todas formas, utilizar un `const_cast` es peligroso, y podría provocar incluso violaciones de segmento (especialmente con cadenas). Si algo se marco como `const`, es por algo.

Bueno, continuemos. ¿Y qué es lo que hace `reinterpret_cast<tipo>(var)`? Pues sencillamente lo siguiente: coje la dirección de memoria de la variable que le has pasado, y a partir de ahí, coje `sizeof(tipo)` bytes y lo devuelve como un objeto de tipo `tipo`. Ajin! Completamente a pelo, y éso es muy peligroso.

Por ejemplo (relacionandolo con tu caso):

Código (cpp) [Seleccionar]

wchar_t wc = /* un caracter, el que sea */
unsigned char c = reinterpret_cast<unsigned char>(wc);


Veamos el peligro: al menos en mi arquitectura, `sizeof(wc) == 4`, mientras que `sizeof(char) == 1`. Al realizar dicho `reinterpret_cast`, el compilador coje el primer byte the `wc` y devuelve la secuencia de bits como un `unsigned char`, sin adaptar la representación ni nada, y `c` tendría una copia del primer byte the `wc`.

¿Qué pasa con cadenas/punteros?

Código (cpp) [Seleccionar]

wchar_t* wc = /* lo que sea. Cadena de 5 caracteres. */
unsigned char* c = reinterpret_cast<unsigned char*>(wc);


Aquí `wc` es un puntero (y todos los punteros tienen el mismo tamaño en una misma arquitectura), así que `reinterpret_cast`  devuelve la misma dirección de memoria, pero ahora desde `c` estás manipulando `unsigned char`s y no `wchar_t`s. Así que:

Código (cpp) [Seleccionar]

for (unsigned i = 0; i < 5; ++i)
   cout << c[i] << endl;


Aquí, tu crees que estás imprimiendo los 5 primeros carácteres de la cadena original (transformada como `unsigned char`), pero resulta que estás imprimiendo ¡los 4 primeros bytes del primer carácter de la cadena `wchar_t` original, más el primer byte de la segunda! Habría que hacerlo de la siguiente forma:

Código (cpp) [Seleccionar]

for (unsigned i = 0; i < 5 * sizeof(wchar_t); ++i)
   cout << c[i] << endl;


Además, utilizarlo de la siguiente forma tiene muchos riesgos, el más importante es que `wchar_t` es un tipo muy especial, cuya implementación es a gusto del compilador, así que en teoría, no hay forma de saber cómo está representado cada carácter. ¿Están los bytes puestos de abajo a arriba? ¿De arriba a bajo? ¿Con complemento a uno o a dos? ¿Cada carácter tiene 2 bytes, 4 u 8?

Así que, si trabajar con `reinterpret_cast` es peligroso de por sí (solo debe usarse en situaciones muy controladas), y utilizarlo para llegar de `wchar_t` a `char` lo es más aún.

Siempre que puedas, utiliza `char*`, a no ser que sea exclusivamente necesario.

pikoc

Osea... que estoy Jod%$#...
Sí, es totalmente necesario que utilice wchar_t, ya que yo recibo datos del puerto serie, y esos datos son guardados en una cadena tipo string, luego los paso a wchar_t para poder interpretarlos y que se interpreten bien en todo el código, pero no hay manera de verlos bien representados de otra manera.
Te comento; he quitado todo esto del reinterpret_cast y del const_cast y los he dejado con wchar_t en todos lados y va bien... el problema está al usar funciones externas que vienen dadas y supongo que es por lo que decías tú de cómo se ordenan los datos en el wchar_t.
Gracias
Seguiré buscando...  >:(

pikoc

Pero igualmente, conoces alguna manera de poder pasar (de modo seguro... ) de wchar_t a char??????