I have a very large codebase (read: thousands of modules) that has code shared across numerous projects that all run on different operating systems with different C++ compil
Generally this is done by using a script that tries running the preprocessor on an attempt at including the file. Depending on if the preprocessor returns an error, the script updates a generated .h file with an appropriate #define (or #undef). In bash, the script might look vaguely like this:
cat > .test.h <<'EOM'
#include
EOM
if gcc -E .test.h
then
echo '#define HAVE_ASDF_H 1' >> config.h
else
echo '#ifdef HAVE_ASDF_H' >> config.h
echo '# undef HAVE_ASDF_H' >> config.h
echo '#endif' >> config.h
fi
A pretty thorough framework for portably working with portability checks like this (as well as thousands others) is autoconf.