To make a long story short, I'm working on some code that needs different header files based on various configuration options. I could do something like this:
#if OPTION == A
#include "relative/path/to/file/a.hpp"
#elseif OPTION == B
#include "relative/path/to/file/b.hpp"
#elseif OPTION = ...
...
#endif
This would work, but it seems like a really ugly solution for what could potentially be more than a handful files. I could also simply include all the various header files, but that, too, seems like a bit of an ugly solution, and it could pose problems in the future if files (for some horrible reason) start redefining the same objects. The idea I had was that something like the following would be nice, particularly in the context of how the rest of the code is written:
#define QUOTE(str) #str
#define STRINGIFY(A,B) QUOTE(A##B)
...
#include STRINGIFY(relative/path/to/option/,OPTION)
The problem with this seems to be two-fold:
OPTION
definition doesn't expand properly inside STRINGIFY
. STRINGIFY
fails altogether, with g++ giving me an error along the lines of the following:error: pasting "/" and "OPTION" does not give a valid preprocessing token
I can't seem to find any real information on why /
is a bad character for the C++ preprocessor, just a few articles saying that you should just put "/"
in and rely on automatic C++ string concatenation (which doesn't work in an #include
statement). I'm willing to consider design alternatives if I'm just trying to do something really dumb, but I'd also like to figure out why this isn't working.
EDIT: I should make the clarification that I'm working on a codebase originally designed by a group of scientists. Reasonable coding conventions and typical expectations for how a code gets used go completely out the window. This code will probably be modified at least as many times as it gets used, often by people who have spent their entire careers writing Fortran 77 and think object-oriented programming is some new-fangled invention that just makes your code harder to understand.
When writing macros it is essential to look at the output after preprocessing. With gcc
thats the -E
option.
To include different files I would rely on your build tools rather than macro voodoo. For example when the code is
include option
Then gcc -E -option=\"foo.h\"
yields
include "foo.h"
Note that I ommitted the #
here, otherwise preprocessing would fail due to not finding "foo.h"
.
I wanted to do something similar, searched around and found this post, and after trying few things I think I can help you.
So, your approach:
#define QUOTE(str) #str
#define STRINGIFY(A,B) QUOTE(A##B)
...
#include STRINGIFY(relative/path/to/option/,OPTION)
As I understand from https://gcc.gnu.org/onlinedocs/cpp/Concatenation.html doesn't work because when using ##
to paste tokens, the result must be a valid token, where something like path/to/file.hpp
is not a valid preprocessing token.
Now, I think that you only need to have a macro which holds the path plus the token to be replaced. Well, you don't really need to add the relative path if you are using the -I
flag. Your macro could look like #define myMacro relative/path/to/file/OPTION.hpp
, then you just need to define OPTION
and pass it to one macro so OPTION
gets replaced with its value and then another macro to stringize. These two levels of macros are described herehttps://gcc.gnu.org/onlinedocs/cpp/Stringizing.html#Stringizing
The following example will ilustrate both cases (with/without -I
flag).
Consider this directory structure:
.
|-- a.c
|-- headers
| |-- A.hpp
| `-- B.hpp
`-- makefile
Where ac:
#define header OPTION.hpp
#define xstr(x) #x
#define str(x) xstr(x)
#include str(header)
makefile:
PATH_OPTION ?= headers/A
OPTION ?= A
.PHONY: all
all: a.c
cpp -D OPTION=${PATH_OPTION} $<
cpp -I./headers -D OPTION=${OPTION} $<
A.hpp
#warning Including A.hpp
1
B.hpp
#warning Including B.hpp
2
With this, you can call make
which by default will include headers/A.hpp
and you should be able to see both approaches (with/without -I
flag) working and printing to stdout the corresponding warning.
To include headers/B.hpp
you would do make OPTION=B
or make PATH_OPTION=headers/B
depending on the approach you take.
I also tried with g++
instead of cpp
and it works as well.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.