lenguaje - preprocesamiento c++
¿Cómo convertir cadenas concatenadas a wide-char con el preprocesador C? (3)
Estoy trabajando en un proyecto en el que tengo muchas cadenas constantes formadas por concatenación (números, etc.).
Por ejemplo, tengo una macro LOCATION
que formatea __FILE__
y __LINE__
en una cadena que puedo usar para saber dónde estoy en el código, al imprimir mensajes o errores:
#define _STR(x) # x
#define STR(x) _STR(x)
#define LOCATION __FILE__ "(" STR(__LINE__) ")"
Entonces, esto formatearía una ubicación como "file.cpp (42)". El problema es cuando trato de convertir el resultado a una cadena ancha:
#define _WIDEN(x) L ## x
#define WIDEN(x) _WIDEN(x)
#define WLOCATION WIDEN(LOCATION)
Esto funciona bien con GCC, y los resultados en L "file.cpp (42)" se insertan en mi código. Sin embargo, cuando intento esto con MSVC ++ (usando Visual C ++ 2008 Express), aparece un error:
error: Concatenating wide "file.cpp" with narrow "("
Entiendo que el prefijo L
se agrega solo al primer término en mi expresión. También he intentado esto:
#define _WIDEN(x) L ## #x
Lo cual "funciona", pero da la cadena L"/"file.cpp/" /"(/" /"42/" /")/""
que obviamente no es muy conveniente (y no es lo que estoy buscando), especialmente teniendo en cuenta que esta macro es simple en comparación con otras macros.
Entonces, mi pregunta es: ¿cómo puedo hacer que se aplique a toda la expresión en MSVC ++, para que pueda obtener el mismo resultado que obtengo con GCC? Preferiría no crear una segunda cadena con tokens de ancho total, porque tendría que mantener dos macros para cada uno, lo que no es muy conveniente y puede provocar errores. Además, también necesito la versión angosta de cada cadena, por lo que el uso de cadenas de todo el ancho tampoco es una opción, desafortunadamente.
De acuerdo con el estándar C (también conocido como "ISO-9899: 1999", también conocido como "C99"), Visual C está equivocado y gcc es correcto. Esa norma establece, sección 6.4.5 / 4:
En la fase de traducción 6, las secuencias de caracteres multibyte especificadas por cualquier secuencia de caracteres adyacentes y tokens literales de cadena ancha se concatenan en una única secuencia de caracteres multibyte. Si alguno de los tokens son tokens literales de cadena ancha, la secuencia de caracteres multibyte resultante se trata como un literal de cadena ancha; de lo contrario, se trata como un literal de cadena de caracteres.
Entonces puedes presentar una queja. Podría decirse que la versión anterior del estándar C (también conocido como "C89", también conocido como "C90", también conocido como "ANSI C") no obligaba a fusionar cadenas amplias con cadenas no anchas. Aunque C99 tiene ahora más de diez años, parece que Microsoft no tiene interés en hacer que su compilador de C se conforme. Algunos usuarios han informado que pueden acceder a algunas características "C99" al compilar el código C como si fuera código C ++, porque C ++ incluye estas características, y para C ++, Microsoft hizo un esfuerzo. Pero esto no parece extenderse al preprocesador.
En el dialecto C89, creo que lo que estás buscando no es posible (de hecho, estoy bastante seguro de ello, y dado que he escrito mi propio preprocesador, creo que sé de lo que estoy hablando). Pero podría agregar un parámetro adicional y propagarlo:
#define W(x) W_(x)
#define W_(x) L ## x
#define N(x) x
#define STR(x, t) STR_(x, t)
#define STR_(x, t) t(#x)
#define LOCATION_(t) t(__FILE__) t("(") STR(__LINE__, t) t(")")
#define LOCATION LOCATION_(N)
#define WLOCATION LOCATION_(W)
que debería funcionar tanto en gcc como en Visual C (al menos, funciona para mí, usando Visual C 2005).
Nota al margen: no debe definir macros con un nombre que comience con un guión bajo. Estos nombres están reservados, por lo que al usarlos podría chocar con algunos nombres utilizados en los encabezados del sistema o en versiones futuras del compilador. En lugar de _WIDEN
, use WIDEN_
.
Para concatenar dos cadenas literales anchas podrías usar
L"wide_a" L"wide_b"
Entonces podrías definir
#define WLOCATION WIDEN(__FILE__) L"(" WIDEN(STR(__LINE__)) L")"
(Nota: no probado en MSVC ++)
Solo usar un literal de cadena ancha vacía debería funcionar:
#define WLOCATION L"" __FILE__ "(" STR(__LINE__) ")"