Skip to content
Advertisement

Stringizing / stringify name mangling

I load a path name with cmake

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

and want to use as a string in my C++ program to load some data. For this the stringification operator # is really handy – I use the macro provided in this answer which is the same as here. Now when I have “linux” or “unix” in my path, this goes horribly wrong (at least with gcc), as these names are simply replaced by “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

Can anyone give a hint why is this happening and how I can prevent it? There is a similar question here, but there is no suitable answer to use it with cmake.

Advertisement

Answer

why is this happening

Macros unix and linux are legacy defined to 1 on UNIX platforms.

Part of the program /path/x86-unix/linux/pat consists of tokens unix and linux, so as part of macro expansion in xstr these macros are substituted for 1.

how I can prevent it?

#undef linux and unix macros. Or disable gnu extensions, for example, use c11 standard. With GCC compiler on Linux:

$ 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

how I can prevent it?

I load a path name with cmake and want to use as a string in my C++ program

Adapting example from Example section from CMake documentation of configure_file, create a file named my_path.h.in with content:

#cmakedefine MY_PATH "@MY_PATH@"

Add the following to your cmake configuration:

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
)

And use #include <my_path.h> in your program to get MY_PATH definitions.

I see no reason to use a macro – maybe it would be better to do static const char MY_PATH[] = "@MY_PATH@"; instead.


I load a path name with cmake

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

Do not use add_definitions. Prefer target_compile_definitions instead. See CMake add_definitions documentations.

As a crude workaround, you can add quotes, assuming the shell and compiler will properly parse them:

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

(Note that quotes in the above are preserved literally and passed to the compiler (or build system). CMake quoting does not work like shell quoting. In CMake language, to quote a word, quotes " have to be exactly the first and last characters of the word. If one of them is in the middle, they are interpreted literally)

However, I think using configure_file would be preferred, because of ESCAPE_QUOTES.

7 People found this is helpful
Advertisement