yar
yar

Reputation: 1915

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.

Upvotes: 0

Views: 214

Answers (2)

KamilCuk
KamilCuk

Reputation: 141145

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.

Upvotes: 1

FakeMod
FakeMod

Reputation: 125

In your case, using quotes around the path that you define will suffice:

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

This will give the expected output (though this contains quotes on both the sides). The reason why linux and unix are substituted with 1, is because they are already defined as macros. If you want the path without any quotes, you could undef unix and linux beforehand:

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

But this might cause some unexpected problems when other libraries check for the unix or the linux macro. You can use #pragma push_macro to prevent macro substitution (as explained in this answer), but as you would expect, it is not very portable.

As for Windows paths, it seems to work with escaping the backslash where required:

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

This gives the expected output.

Upvotes: 2

Related Questions