В моих проектах мне нужно получить доступ к значению макроса $(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
.