В моих проектах мне нужно получить доступ к значению макроса $(SolutionDir) во время выполнения. Для этого я попытался добавить записи в предварительном процессоре, такие как DEBUG_ROOT=$(SolutionDir) или DEBUG_ROOT=\"$(SolutionDir)\", но это приводит к различным ошибкам компилятора из-за недопустимых управляющих последовательностей, поскольку $(SolutionDir) содержит одиночные символы \ (например, $(SolutionDir) = c:\users\lukas\desktop\sandbox\).
Есть ли простой способ передать значение макроса $(SolutionDir) в мой код?
Фон
Я использую функцию OutputDebugString(..) довольно много в своих отладочных сборках, чтобы увидеть, что делает мой код.
/* debug.h */
#define STRINGIFY(x) #x
#define TOSTRING(x) STRINGIFY(x)
#define LOCATION __FILE__ "(" TOSTRING(__LINE__) ") : "
#if !defined(DEBUG_ROOT)
#define DEBUG_ROOT "#" /* escape string to force strstr(..) to fail */
#endif
/*
** DBGMSG macro setting up and writing a debug string.
** Note: copying the strings together is faster than calling OutputDebugString(..) several times!
** Todo: Ensure that size of dbgStr is not exceeded!!!
*/
#define DBGMSG(text) \
{ \
char dbgStr[1024]; \
char *pFile; \
pFile = strstr(LOCATION, DEBUG_ROOT); \
if (pFile == LOCATION) \
{ \
wsprintf(dbgStr, ".%s", pFile + strlen(DEBUG_ROOT)); \
} \
else \
{ \
wsprintf(dbgStr, "%s", LOCATION); \
} \
wsprintf(dbgStr, "%s%s", dbgStr, text); \
OutputDebugString(dbgStr); \
}
/* somewhere in the code */
DBGMSG("test")
Использование отрезка приведет к распечатке типа c:\users\lukas\desktop\sandbox\testconsole\main.c(17) : test в окне вывода Visual Studio. Это ускоряет поиск местоположения внутри вашего кода, вызвавшего распечатку, поскольку вы можете просто дважды щелкнуть по строке окна вывода, а Visual Studio автоматически переместится в указанное расположение кода.
Поскольку в зависимости от местоположения решения абсолютный путь (__FILE__ расширяется до абсолютного пути), заголовок строк отладки может занять довольно много времени. Я видел, что Visual Studio достаточно умен, чтобы понимать относительные пути, например. корневой каталог решения. Чтобы уменьшить длину строк, я проверяю, находится ли __FILE__ в каталоге DEBUG_ROOT, и если это так, я заменяю DEBUG_ROOT простым '.', чтобы создать относительный путь к DEBUG_ROOT. Поэтому, если я пишу #define DEBUG_ROOT "c:\\users\\lukas\\desktop\\sandbox", окончательная строка отладки вышеприведенного примера будет .\testconsole\main.c(17) : test. В настоящее время я устанавливаю значение DEBUG_ROOT в определениях препроцессора проекта.
Поскольку несколько человек работают над проектом, это не умное движение, чтобы иметь абсолютный путь в настройках проекта, поскольку каждый член команды может проверять исходные файлы на другой корневой каталог. Поэтому я попытался использовать макрос $(SolutionDir) для создания чего-то вроде DEBUG_ROOT=\"$(SolutionDir)\\". Но, делая это, у меня проблемы. Поскольку $(SolutionDir) = c:\users\lukas\desktop\sandbox\ расширение DEBUG_ROOT приводит к undefined escape-последовательностям, неисчерпаемым строкам и намного более уродливым ошибкам компилятора...
Решение
Основываясь на ответе kfsone, я придумал следующее решение, позволяющее передать любое значение Visual Макрос Studio, такой как $(SolutionDir) в ваш код. Следующее решение не зависит от используемой версии Visual Studio и языка C/С++.
Добавление SOLUTION_DIR=\"$(SolutionDir)" в записи препроцессора вашего проекта приводит к командной строке компилятора, которая выглядит примерно так:
/Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "SOLUTION_DIR=\"C:\Users\Lukas\Desktop\sandbox\""
/Gm /EHsc /RTC1 /MDd /Fo"Debug\\" /Fd"Debug\vc80.pdb" /W3 /nologo /c /Wp64 /ZI /TP
/errorReport:prompt
Обратите внимание, что $(SolutionDir) предшествует \", чтобы создать " с символом infront значения $(SolutionDir), но заканчивается одним ". Глядя на командную строку компилятора, показано, что завершающий " экранируется последним \ из $(SolutionDir).
Использование SOLUTION_DIR в вашем коде приводит к неизвестным escape-последовательностям, и строка заканчивается удалением всех \ символов. Это выполняется компилятором, который расширяет SOLUTION_DIR и intepretes \ как начало escape-последовательности.
Использование макроса TOSTRING(x) моего кода, вышедшего выше, решает эту проблему, поскольку он заставляет компилятор использовать строку, как она есть без дальнейшей обработки.
#define STRINGIFY(x) #x
#define TOSTRING(x) STRINGIFY(x)
#define SOLUTION_DIR2 TOSTRING(SOLUTION_DIR)
// the following line may cause compiler warnings (unrecognized character escape sequence)
printf("%s\n", SOLUTION_DIR); // prints C:UsersLukasDesktopsandbox
// the following line compiles without any warnings
printf("%s\n", SOLUTION_DIR2); // prints "C:\Users\Lukas\Desktop\sandbox"
Отсюда просто простой способ сделать некоторую строчную магию, чтобы удалить символы " из SOLUTION_DIR2.
