【发布时间】:2015-05-25 12:56:43
【问题描述】:
在我的项目中,我需要在运行时访问 $(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")
使用 snipped 会在 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 的回答,我提出了以下解决方案,可以将$(SolutionDir) 等Visual Studio 宏的任何值传递到您的代码中。以下解决方案与使用的 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) 的值前面创建一个 " 字符,但由单个 " 终止。查看编译器的命令行显示终止 " 被 $(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 中删除 " 字符。
【问题讨论】:
-
从您的问题看来,您可以在 C++ 中使用
$SolutionDir,但只会遇到反斜杠的问题。是这样吗?如果是这样,您是如何做到的(我无法弄清楚)。还有你用的是什么版本的VS? -
嗯,不,它不是 $(SolutionDir)。您的 main.c 存储在项目目录中。当然不适用于库项目中的代码。您需要在运行时修剪路径,通过 argv[0] 和 GetCurrentDirectory() 将项目目录交给银盘。
-
@Motti:我不能直接在我的代码中使用
$(SolutionDir)。因此,我通过在 Visual Studio 的项目设置中定义预处理器指令DEBUG_ROOT=\"$(SolutionDir)\\"来使用解决方法将$(SolutionDir)传递到我的代码中。但显然我得到了无效的转义序列,因为 Visual Studio 使用'\'而不是'/'。 -
似乎您只需要在启动时正确转义 DEBUG_ROOT 一次,或者可能是第一次调用 log 函数并使用该变量。
-
@HansPassant:我已经发布了在运行时修剪路径的整个代码。你是对的,我用我也修复的路径做了一个复制/粘贴错误。使用
argv[0]和GetCurrentDirectory()会引入某种初始化,这也具有Visual Studio 找不到嵌套库文件的缺点。这就是我将解决方案目录用作DEBUG_ROOT的原因,这将确保正确修剪所有调试消息。所以我正在寻找一种将解决方案目录传递到我的代码中的方法,但我担心使用$(SolutionDir)宏将不起作用。
标签: c++ c visual-studio