Clang preprocessor concatenates an extra space vs. gcc - standard C99 I maintain a C library project both on gcc and clang.

I am looking for advice on how to make the following macros more portable, terse, readable or standard abiding.

When I compile

#define STRINGIFY(arg) STRINGIFY_(arg)
#define STRINGIFY_(arg) #arg

#define VERSION 1.01
#define PROJECT myProject
#define SUB_PROJECT mySub
#define DASH -

#include STRINGIFY(PROJECT DASH SUB_PROJECT DASH VERSION.c)


I get

clang 6.0.0 standard C99

myProject - mySub - 1.01.c


gcc 7.4.0 standard C99

myProject-mySub-1.01.c


I came up with the following solution

#define STRINGIFY(arg) STRINGIFY_(arg)
#define STRINGIFY_(arg) #arg
#define ID(arg) arg

#define VERSION 1.01
#define PROJECT myProject
#define LIB myLib
#define DASH -
#define DOTC .c

#define FILEVERSION(a, b, c, d, e) STRINGIFY(ID(a)ID(b)ID(c)ID(d)ID(e)ID(DOTC))

#include FILEVERSION(PROJECT, DASH, LIB, DASH, VERSION)
I think this is beyond the jurisdiction of the C standard.  See https://stackoverflow.com/questions/37796947/spaces-inserted-by-the-c-preprocessor.  All the standard says on the topic is a footnote:


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


https://port70.net/~nsz/c/c11/n1570.html#note170

But this doesn't really matter since you are not making multiply strings.



Investigating GCC and Clang...

GCC does different things depending on what you ask for:

STRINGIFY(PROJECT DASH SUB_PROJECT DASH VERSION.c)


... gets me "myProject - mySub - 1.01.c" with GCC 9.3 and -E, but ...

#include STRINGIFY(PROJECT DASH SUB_PROJECT DASH VERSION.c)


gets me myProject-mySub-1.01.c: No such file or directory.  So -E has spaces and #include doesn't.

Clang is at least consistent.  With Clang 10, #include has spaces: fatal error: 'myProject - mySub - 1.01.c' file not found And Clang 10 and -E also has spaces myProject - mySub - 1.01.c.

In my opinion, Clang's behavior makes the most sense.  Suppose you wanted a space -- how else could you do that?



#define ID(arg) arg


This is a common trick.  I think it's a good idea although it looks a little ugly when you have to use it in so many spots.



#define STRINGIFY(arg) STRINGIFY_(arg)
#define STRINGIFY_(arg) #arg


This is fine, but I think it'd be clearer to define STRINGIFY below the helper macro it uses.



Can you get away with something like:

#if VERSION == 1
#include "library-version-1.h"
#else
#include "library-version-other.h"
#endif