如何将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的输出窗口中。这可以加快在代码中导致打印输出的位置的查找速度,因为您只需双击输出窗口的行,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”

PropertySheet宏页面

点击“添加宏”

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

您现在应该可以使用预处理程序条目

SOLUTIONDIR=$(RawSolutionDir)

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章

将C预处理器转换为Rust

在xcode 6中将预处理器宏添加到目标

如何在msvc预处理器中打印宏?

在Visual Studio中的哪里指定预处理器指令?

如何防止c预处理器宏扩展为重复的值?

如何使预处理器宏贪婪?

如何在包含文件中使用预处理器宏?

将预处理器宏__FUNCTION__与字符串连接

用于在Visual Studio中使用整个项目或解决方案的参数定义预处理器宏的选项

C预处理器:迭代地将宏扩展为逗号分隔的列表

Tensorflow如何定义预处理器宏?

如何在GCC基本asm中使用预处理器宏?

如何通过MinGW Make定义预处理器宏

C / C ++:预处理器指令应如何在宏参数列表上工作?

如何测试C预处理器-D标志值?

如何根据预定义的宏(C ++预处理器)编写有效令牌?

如何区分预处理器和编译器指令?

预处理器如何知道将HEADER_H转换为header.h?

如何在Visual Studio中指定预处理器选项

使用 Visual Studio 2015 的 C 预处理器

在 Visual Studio 中重命名预处理器指令中的变量

如何在 Visual Studio 2015 的 Xamarin 项目中更改预处理器 #if 代码的颜色?

如何将处理器编号转换为 32 位十六进制值

如何将 NiFi 处理器错误转换为 Flowfile 属性?

将 CMake 选项转换为预处理器定义的更简单/更简洁的机制?

如何将其更改为预处理器宏

如何定义预处理器宏名称

如何在宏中扩展预处理器变量?

MSVC:如何在从命令行传递的预处理器宏的值中包含 # 字符