Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

#include file derived from macro __FILE__?

Observe the following program:

#include __FILE__
main(){}

The preprocessor gets stuck in infinite recursion including a copy of itself inside itself and complaining about main() already being defined.


If I can use macros to include files, can I derive a file name based on __FILE__ and include it?


For example, I'd like to include "foo.h" whilst inside "foo.cpp", but derive it from __FILE__.

  • ## will concatenate macros.
  • It is also possible to Stringify macros.

Can it be done with the preprocessor?

like image 218
Trevor Hickey Avatar asked Nov 21 '12 01:11

Trevor Hickey


1 Answers

The C standard specifies three forms of #include:

#include <file>
#include "file"
#include ANYTHING ELSE

In the former two cases, no macro expansion takes place, so there's no way to vary the behavior. In the third case, C99 says (§6.10.2p4):

The preprocessing tokens after #include in the directive are [macro-expanded]. The directive resulting after all replacements shall match one of the two previous forms [footnote: Note that adjacent string literals are not concatenated into a single string literal]. The method by which a sequence of preprocessing tokens between a < and a > preprocessing token pair or a pair of " characters is combined into a single header name preprocessing token is implementation-defined.

Slightly different, but effectively equivalent, wording appears in C++98 §16.2p4.

Any sentence with "shall" in it imposes a hard requirement: in this case, the program is ill-formed if ANYTHING ELSE expands to anything but a sequence of tokens beginning with < and ending with >, or beginning and ending with ". The exact interpretation of that sequence of tokens is implementation defined, but note that the footnote specifically forbids string-literal concatenation.

So, as the expansion of __FILE__ is a string constant, the only ways to use it in an #include are

#include __FILE__

which, as you discovered, leads to infinite recursion, and

#define LT <
#define GT >
#include LT __FILE__ etc GT

which has amusing, but useless, effects on all the compilers I can conveniently test. Assuming that the above is in a file named test.c:

  • GCC attempts to open a file named "test.c" etc, with the quotation marks and space included verbatim.
  • clang is even more literal, and looks for that same filename but with leading and trailing spaces.
  • MSVC macro-expands only the LT (it is my considered opinion that this is a conformance violation), complains that there is no matching >, and then attempts to open a file named __FILE__ etc GT.

(GCC's behavior is documented here; you are on your own for anything else.)

tl;dr: There is no way to do what you want from inside the preprocessor. I recommend working out the name of the file-to-be-included from your build system and notifying the compiler of it with a -D switch (on a Unixy system you will need double quotation, -DINCLUDEME='"includeme.h"'; I don't speak CMD)

like image 189
zwol Avatar answered Nov 11 '22 09:11

zwol