在我的项目中,我需要$(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
导致未定义的转义序列,未终止的字符串和更多难看的编译器错误...
根据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)
以a开头,\"
以创建a"
的值前面的字符,$(SolutionDir)
但以single结尾"
。纵观编译器的命令行显示终端"
是由最后逃脱\
的$(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
。
Visual Studio 2013及更高版本提供了C ++ 11功能,即原始字符串文字,您可以通过此功能执行此操作。语法是
'R"' <delimiter> '(' <string> ')' <delimiter> '"'
例如,如果您选择“?:?” 作为您的分隔符
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))?"
如果这很麻烦,则可以在属性表中添加一个自定义宏。转到“属性资源管理器”,右键单击您的项目,添加一个新的属性表,将其称为“ ProjectMacros.props”或其他名称。
展开您的项目并选择一种配置,例如调试,双击“ PropertySheet”值以打开“ PropertySheet PropertyPages”并选择“ UserMacros”
点击“添加宏”
Name: RawSolutionDir
Value: R"?path?($(SolutionDir))?path?"
您现在应该可以使用预处理程序条目
SOLUTIONDIR=$(RawSolutionDir)
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句