I am trying to get Bloomberg's BDE library to compile in Visual Studio 2015. Because they re-implement the standard libraries typically provided by the compiler, there are header files that have names that exactly match the standard library names, such as stddef.h
. They optionally allow you to turn off the overriding of the standard library, and to facilitate this, the files they re-implemented will optionally just include the original compiler supplied version such as stddef.h
. They do this include through macros such as the following:
# if defined(BSLS_COMPILERFEATURES_SUPPORT_INCLUDE_NEXT)
# include_next <stddef.h>
# else
# include BSL_NATIVE_C_LIB_HEADER(stddef.h)
# endif
Source
Where BSL_NATIVE_C_LIB_HEADER
expands to something like this:
#if defined(BSLS_PLATFORM_CMP_SUN) // Sun Compiler
# define BSL_NATIVE_C_LIB_HEADER(filename) <../include/filename>
#elif defined(BSLS_PLATFORM_CMP_CLANG) || defined(BSLS_PLATFORM_CMP_GNU)
// Clang and GCC use 'include_next'
#elif defined(BSLS_PLATFORM_CMP_HP) // HP Compiler
# define BSL_NATIVE_C_LIB_HEADER(filename) <../include_std/filename>
#else
// Most other compilers
# define BSL_NATIVE_C_LIB_HEADER(filename) <../include/filename>
#endif
Source
The issue is that Visual Studio 2015 introduces some refactoring that moves some of the C Standard Library header files to a path like this: C:\Program Files (x86)\Windows Kits\10\Include\10.0.10150.0\ucrt
. This obviously means that <../include/filename>
will no longer find the moved files. The issue is that all files have not moved. For example, iso646.h
is still in C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\include
and will be picked up by the include.
So here's my question in a nutshell: Is there a way I can continue to support the BSL_NATIVE_C_LIB_HEADER
macro being used, while behind the scenes figuring out whether the import should be from ../ucrt/
or ../include
, based on the file name? I know I could create two separate macros, but I'd rather keep the interface the same if possible.
Ideally, the BSL_NATIVE_C_LIB_HEADER()-like macros would continue to work, while behind the scenes there is a mechanism to override some them based on filename to point at a different directory. At first it seems impossible because the argument to the macros is a filename and may contain "." or "/". But with enough macro-foolery I think it can still be done without changing the build scripts or the filesystem.
BTW, I'm not sure what this says about the C preprocessor, but here goes...
The idea is to construct a series of macros that would be inserted into bsl_stdhdrs_incpaths.h in a MSVC 2015 specific block. There are several of these macros that do basically the same thing, so I have factored the solution so that each user macro like BSL_NATIVE_C_LIB_HEADER is implemented in terms of a common macro FINDER. The implementation macros have short names for readability, they should get some sort of prefix.
Caveat: these examples have only been tested on GCC 4.8.4 and unstable, clang-3.5, and MSVC 2015.
Simple Idea
The first clue is that even if the argument to BSL_NATIVE_C_LIB_HEADER(stddef.h)
contains odd characters, it is not a single token. It is the list {'stddef', '.', 'h'}.
So we could #define stddef to ../include/stddef and it would work because the '.' and 'h' get appended after expansion! But better, we can use ## to paste the first token into macro with a unique name, say SELECTOR_stddef
. This can then be #defined with a unique path based on each filename:
#define ANGLES(f) <f>
#define BSL_NATIVE_C_LIB_HEADER(file) ANGLES(SELECTOR_##file)
/* each can now have a different prefix */
#define SELECTOR_stddef ../ucrt/stddef
#define SELECTOR_stdarg ../include/stdarg
/* later on, down in the user code... */
#include BSL_NATIVE_C_LIB_HEADER(stddef.h) /* #include <../ucrt/stddef.h> */
#include BSL_NATIVE_C_LIB_HEADER(stdarg.h) /* #include <../include/stddef.h> */
So this is working great! The only problem is that every single file that is called must have a selector. If not, it will try to include some stupid filename that looks like #include <SELECTOR_stdio.h>
and it won't work that well. Some maintenance programmer will probably come around and "fix" it by adding a bunch of symlinks to SELECTOR_stdio.h in the /usr/include directory and it will just end badly for everyone.
What would be really nice is if there could be a default for most files. Most appear to have been shuffled into .../ucrt and only compiler specific ones are left in .../include. So it would be nice to just override the ones we want. But even in this first form, it may work just fine. It has the advantage of simplicity.
A bunch of SO questions that helped with this:
A More Complete Solution
/* presumably within an #if MSVC 2015 conditional in bsl_stdhdrs_incpaths.h */
#define DELIMITER(a) a
/* same as DELIMITER, but named to distinguish the MSVC __VA_ARGS__ bug */
/* workaround is fine to leave in place for standard compilers */
#define MSVCFIXER(a) a
/* add the angle brackets and re-attach the "rest" tokens */
#define FORMATER(x1, x2, pre, rest, ...) <DELIMITER(pre)rest>
/* if __VA_ARGS__ only has one argument, shift so that pre is the default
* otherwise if __VA_ARGS__ has two, pre is the override */
#define SHIFTER(pre, rest, def, ...) MSVCFIXER(FORMATER(__VA_ARGS__, pre, rest, def))
/* expand the commas */
#define EXPANDER(...) MSVCFIXER(SHIFTER(__VA_ARGS__))
/* main implementation - pass both the selector override and default */
#define FINDER(file, defloc) \
EXPANDER(HEAD_LOC_OVERRIDE_##file, DELIMITER(defloc)file,,)
/* now implement the top level macros */
#define BSL_NATIVE_C_LIB_HEADER(file) FINDER(file, HEAD_LOC_DEFAULT_PREFIX)
#define BSL_NATIVE_SYS_TIME_HEADER(file) FINDER(file, HEAD_LOC_DEFAULT_PREFIX)
#define BSL_NATIVE_CISO646_HEADER(file) FINDER(file, /tmp/)
/* maybe define a common default prefix, or hard code it like iso646
* since most files appear to be in ucrt, make this the default
(file.h) will become <../ucrt/file.h> */
#define HEAD_LOC_DEFAULT_PREFIX ../ucrt/
/* override any other files NOTE: the commas
* (stdarg.h) will become <../include/stdarg.h>
* (stdint.h) will become <../include/stdint.h> */
#define HEAD_LOC_OVERRIDE_stdarg ../include/stdarg,
#define HEAD_LOC_OVERRIDE_stdint ../include/stdint,
/* and you can even override the name part too, or remove or add the .h
* (where.h) will become <../somewhere/when> (note: use two commas)
* (sys/*.h) will become <../include/sys/*.h>
* (cstdio) will become <windows.h> */
#define HEAD_LOC_OVERRIDE_where ../somewhere/when,,
#define HEAD_LOC_OVERRIDE_sys ../include/sys,
#define HEAD_LOC_OVERRIDE_cstdio windows.h,
/* later on, down in the user code... */
#include BSL_NATIVE_C_LIB_HEADER(stdarg.h) /* <../include/stdarg.h */
#include BSL_NATIVE_C_LIB_HEADER(stdio.h) /* <../ucrt/stdio.h */
#include BSL_NATIVE_C_LIB_HEADER(cstdio) /* <windows.h> */
#include BSL_NATIVE_C_LIB_HEADER(what.h) /* <../ucrt/what.h> */
#include BSL_NATIVE_C_LIB_HEADER(where.h) /* <../somewhere/when> */
#include BSL_NATIVE_CISO646_HEADER(iso646.h) /* </tmp/iso646.h> */
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With