2010-02-03 14 views
7

Pracuję nad projektem, gdzie mam wielu stałych ciągów utworzone przez łączenie (numery, etc.).Jak przekonwertować konkatenowanych struny do szerokiego char z C preprocesora?

Na przykład, mam LOCATION makro, które formatuje __FILE__ i __LINE__ na ciąg znaków, które można wykorzystać, aby wiedzieć, gdzie jestem w kodzie, podczas drukowania wiadomości lub błędy:

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

Tak, to byłoby sformatuj lokalizację taką jak "file.cpp (42)". Problem jest, gdy próbuję przekonwertować wynik do szerokiej-string:

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

Działa to dobrze z GCC, a wyniki w L „file.cpp (42)” jest włożona w moim kodu. Jednak gdy próbuje to z MSVC++ (przy użyciu Visual C++ 2008 Express), pojawia się błąd:

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

Rozumiem, że przedrostek L zostanie dodany tylko do pierwszego terminu w mojej wypowiedzi. Próbowałem już też to:

#define _WIDEN(x) L ## #x 

, która „działa”, ale daje ciąg L"\"file.cpp\" \"(\" \"42\" \")\"" co nie jest oczywiście bardzo wygodne (a nie to, czego szukam), zwłaszcza biorąc pod uwagę, że to makro jest prosty w porównaniu do innych makra.

Więc moje pytanie brzmi: w jaki sposób mogę je zastosować do całego wyrażenia w MSVC++, więc mogę uzyskać ten sam wynik otrzymuję z GCC? I raczej nie utworzyć drugi ciąg z szerokimi wszystkie żetony, bo będę wtedy musiał utrzymać dwa makra dla każdego z nich, które nie jest bardzo wygodne i może prowadzić do błędów. Dodatkowo, potrzebuję wąskiej wersji każdego łańcucha, więc używanie nieskończonych łańcuchów niestety nie jest opcją.

Odpowiedz

11

Zgodnie ze standardem C (aka "ISO-9899: 1999" vel "C99"), Visual C jest nieprawidłowe, a gcc poprawne. Ten standard stwierdza, sekcja 6.4.5/4:

W fazie translacji 6, wielobajtowe sekwencje znaków określone przez dowolną sekwencję sąsiednich znaków i szerokich literowych tokenów literowych są łączone w jedną wielobajtową sekwencję znaków. Jeżeli którykolwiek ze znaczników są szerokie w ciągach w znaczniki, uzyskane sekwencje wielobajtowymi jest traktowany jako szeroki ciąg dosłownym; w przeciwnym razie jest traktowane jako literał ciągu znaków.

Więc można złożyć zażalenie. Prawdopodobnie poprzednia wersja standardu C (zwanego również "C89", zwanym również "C90", inaczej "ANSI C") nie wymagała łączenia szerokich ciągów z nie-szerokimi łańcuchami. Chociaż C99 ma teraz ponad dziesięć lat, wydaje się, że Microsoft nie jest zainteresowany dostosowaniem swojego kompilatora C. Niektórzy użytkownicy zgłaszali, że mogą uzyskać dostęp do niektórych funkcji "C99", kompilując kod C tak, jakby był to kod w C++, ponieważ C++ zawiera te funkcje - a dla C++ Microsoft dołożył starań. Ale wydaje się, że nie dotyczy to preprocesora.

W dialekcie C89 myślę, że to, czego szukasz, nie jest możliwe (właściwie jestem tego pewien i skoro napisałem własny preprocesor, myślę, że wiem o czym mówię). Ale można dodać dodatkowy parametr i propagować go:

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

który powinien pracować zarówno GCC i Visual C (przynajmniej, że pracuje dla mnie, za pomocą Visual C 2005).

Nota boczna: nie powinieneś definiować makr z nazwą zaczynającą się od podkreślenia. Te nazwy są zarezerwowane, więc używając ich można kolidować z niektórymi nazwami używanymi w nagłówkach systemowych lub w przyszłych wersjach kompilatora. Zamiast _WIDEN użyj WIDEN_.

+0

To działa, mimo że jest to rodzaj niezręcznej składni i ma dużo narzutów ... ale działa i nie muszę utrzymywać wielu makr. Mam nadzieję, że Microsoft zaktualizuje swój preprocesor, aby obsługiwał więcej funkcji C99, w ten sposób byłoby znacznie łatwiej. Dzięki! –

1

Aby złączyć dwa szerokie dosłowne ciągi można użyć

L"wide_a" L"wide_b" 

więc można określić

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

(uwaga: nie testowane na MSVC++)

+1

To będzie działać, ale to makro jest tylko jednym z wielu, i wolałbym je zdefiniować tylko raz i nie trzeba utrzymywać dwie kopie (szeroki i wąski) każdego makra, szczególnie jeśli edytuję je jeden dzień. Z łatwością może to być trudny do utrzymania, chociaż muszę przyznać, że to zadziała! –

0

Wystarczy za pomocą pustego szeroki ciąg dosłowne powinno działać:

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