Reputation: 1915
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;
}
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
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
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