【问题标题】:How to create string literal from -D compiler defined variable of a Windows path如何从 -D 编译器定义的 Windows 路径变量创建字符串文字
【发布时间】:2024-04-25 19:35:01
【问题描述】:

在 Windows 下,我有一个包含 Windows 样式路径的环境变量。我想将该路径构建到我的程序中并将其打印出来。所以如果我的路径是 c:\top,我会使用 -DTOP=$(TOP) 将它传递给编译器。请注意,在将其传递给编译器之前,我无法将其转换为 c:\\top。

现在,我有相当于:

#define TOP=c:\top

我想要相当于:

char path[]="c:\\top";

我不能只使用字符串化操作符:

#define WRAP1(X) #X
#define WRAP(X) WRAP1(X)
char path[] = WRAP(TOP);

这只会导致编译器将其视为转义序列(即 \t)的字符串“c:\top”。

我认为一个可能的解决方案是构造一个字符串文字,但其他解决方案也可以。有没有办法使用宏来构造会产生的字符串文字:

char path[] = R"foo(c:\top)foo";

到目前为止,我所有的尝试都失败了,原因涉及 " 或 ( ) 或 \ 的变体。

谢谢。

【问题讨论】:

  • "请注意,在将其传递给编译器之前,我无法将其转换为 c:\\top" - 为什么?
  • 为什么不改为-DTOP=R"foo($(TOP))foo"?编辑:引号可能需要转义,因为这是一个命令行选项,如下所示:-DTOP="R""foo($(TOP))foo"""
  • 我处于无法更改的自定义编译环境中。当我指定 -D 时,我不能使用额外的引号。编译环境也使用引号来解析编译器的附加参数。
  • 你试过用正斜杠代替反斜杠吗?
  • 正确的答案是通知构建环境人员/团队您需要更改它。不要害怕“它无法完成”的场景,并在它损坏的地方修复它 - 即无法引用任何内容,或在命令行上更改任何内容!疯了!

标签: c++ c path string-formatting compiler-options


【解决方案1】:

您可以将定义的路径转换为字符串,方法是在其前面加上字符串化运算符#。但是,这只适用于宏。您实际上需要一个双宏才能使其正常工作,否则它只会打印 TOP。 将路径名放在引号中也很重要 - 哦,示例中的路径存储在 env PathDirName 下

为编译器定义路径-

/DTOP="\"$(PathDirName)\\""

在代码中使用

#define STRINGIZE2(x) #x
#define STRINGIZE(x) STRINGIZE2(x)

char path[] = STRINGIZE(TOP);

这对我有用。你几乎已经成功了,所以希望这会有所帮助。

[编辑] 我现在可以看到问题 - 在 C:\top 中 - 它将“反斜杠 t”作为控制代码 -“\t”。这个 appoarch 正在变得有点像噩梦一样 - 因为您确实需要使用两个斜杠创建文件名或使用正斜杠。我确实觉得在全面回顾发生的事情之前回答我在这里混淆了问题。 我尝试了几种方法 - 但无法更改传入的定义 - 我只能建议使用正则表达式库或手动扫描字符串 - 用正确的“\”字母替换控制字符。

我已经敲了一个示例,仅在您的示例中使用 '\t' 来显示这一点 - 这不是很好的代码,它是为了解释正在做什么而编写的,希望它提供了一个视觉示例并且它确实如此(在一个不是好方法)解决你在 'C:\top' 中遇到的一个问题 .. 正如我所说的 - 如果使用这种咳嗽方法,你将需要处理所有控制代码。 :-)

char stringName[256];
char* pRefStr = STRING(TOP);
char* pDestStr = stringName;
int nStrLen = strlen( pRefStr );

for( int nIndex = 0; nIndex < nStrLen; nIndex++ )
{
    if ( *pRefStr == '\t' )
    {
        *pDestStr++ = '\\';
        *pDestStr++ = 't';
        pRefStr++;
    }
    else
    {
        *pDestStr++ = *pRefStr++;
    }
}
*pDestStr = '\0';

再一次 - 很抱歉有任何混淆 - 我把我的答案留在这里作为你的参考 - 希望有人会想出一种处理定义字符串(使用控制字符)的方法。

谢谢,尼尔

【讨论】:

  • 谢谢,但同样,我无法更改传入的内容。我想知道是否可以使用连接字符串运算符## 以某种方式构造字符串文字。
  • 嗯,因为它是一个宏 - 您可以尝试在 TOP 之前和之后添加引号。我无法尝试,因为我远离我的开发机器 - 所以它应该可以工作。一种方法是使用 format(..) - 如果我靠近我的机器,我会尝试一下。
  • 我已经编辑了我的答案 shol74,这不是好消息,因为我无法弄清楚在获得 TOP 定义时如何不获得 '\t' 字符。正如我所提到的 - 希望有人能够解决这个问题。我真的会尝试更改传入的定义。
  • 谢谢尼尔。不过,我的实际问题要稍微糟糕一些。 C:\top 在我的示例中实际上被缩短了。 \t 至少会编译。我的实际路径中有一个 \o ,但不会。所以不幸的是,这也不仅仅是一个字符串替换的问题。不过感谢您的宝贵时间。
【解决方案2】:

您正在寻找的相当奇怪的语法在变量扩展中执行动态子字符串替换,以及一些引号转义以使定义成为字符串。找到了子串替换信息here

set DIR=C:\WINDOWS 获取环境变量集,然后我们有一个测试程序:

#include <stdio.h>

#define STR(x)    #x
#define STRING(x) STR(x)      

int main( int argc, char* argv[] )
{
    printf( "DIR: %s\n", STRING(DIR) );
    return 0;
}

由于不能在 shell 中引用,所以可以在此处进行字符串化,但仍必须进行变量子字符串替换。

通过 cmd.exe 传入环境变量:

gcc -Wall -DDIR=%DIR:\=\\% main.c

请参阅上面的链接以获取更多信息,或 google 子字符串替换。我找不到任何有关该功能的 Microsoft 信息的链接(真是令人惊讶!)

【讨论】:

  • 再一次,由于自定义构建环境已经在使用引号字符,我无法在传递字符串之前更改它。因此,在此处添加任何引号字符只会在此处尽早终止我的字符串。我需要使用 .cpp 文件中的 #define top c:\top。
  • @shol74 我已编辑以从编译命令行中删除引号。似乎是一个奇怪的要求。正如 Neil 所说,您可以使用宏扩展对其进行字符串化。
  • @shol74 我认为您要求的是不可能的,因为没有可用于预处理器的合理编码,而预处理器是您拥有的唯一工具。
  • 是的,我只有预处理器。这就是为什么我希望可能有一些聪明的方法将 R"(c:\top)" 连接到它并使其成为字符串文字。