【问题标题】:Stringizing / stringify name mangling字符串化/字符串化名称修饰
【发布时间】:2021-10-04 00:46:32
【问题描述】:

我用 cmake 加载一个路径名

add_definitions(-DMY_PATH =${CMAKE_INSTALL_FULL_DATADIR}/path)

并想在我的 C++ 程序中用作字符串来加载一些数据。为此,字符串化运算符# 非常方便——我使用this answer 中提供的宏,它与here 相同。现在,当我的路径中有“linux”或“unix”时,这是非常错误的(至少对于 gcc),因为这些名称只是被“1”替换:

#include <stdio.h>

#define xstr(a) str(a)
#define str(a) #a

#ifndef MY_PATH
    #define MY_PATH /path/x86-unix/linux/path
#endif

int main()
{   
    char my_path_str[] = xstr(MY_PATH);
    printf("my path is %s", my_path_str);

    return 0;
}

Try it out

谁能提示为什么会发生这种情况以及如何防止它? 有一个类似的问题here,但是没有合适的答案可以和cmake一起使用。

【问题讨论】:

  • 当您在路径中使用 macro 时会出现“严重错误”。始终使用字符串作为路径,即使您将它们定义为宏。
  • @Someprogrammerdude 如果您使用 Windows 路径,这是一个问题,因为反斜杠被视为转义字符。 # 运算符考虑了这一点
  • I load a path name with cmake and want to use as a string 如何你“用cmake加载路径”?请显示 cmake 代码。你想使用configure_file吗?您是否阅读了cmake.org/cmake/help/latest/command/configure_file.html 并检查了带有字符串示例的示例部分?你在问 XY 问题吗?
  • 但它带来了更多更难解决的问题(否则你不会在这里问,对吧?;))。甚至大多数初学者都知道需要转义反斜杠。此外,Windows 无论如何都支持正常路径的正斜杠。
  • @Someprogrammerdude 是的,确实如此。但是,如果用户提供带有反斜杠的路径,我就有问题了。另外,在 windows 上使用时,cmake 还可以抓取带有反斜杠的路径。

标签: c++ c linux gcc cmake


【解决方案1】:

为什么会这样

unixlinux 在 UNIX 平台上被定义为 1。

部分程序/path/x86-unix/linux/pat 由标记unixlinux 组成,因此作为xstr 中宏扩展的一部分,这些宏被替换为1

如何预防?

#undef linuxunix 宏。或者禁用 gnu 扩展,例如使用 c11 标准。在 Linux 上使用 GCC 编译器:

$ gcc  -E -dM - </dev/null | grep linux
#define __linux 1
#define __gnu_linux__ 1
#define linux 1  // here it is
#define __linux__ 1
$ gcc -std=c11 -E -dM - </dev/null | grep linux
#define __linux 1
#define __gnu_linux__ 1
#define __linux__ 1
// now there's no #define linux

如何预防?

我用 cmake 加载一个路径名并想在我的 C++ 程序中用作字符串

CMake documentation of configure_file 改编Example 部分的示例,创建一个名为my_path.h.in 的文件,内容为:

#cmakedefine MY_PATH "@MY_PATH@"

将以下内容添加到您的 cmake 配置中:

set(MY_PATH "/path/x86-unix/linux/path") 
# Generate the file
configure_file(my_path.h.in
    ${CMAKE_CURRENT_BINARY_DIR}/generated/my_path.h
    ESCAPE_QUOTES
    @ONLY
)
# add the path to your target
target_include_directories(your_target
    PUBLIC_or_PRIVATE
    ${CMAKE_CURRENT_BINARY_DIR}/generated
)

并在您的程序中使用#include &lt;my_path.h&gt; 来获取MY_PATH 定义。

我认为没有理由使用宏 - 也许改用 static const char MY_PATH[] = "@MY_PATH@"; 会更好。


我用 cmake 加载一个路径名

 add_definitions(-DMY_PATH =${CMAKE_INSTALL_FULL_DATADIR}/path)

不要使用add_definitions。更喜欢target_compile_definitions。见CMake add_definitions documentations

作为一种粗略的解决方法,您可以添加引号,假设 shell 和编译器会正确解析它们:

target_compile_definitions(your_target PRIVATE 
    MY_PATH="${CMAKE_INSTALL_FULL_DATADIR}/path"
)

(请注意,上面的引号按字面意思保留并传递给编译器(或构建系统)。CMake 引用确实 not 像 shell 引用一样工作。在 CMake 语言中,引用一个词,引号" 必须正好是单词的第一个和最后一个个字符。如果其中一个在中间,则按字面意思解释)

但是,我认为使用configure_file 会更好,因为ESCAPE_QUOTES

【讨论】:

    【解决方案2】:

    在您的情况下,在您定义的路径周围使用引号就足够了:

    #define MY_PATH "/path/x86-unix/linux/path"
    

    这将给出 expected 输出(尽管这两边都包含引号)。将linuxunix 替换为1 的原因是它们已经被定义为宏。如果您想要不带引号的路径,您可以事先取消定义unixlinux

    #undef unix
    #undef linux
    #define MY_PATH /path/x86-unix/linux/path
    

    但是当其他库检查unixlinux 宏时,这可能会导致一些意想不到的问题。您可以使用#pragma push_macro 来防止宏替换(如this answer 中所述),但正如您所料,它不是很便携。

    至于 Windows 路径,它似乎可以在需要的地方转义反斜杠:

    #define MY_PATH "C:\\Program Files\\Path\\Folder"
    

    这给出了预期的输出。

    【讨论】:

    • 是的,但这不适用于 windows 路径,因为反斜杠被视为转义字符
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-05-28
    • 1970-01-01
    • 2011-01-25
    • 2021-01-31
    • 2016-11-25
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多