2010-02-03 8 views
7

Sto lavorando a un progetto in cui ho molte stringhe costanti formate da concatenazione (numeri, ecc.).Come convertire stringhe concatenate in wide-char con il preprocessore C?

Ad esempio, ho una macro LOCATION che formatta __FILE__ e __LINE__ in una stringa che posso usare per sapere dove mi trovo nel codice, quando si stampano messaggi o errori:

#define _STR(x) # x 
#define STR(x)  _STR(x) 
#define LOCATION __FILE__ "(" STR(__LINE__) ")" 

Quindi, questo sarebbe formattare una posizione come "file.cpp (42)". Il problema è quando si tenta di convertire il risultato a un livello di stringa:

#define _WIDEN(x) L ## x 
#define WIDEN(x) _WIDEN(x) 
#define WLOCATION WIDEN(LOCATION) 

Questo funziona bene con GCC, e si traduce in L "file.cpp (42)" viene inserito nel mio codice. Tuttavia, quando si cerca questo con MSVC++ (utilizzando Visual C++ 2008 Express), ottengo un errore:

error: Concatenating wide "file.cpp" with narrow "(" 

Capisco che il prefisso L viene aggiunto solo per il primo termine nella mia espressione. Ho anche provato questo:

#define _WIDEN(x) L ## #x 

Quali "opere", ma dà la stringa L"\"file.cpp\" \"(\" \"42\" \")\"" che ovviamente non è molto conveniente (e non quello che sto cercando), soprattutto considerando che questa macro è semplice rispetto ad altri macro.

Quindi, la mia domanda è: come posso farlo applicare all'intera espressione in MSVC++, così posso ottenere lo stesso risultato che ottengo con GCC? Preferirei non creare una seconda stringa con token all-wide, perché dovrei quindi mantenere due macro per ognuno, il che non è molto conveniente e può portare a bug. Inoltre, ho bisogno anche della versione ridotta di ogni stringa, sfortunatamente non è possibile utilizzare stringhe all-wide.

risposta

11

Secondo lo standard C (aka "ISO-9899: 1999" alias "C99"), Visual C è errato e gcc è corretto. Tale standard afferma, sezione 6.4.5/4:

Nella fase di conversione 6, le sequenze di caratteri multibyte specificate da una sequenza di caratteri adiacenti e token letterali a stringa ampia vengono concatenate in una singola sequenza di caratteri multibyte. Se uno qualsiasi dei token sono ampie stringa token letterale, la sequenza di caratteri multibyte risultante viene trattato come una stringa ampia letterale; in caso contrario, viene trattato come una stringa di caratteri letterale.

Quindi è possibile presentare un reclamo. Probabilmente, la versione precedente dello standard C (noto anche come "C89" o "C90" o "ANSI C") non richiedeva la fusione di stringhe ampie con stringhe non larghe. Sebbene C99 abbia ora più di dieci anni, sembra che Microsoft non abbia alcun interesse a rendere conforme il compilatore C. Alcuni utenti hanno segnalato di essere in grado di accedere ad alcune funzionalità "C99" compilando il codice C come se fosse codice C++, poiché il C++ include queste funzionalità e, per C++, Microsoft ha fatto uno sforzo. Ma questo non sembra estendersi al preprocessore.

Nel dialetto C89, penso che ciò che stai cercando non sia possibile (in realtà ne sono abbastanza sicuro, e dal momento che ho scritto il mio preprocessore credo di sapere di cosa sto parlando). Ma si potrebbe aggiungere un parametro in più e propagarla:

#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) 

che dovrebbe funzionare su entrambi GCC e Visual C (almeno, funziona per me, utilizzando Visual C 2005).

Nota a margine: non è necessario definire macro con un nome che inizia con un trattino basso. Questi nomi sono riservati, quindi usandoli potresti scontrarti con alcuni nomi usati nelle intestazioni di sistema o nelle future versioni del compilatore. Invece di _WIDEN, utilizzare WIDEN_.

+0

Funziona, anche se è una specie di sintassi scomoda e ha un sacco di spese generali ... ma funziona e non devo mantenere più macro. Eventualmente, Microsoft aggiornerà il suo preprocessore per supportare più funzionalità C99, le cose sarebbero molto più semplici in questo modo. Grazie! –

1

per concatenare due ampie stringhe letterali si potrebbe usare

L"wide_a" L"wide_b" 

Così si potrebbe definire

#define WLOCATION WIDEN(__FILE__) L"(" WIDEN(STR(__LINE__)) L")" 

(Nota: non testato su MSVC++)

+1

Che avrebbe funzionato, ma questo macro è solo uno dei tanti, e preferisco definirli solo una volta e non è necessario mantenere due copie (ampia e stretta) di ogni macro, soprattutto se li modifico un giorno. Potrebbe facilmente diventare un dolore da mantenere, anche se devo ammettere che funzionerebbe! –

0

Proprio utilizzando una vasta stringa vuota letterale dovrebbe funzionare:

#define WLOCATION L"" __FILE__ "(" STR(__LINE__) ")"