Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C preprocessor: building a path string

Given a macro that has been defined previously:

#define FILENAME somefile.h

I want to concatenate this with another macro-string that defines the (relative) path of this file. My current approach is to do this like so:

#define DIRECTORY ../somedir/

#define STRINGIFY_(x) #x
#define FILE2_(dir, file) STRINGIFY_(dir ## file)
#define FILE_(dir, file) FILE2_(dir, file)

#include FILE_(DIRECTORY, FILENAME)

This however results in an error (GCC4.9):

error: pasting "/" and "file" does not give a valid preprocessing token

Removing the final forward slash from the DIRECTORY definition removes this error, but obviously does not yield the desired result. Similar errors appear when I try to smuggle the / in otherwise. For example:

#define FILE2_(dir, file) STRINGIFY_(dir ## / ## file)

does not work for the same reason.

I would like to know what is going wrong here and, obviously, how to circumvent this.

EDIT: Changed double underscores to singles on Columbo's advice. Apparently, identifiers containing double underscores are reserved to the implementation, regardless of where they appear (I was under the impression that this only held true for double underscores at the beginning of an ID).

like image 215
JorenHeit Avatar asked Sep 29 '22 07:09

JorenHeit


1 Answers

[cpp.include]/4:

A preprocessing directive of the form

        # include pp-tokens new-line

(that does not match one of the two previous forms) is permitted. The preprocessing tokens after include in the directive are processed just as in normal text (i.e., each identifier currently defined as a macro name is replaced by its replacement list of preprocessing tokens). If the directive resulting after all replacements does not match one of the two previous forms, the behavior is undefined.152


152Note that adjacent string literals are not concatenated into a single string literal (see the translation phases in 2.2); thus, an expansion that results in two string literals is an invalid directive.

So though #include MACRO is valid, MACRO must directly expand to an otherwise valid argument to #include. The concatenation of string literals happens two translation phases after preprocessing.

Also, in the definition of the ## operator, [cpp.concat]/3:

For both object-like and function-like macro invocations, before the replacement list is reexamined for more macro names to replace, each instance of a ## preprocessing token in the replacement list (not from an argument) is deleted and the preceding preprocessing token is concatenated with the following preprocessing token. [..] If the result is not a valid preprocessing token, the behavior is undefined.

Hence the result of A##B must be one valid preprocessing token. / is an own preprocessing token, and so are the names of the directories and files.
You can't concatenate "abc and /xyz", since abc/ is not a valid preprocessing token - "abc is not one preprocessing token, but two, though "abc" is one.
On the other hand, if you concatenate <abc/ and xyz>, then / and xyz are concatenated, examined, and we have a problem again.

Thus it appears to be impossible to concat the paths using ##. Your approach looks quite impossible to me, too.


With GCC, this is fine though:
#define PATH <foo/bar/
#define FILE boo>

#define ARG PATH FILE
#include ARG

It works because GCCs preprocessor removes the white space (for some reason). Does not work on VC++ or Clang and isn't covered by standard anyway, so definitely not recommended.

like image 72
Columbo Avatar answered Dec 07 '22 06:12

Columbo