在我的项目中,我需要在运行时访问 $(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")
使用该代码片段将在Visual Studio的输出窗口中打印出类似于c:\users\lukas\desktop\sandbox\testconsole\main.c(17) : test
的内容。这加速了查找导致打印的代码位置,因为您只需双击输出窗口中的行,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
会导致未定义的转义序列、未终止的字符串和许多丑陋的编译器错误...
解决方案
根据kfsone的答案,我想出了以下解决方案,使得可以将任何Visual Studio宏(如$(SolutionDir)
)的值传递到您的代码中。以下解决方案与使用的Visual Studio版本和语言C/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)
前面有一个 \"
来创建 "
字符,但是以单个 "
结束。查看编译器的命令行显示,终止的 "
是由 $(SolutionDir)
的最后一个 \
转义的。在代码中使用
SOLUTION_DIR
会导致未知的转义序列,并且该字符串中所有的 \
字符都被删除。这是由编译器执行的,它展开了 SOLUTION_DIR
并将 \
解释为转义序列的开始。使用我上面发布的代码中的
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
中删除"
字符。
$SolutionDir
,但是只有反斜杠存在问题。是这样吗?如果是这样,你是如何做到的(我无法弄清楚)。另外,你使用的VS版本是什么? - Mottiargv [0]
和GetCurrentDirectory()
会引入某种初始化,这也有缺点,即Visual Studio无法找到嵌套库的文件。这就是为什么我将解决方案目录用作DEBUG_ROOT
的原因,这将确保所有调试消息被正确修剪。因此,我正在寻找一种将解决方案目录传递给我的代码的方法,但我担心使用$(SolutionDir)
宏不起作用。 - Lukas Thomsenchar *debugRoot = DEBUG_ROOT;
并使用调试器查看了其内容。似乎Visual Studio在编译时已经转义了$(SolutionDir)
的值,因为所有的 '\' 字符都消失了,而且没有办法恢复它 :( - Lukas Thomsen