What’s the difference between …
#define CPPX_CSI_DIR( maindir ) \ CPPX_CONCAT3( maindir, /, CPPX_CSI_SUBDIR )
… and …
#define CPPX_CSI_DIR( maindir ) \ CPPX_CONCAT3_( maindir,/, CPPX_CSI_SUBDIR )
…? Well, the former works with MSVC (Microsoft Visual C++), while the latter works with both MSVC and g++, for the purpose of this macro. The macro is part of a scheme for automatically choosing a compiler specific header, as I described in an earlier posting, and g++ treats spaces, such as the space before the slash, as Very Significant when the macro expansion ends up as the path specification of an
Hubris… When I made that earlier posting I’d just streamlined the compiler specific include scheme to be more clear and reusable, but I’d only tested that new version with MSVC! Of course g++ would have none of that. Yer givin’ me a version that’s only been tested with MSVC? Ye got to be kidding! I’ll show you who’s G around here! I’ll stomp and spit on all them significant spaces!
To make a long story short (applause!), here’s the updated g++ and MSVC compatible version of the CSI scheme:
#include <progrock/cppx/macro_util.h> // CPP_CONCAT3_ #if defined( _MSC_VER ) # define CPPX_CSI_SUBDIR msvc #elif defined( __GNUC__ ) # define CPPX_CSI_SUBDIR gnuc #else # define CPPX_CSI_SUBDIR _generic #endif // NOTE: The "missing" spaces before the slashes are important for the g++ compiler. #define CPPX_CSI_DIR( maindir ) \ CPPX_CONCAT3_( maindir,/, CPPX_CSI_SUBDIR ) #define CPPX_CSI_PATH( maindir, path ) \ CPPX_CONCAT3_( CPPX_CSI_DIR( maindir ),/, path ) #define CPPX_CSI_BRACKET_PATH( maindir, path ) \ CPPX_CONCAT3_( <, CPPX_CSI_PATH( maindir, path ), > ) // To avoid macro replacement a component of 'path' should not be all uppercase // and should not be any of 'assert', 'errno', 'offsetof', 'setjmp', 'va_arg', // 'va_end' or 'va_start', which are macros -- see C++98 §18.104.22.168/5. // NOTE: The "missing" space before "progrock" is important for the g++ compiler. // When invoking this macro do not have spaces around the macro argument! #define CPPX_COMPILER_SPECIFIC_INCLUDE(path) \ CPPX_CSI_BRACKET_PATH(progrock/cppx/compiler_specific, path )
Of course, for this to be useful to you you’ll also need the definition of the
CPPX_CONCAT3_ macro. Sorry, I forgot that the first time around. But here it is:
// Concatenate with expansion of arguments: #define CPPX_CONCAT_RAW( a, b ) a ## b #define CPPX_CONCAT( a, b ) CPPX_CONCAT_RAW( a, b ) #define CPPX_CONCAT3_RAW( a, b, c ) a ## b ## c #define CPPX_CONCAT3( a, b, c ) CPPX_CONCAT3_RAW( a, b, c ) // NOTE: spaces may be significant (e.g. for the g++ compiler). // This is just in support of <progrock/cppx/compiler_specific/include.h> with g++. #define CPPX_TERM(x)x #define CPPX_CONCAT_( a, b )CPPX_TERM(a)CPPX_TERM(b) #define CPPX_CONCAT3_( a, b, c )CPPX_TERM(a)CPPX_TERM(b)CPPX_TERM(c)
I don’t know what the standard mandates, whether g++ is within its rights here or not. But while I fixed this I ran into a g++ bug. Namely that in some cases, such as within a
struct or within a function template, it chokes on a
typedef that defines the same type (in the same way) as earlier, contrary to C++98 §7.1.3/2:
In a given scope, a
typedefspecifier can be used to redefine the name of any type declared in that scope to refer to the type to which it already refers.
That g++ does not honor that paragraph meant that I had to fix up the
CPPX_STATIC_ASSERT macro that I described in an earlier posting, solely to work around the g++ compiler bug:
// The __LINE__ is not formally necessary (see C++98 §7.1.3/2), but caters to g++ bug. #define CPPX_STATIC_ASSERT( e ) \ typedef char CPPX_CONCAT( CppxStaticAssertShouldBeTrue_, __LINE__ )[(e)? 1 : -1]
Ah, well, more hubris… I was sure that the original worked OK with both MSVC and g++ since it passed all unit testing and was standard-conforming. But the unit test didn’t test having two
CPPX_STATIC_ASSERT invocations in the same scope, with g++! In my defense, it’s practically impossible to test for all possible compiler bugs. But still, I think I should have caught this one!