Ответ 1
Использование может использовать оператор склеивания токена, например:
#define WIDEN2(x) L ## x
#define WIDEN(x) WIDEN2(x)
#define WFILE WIDEN(__FILE__)
int main() {
wprintf("%s\n", WFILE);
return 0;
}
Рассмотрим эту программу:
#include <stdio.h>
int main() {
printf("%s\n", __FILE__);
return 0;
}
В зависимости от имени файла эта программа работает - или нет. Проблема, с которой я сталкиваюсь, заключается в том, что я хотел бы напечатать имя текущего файла безопасным способом. Однако, если в файле есть забавные символы, которые не могут быть представлены на текущей кодовой странице, компилятор выводит предупреждение (по праву):
?????????.c(3) : warning C4566: character represented by universal-character-name '\u043F' cannot be represented in the current code page (1252)
Как мне это решить? Я хотел бы сохранить строку, указанную в __FILE__
, например. UTF-16, чтобы я мог корректно печатать его на любой другой системе во время выполнения (путем преобразования сохраненного представления UTF-16 в то, что использует система времени исполнения). Для этого мне нужно знать:
__FILE__
? Кажется, что, по крайней мере, в Windows используется текущая системная кодовая страница (в моем случае, Windows-1252), но это просто гадание. Это правда?Мой реальный случай использования: у меня есть макрос, который отслеживает текущее выполнение программы, записывая текущую информацию о номере исходного кода/строки в файл. Это выглядит так:
struct LogFile {
// Write message to file. The file should contain the UTF-8 encoded data!
void writeMessage( const std::string &msg );
};
// Global function which returns a pointer to the 'active' log file.
LogFile *activeLogFile();
#define TRACE_BEACON activeLogFile()->write( __FILE__ );
Это прерывается, если текущий исходный файл имеет имя, которое содержит символы, которые не могут быть представлены текущей кодовой страницей.
Использование может использовать оператор склеивания токена, например:
#define WIDEN2(x) L ## x
#define WIDEN(x) WIDEN2(x)
#define WFILE WIDEN(__FILE__)
int main() {
wprintf("%s\n", WFILE);
return 0;
}
__FILE__
всегда будет расширяться до символьного строкового литерала, поэтому по существу он будет совместим с char const*
. Это означает, что реализация компилятора не имеет иного выбора, кроме использования исходного байтового представления имени исходного файла, которое оно представляет во время компиляции.
Независимо от того, является ли это что-то разумное в текущей локали или нет, не имеет значения, вы могли бы иметь имя исходного файла, содержащее в основном мусор, если ваша система времени выполнения и компилятор принимают его как допустимое имя файла.
Если у вас, как у пользователя, есть другой язык с разной кодировкой, чем используется в вашей файловой системе, вы увидите много???? или аналогично.
Но если оба ваших сайта согласны с кодировкой, достаточно простого printf
, и ваш терминал (или все, что вы используете для просмотра вывода) должен иметь возможность правильно печатать символы.
Итак, короткий ответ: он будет работать, только если ваша система совместима с кодировкой w.r.t. В противном случае вам не повезло, так как угадывание кодировок - довольно сложная задача.
Что касается кодировки, я собираюсь угадать, что используется файловой системой, возможно, Unicode.
Что касается того, как это сделать, как "сменить код вам что-то вроде:
#define TRACE_BEACON activeLogFile()->write( FixThisString(__FILE__ ));
std::string FixThisString(wchar_t* bad_string) { .....}
(Реализация FixThisString остается как упражнение для ученика.)
Лучшим решением является использование исходных имен файлов в наборе символов переносимых файлов [A-Za-z0-9._-]
. Поскольку Windows не поддерживает UTF-8, нет возможности для того, чтобы произвольные символы, отличные от ASCII, были представлены в обычных строках вне зависимости от вашего настроенного локального языка.
gcc, вероятно, не волнует; он обрабатывает все имена файлов как 8-битные строки, и поэтому, если имя файла доступно для gcc, его имя будет представлено. (Я знаю, что cygwin по умолчанию имеет среду UTF-8, а современный * nix обычно будет UTF-8.) Для MSVC вы могли бы использовать препроцессор для добавления L
к расширению __FILE__
и использовать %ls
, чтобы отформатировать его.
В MSVC вы можете включить Unicode и получить кодированные строки UTF-16. Это где-то в свойствах проекта. Кроме того, вы должны просто использовать wcout/cout not printf/wprintf. Windows нуждалась в Unicode перед тем, как существовал Unicode, поэтому у них была пользовательская многобайтовая кодировка символов, которая по умолчанию. Однако Windows поддерживает UTF16- это, например, С#.
#include <iostream>
int main() {
std::wcout << __WFILE__;
}