在我的项目中,我需要在运行时访问$(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的答案,我提出了以下解决方案,该解决方案可以将$(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)
前面有一个\"
来创建"
的{{1}}前面的$(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
字符。
答案 0 :(得分:12)
有一个C ++ 11功能,原始字符串文字,在Visual Studio 2013及更高版本中可用,它允许您这样做。语法是
'R"' <delimiter> '(' <string> ')' <delimiter> '"'
e.g。如果你选择“?:?”作为你的分隔符
R"?:?(don't\escape)?:?"
或者如果您选择“Foo123”
R"Foo123(don't\escape)Foo123"
但是对于这个演示,我要去?作为单字符分隔符,因为我们知道它在Windows文件名中是非法的。
现在您可以设置项目级预处理器定义:
DIR=R"?(C:\\Temp\\)?"
然后以下代码生成预期输出
#include <iostream>
int main() {
std::cout << DIR << '\n';
}
写入
C:\\Temp\\
而不是
C:\Temp\
现在要捕获SolutionDir宏,它就像
一样简单DIR=R"?($(SolutionDir))?"
如果这很痛苦,您可以在属性表中添加自定义宏。转到“Property Explorer”并右键单击您的项目,添加一个新的属性表,将其称为“ProjectMacros.props”或其他内容。
展开您的项目并选择其中一个配置,例如调试,双击“PropertySheet”值打开“PropertySheet PropertyPages”并选择“UserMacros”
点击“添加宏”
Name: RawSolutionDir
Value: R"?path?($(SolutionDir))?path?"
您现在应该可以使用预处理器条目
SOLUTIONDIR=$(RawSolutionDir)