如何将Visual Studio宏值转换为预处理器指令?

在我的项目中,我需要在运行时访问$(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的输出窗口中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导致未定义的转义序列,未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)的字面值,但是会被一个单独终止。”查看编译器的命令行显示终止"是终止的\$(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 ++ 11function,原始字符串文字,可在Visual Studio 2013及更高版本中使用,它允许您执行此操作。 语法是

 'R"'  '('  ')'  '"' 

例如,如果你选择“?:?” 作为你的分隔符

 R"?:?(don't\escape)?:?" 

或者如果你选择“Foo123”

 R"Foo123(don't\escape)Foo123" 

但是对于这个演示,我要去? 作为单字符分隔符,因为我们知道它在Windows文件名中是非法的。

现在您可以设置项目级预处理器定义:

 DIR=R"?(C:\\Temp\\)?" 

然后以下代码生成预期的输出

 #include  int main() { std::cout << DIR << '\n'; } 

 C:\\Temp\\ 

代替

 C:\Temp\ 

现在要捕获SolutionDir宏,就像它一样简单

 DIR=R"?($(SolutionDir))?" 

如果这很痛苦,您可以在属性表中添加自定义宏。 转到“Property Explorer”并右键单击您的项目,添加一个新的属性表,将其命名为“ProjectMacros.props”或其他内容。

展开项目并选择其中一个配置,例如debug,双击“PropertySheet”值打开“PropertySheet PropertyPages”并选择“UserMacros”

PropertySheet宏页面

点击“添加宏”

 Name: RawSolutionDir Value: R"?path?($(SolutionDir))?path?" 

您现在应该能够使用预处理器条目

 SOLUTIONDIR=$(RawSolutionDir)