Как преобразовать сцепленные строки в широкий символ с препроцессором C? - PullRequest
7 голосов
/ 03 февраля 2010

Я работаю над проектом, в котором много константных строк, образованных конкатенацией (числа и т. Д.).

Например, у меня есть макрос LOCATION, который форматирует __FILE__ и __LINE__ в строку, которую я могу использовать, чтобы узнать, где я нахожусь в коде, при печати сообщений или ошибок:

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

Таким образом, это будет форматировать местоположение как "file.cpp (42)". Проблема в том, когда я пытаюсь преобразовать результат в широкую строку:

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

Это прекрасно работает с GCC, и в моем коде вставляется L "file.cpp (42)". Тем не менее, при попытке это с MSVC ++ (с использованием Visual C ++ 2008 Express), я получаю сообщение об ошибке:

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

Я понимаю, что префикс L добавляется только к первому члену в моем выражении. Я также попробовал это:

#define _WIDEN(x) L ## #x

Который "работает", но дает строку L"\"file.cpp\" \"(\" \"42\" \")\"", что, очевидно, не очень удобно (и не то, что я ищу), особенно если учесть, что этот макрос прост по сравнению с другими макросами.

Итак, мой вопрос: как я могу применить его ко всему выражению в MSVC ++, чтобы я мог получить тот же результат, что и с GCC? Я бы предпочел не создавать вторую строку с токенами общего назначения, потому что тогда мне пришлось бы поддерживать два макроса для каждого, что не очень удобно и может привести к ошибкам. Кроме того, мне также нужна узкая версия каждой строки, поэтому, к сожалению, использование широких строк тоже не вариант.

Ответы [ 3 ]

11 голосов
/ 03 февраля 2010

В соответствии со стандартом C (он же «ISO-9899: 1999», он же «C99»), Visual C неверен, а gcc верен. Этот стандарт гласит, раздел 6.4.5 / 4:

На этапе 6 трансляции последовательности многобайтовых символов, заданные любой последовательностью смежных символов и литералов с широкими строками, объединяются в одну последовательность многобайтовых символов. Если какой-либо из токенов является токеном с широкими строковыми литералами, результирующая многобайтовая последовательность символов обрабатывается как литерал с широкими строковыми литералами; в противном случае он рассматривается как строковый литерал.

Так что вы можете подать жалобу. Возможно, предыдущая версия стандарта C (он же «C89», он же «C90», он же «ANSI C») не требовал объединения широких и неширокых строк. Хотя C99 уже более десяти лет, похоже, что Microsoft не заинтересована в том, чтобы привести в соответствие свой компилятор C. Некоторые пользователи сообщали о возможности доступа к некоторым функциям «C99» путем компиляции кода C, как если бы это был код C ++, потому что C ++ включает эти функции - и для C ++ Microsoft приложила усилия. Но это не распространяется на препроцессор.

На диалекте C89 я думаю, что то, что вы ищете, невозможно (на самом деле я в этом совершенно уверен, и, поскольку я написал свой собственный препроцессор, я думаю, что знаю, о чем говорю). Но вы можете добавить дополнительный параметр и распространить его:

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

, который должен работать как на gcc, так и на Visual C (по крайней мере, он работает для меня, используя Visual C 2005).

Примечание: вы не должны определять макросы с именем, начинающимся с подчеркивания. Эти имена зарезервированы, поэтому, используя их, вы можете столкнуться с некоторыми именами, используемыми в системных заголовках или в будущих версиях компилятора. Вместо _WIDEN используйте WIDEN_.

1 голос
/ 03 марта 2014

Просто использование пустого широкого строкового литерала должно работать:

 #define WLOCATION L"" __FILE__ "(" STR(__LINE__) ")"
1 голос
/ 03 февраля 2010

Чтобы объединить две широкие литеральные строки, вы можете использовать

L"wide_a" L"wide_b"

Таким образом, вы можете определить

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

(Примечание: не проверено на MSVC ++)

...