Your question is actually backward, because the non-standard extensions supported by a compiler are specific to that compiler - often to the extent of being specific to a particular compiler version - as are the non-standard macros each compiler defines so they can be detected.
The usual technique is the reverse: specify some feature you want, associate it with some macro, and only write code which uses that feature if the associated macro is defined.
Let's say there is some funky feature that is supported - in exactly the same way by Visual C++ 11 and g++ version 3.2.1, but not with any other compilers (not even other versions of Visual C++ or g++).
// in some header that detects if the compiler supports all sorts of features
#if ((defined(__GNUG__) && __GNUC__ == 3 && __GNUC_MINOR__ == 2 && __GNUC_PATCHLEVEL__ == 1) || (defined(_MSC_VER) && _MSC_VER == 1700))
#define FUNKY_FEATURE
#endif
// and, in subsequent user code ....
#ifdef FUNKY_FEATURE
// code which uses that funky feature
#endif
There are plenty of freely available general purpose libraries which use this sort of technique (obviously with better naming of macros). One example that comes to mind is the ACE (Adaptive Communication Environment) framework which has a set of portability macros, documented here.
Using such macros is not a job for the faint-hearted if you are concerned about a large set of non-standard features, given that it is necessary to understand what versions of what compilers (or libraries) support each feature, and to update the macros every time a new compiler, a new library, or even a patch is released.
It is also necessary to avoid using reserved identifiers in naming those macros, and to ensure the macro names are unique. Identifiers starting with a double underscore are reserved.