[Home]BoostConfig

BOOST WIKI | RecentChanges | Preferences | Page List | Links List

Consistent identification of compilers, platforms and std libraries

Previous discussions

This is a distillation of previous discussions concerning identification of compilers, platforms, and libraries in the context of the Boost.Config system.

Proposal

The current idea is to define a set of macros to identify compilers and consistently represent their version. This includes:

"Null values" could be set, for all macros, in boost/config/select_compiler.hpp; then, for each compiler the corresponding identification macro would be #undef and re-#defined in the corresponding boost/compiler/(cc).hpp; however in the context of the Boost.Config infrastructure using a "prefix" header (to be introduced) or boost/config/suffix.hpp is a better solution. This point is further clarified later.

Current Work

There is ongoing work to implement the macros herein. They are checked into the main CVS repository, under the branch BOOST_VERSION_NUMBER. The following directories and files are branched:

Although there's been no discussion (AFAIK; Rene) about extending this mechanism to all of the three identification features of Boost.Config (compiler, platform and standard library) it seems appropriate to apply the same standard to all of them, as one would think that all the same issues apply.

Design choices

An important design choice concerns how to represent compiler versions by means of a single integer number suitable for use in preprocessing directives. Let's do some calculation. The "basic" signed type for preprocessing constant-expressions is long in C90 (and C++, as of 2006) and intmax_t in C99. The type long shall at least be able to represent the number +2 147 483 647. This means the most significant digit can only be 0, 1 or 2; and if we want all decimal digits to be able to vary between 0 and 9, the largest range we can consider is [0, 999 999 999]. Distributing evenly, this means 3 decimal digits for each version number part.

So we can:

  1. use an uneven distribution or
  2. use more bits (a larger type) or
  3. use 3/3/3 and have the particular compiler/platform/stdlib deal with setting the numbers within the 3-digit range.

It appears relatively safe to go for the first option and set it at 2/2/5. That covers CW and others, which are up to and past 10 for the major number. Some compilers use the build number in lieu of the patch one; five digits (which is already reached by VC++ 8) seems a reasonable limit even in this case.

NOTE: A 2/2/6 scheme would allow for bigger patch/build numbers at the cost, for instance, of limiting the major version number to 20 (or, with further constraints, to 21).

It might reassure the reader that this decision is actually encoded in one place in the code —the definition of BOOST_VERSION_NUMBER.

(1) BOOST_VERSION_NUMBER macro

  #define BOOST_VERSION_NUMBER(major,minor,patch) \
   ( ((major)*10000000) + ((minor)*100000) + (patch) )

This is currently defined in a separate file (version_number.hpp) in the branched boost/config/ directory. Benefits of using a single standard version-definition macro:

(2) BOOST_CXX_(compiler) macros

These are the macros for each supported compiler. Various naming ideas have been posted to the developer list, about compiler vs. vendor names etc.: the majority feels that compiler names are less likely to confuse people. And of course with that there's ample discussion as to which names to use for specific compilers. My (Rene) take on this is that the names should reflect as closely as possible the builtin macro names used by the vendors. That makes it more easily recognized by users who already know such macro names. With that in mind macros for the current set of compilers would be:

  // Comeau C++
  #ifndef BOOST_CXX_COMO
  #define BOOST_CXX_COMO 0
  #endif
  // Digital Mars C++
  #ifndef BOOST_CXX_DMC
  #define BOOST_CXX_DMC 0
  #endif
  // Intel C++
  #ifndef BOOST_CXX_INTELC
  #define BOOST_CXX_INTELC 0
  #endif
  // GNU C++
  #ifndef BOOST_CXX_GNUC
  #define BOOST_CXX_GNUC 0
  #endif
  // Kai C++
  #ifndef BOOST_CXX_KCC
  #define BOOST_CXX_KCC 0
  #endif
  // SGI MIPSpro C++
  #ifndef BOOST_CXX_SGI
  #define BOOST_CXX_SGI 0
  #endif
  // Dec Alpha Tru64 C++
  #ifndef BOOST_CXX_DEC
  #define BOOST_CXX_DEC 0
  #endif
  // Greenhills C++
  #ifndef BOOST_CXX_GHS
  #define BOOST_CXX_GHS 0
  #endif
  // Borland C++
  #ifndef BOOST_CXX_BORLANDC
  #define BOOST_CXX_BORLANDC 0
  #endif
  // Metrowerks C++
  #ifndef BOOST_CXX_CW
  #define BOOST_CXX_CW 0
  #endif
  // Sun C++
  #ifndef BOOST_CXX_SUNPRO
  #define BOOST_CXX_SUNPRO 0
  #endif
  // HP aCC C++
  #ifndef BOOST_CXX_ACC
  #define BOOST_CXX_ACC 0
  #endif
  // MPW C++
  #ifndef BOOST_CXX_MPW
  #define BOOST_CXX_MPW 0
  #endif
  // Visual Age (IBM) C++
  #ifndef BOOST_CXX_IBMCPP
  #define BOOST_CXX_IBMCPP 0
  #endif
  // Microsoft Visual C++
  #ifndef BOOST_CXX_MSVC
  #define BOOST_CXX_MSVC 0
  #endif
  // Common EDG Frontend
  #ifndef BOOST_CXX_EDG
  #define BOOST_CXX_EDG 0
  #endif

As pointed out by D. Abrahams the above has the benefit of removing any need to check for undefined macros when using the BOOST_WORKAROUND utility (need primarily rising from the will to avoid g++ diagnostics when using the -Wundef option). As an example, the definition for Comeau C++ would be:

  // Comeau C++
  #define BOOST_CXX_COMO BOOST_VERSION_NUMBER( \
      (__COMO_VERSION__ & 0xF000)>>12,         \
      (__COMO_VERSION__ & 0xF00)>>8,           \
       __COMO_VERSION__ & 0xFF)

And for others (if you know what the definition is for some compiler, or if there's a mistake below, please edit this list):

  // Digital Mars C++
  #define BOOST_CXX_DMC BOOST_VERSION_NUMBER((__DMC__ & 0xF00)>>8,__DMC__ & 0xFF,0)

  // Intel C++ (note: http://softwareforums.intel.com/ids/board/message?board.id=16&message.id=484)
  #if defined(__INTEL_COMPILER)
  #   define BOOST_CXX_INTELC_VER __INTEL_COMPILER
  #elif defined(__ICL)
  #   define BOOST_CXX_INTELC_VER __ICL
  #elif defined(__ICC)
  #   define BOOST_CXX_INTELC_VER __ICC
  #elif defined(__ECC)
  #   define BOOST_CXX_INTELC_VER __ECC
  #endif
  #define BOOST_CXX_INTELC BOOST_VERSION_NUMBER(  \
     BOOST_CXX_INTELC_VER/100,                    \
     (BOOST_CXX_INTELC_VER%100)/10,               \
     BOOST_CXX_INTELC_VER%10)

  // GNU C++
  #if defined(__GNUC_PATCHLEVEL__)
  # define BOOST_CXX_GNUC BOOST_VERSION_NUMBER(  \
     __GNUC__,__GNUC_MINOR__,__GNUC_PATCHLEVEL__)
  #else
  # define BOOST_CXX_GNUC BOOST_VERSION_NUMBER(  \
     __GNUC__,__GNUC_MINOR__,0)
  #endif

  // Kai C++
  #define BOOST_CXX_KCC BOOST_VERSION_NUMBER(  \
      (__KCC_VERSION & 0xF000)>>12,            \
      (__KCC_VERSION & 0xF00)>>8,              \
       __KCC_VERSION & 0xFF)

  // SGI MIPSpro C++
  #define BOOST_CXX_SGI BOOST_VERSION_NUMBER(  \
      _COMPILER_VERSION/100,                   \
     (_COMPILER_VERSION%100)/10,               \
      _COMPILER_VERSION%10)

  // Dec Alpha Tru64 C++
  #define BOOST_CXX_DEC BOOST_VERSION_NUMBER(  \
      __DECCXX_VER/10000000,                   \
      (__DECCXX_VER%10000000)/100000,          \
      __DECCXX_VER%10000)

  // Greenhills C++
  #define BOOST_CXX_GHS BOOST_VERSION_NUMBER(0,__ghs+1,0)

  // Borland C++
  #define BOOST_CXX_BORLANDC BOOST_VERSION_NUMBER(  \
      (__BORLANDC__ & 0xFF00)>>8,                   \
      (__BORLANDC__ & 0xF0)>>4,                     \
      __BORLANDC__ & 0xF)

  // Metrowerks C++
  #if __MWERKS__ == 0x3000
  #  define BOOST_CXX_CW BOOST_VERSION_NUMBER(8,0,0)
  #elif __MWERKS__ == 0x3001
  #  define BOOST_CXX_CW BOOST_VERSION_NUMBER(8,1,0)
  #elif __MWERKS__ == 0x3002
  #  define BOOST_CXX_CW BOOST_VERSION_NUMBER(8,2,0)
  #elif __MWERKS__ == 0x3003
  #  define BOOST_CXX_CW BOOST_VERSION_NUMBER(8,3,0)
  #elif __MWERKS__ == 0x3200
  #  define BOOST_CXX_CW BOOST_VERSION_NUMBER(9,0,0)
  #elif __MWERKS__ == 0x3201
  #  define BOOST_CXX_CW BOOST_VERSION_NUMBER(9,1,0)
  #elif __MWERKS__ == 0x3202
  #  define BOOST_CXX_CW BOOST_VERSION_NUMBER(9,2,0)
  #elif __MWERKS__ == 0x3204             // note the "skip": 04->9.3
  #  define BOOST_CXX_CW BOOST_VERSION_NUMBER(9,3,0)
  #elif __MWERKS__ == 0x3205
  #  define BOOST_CXX_CW BOOST_VERSION_NUMBER(9,4,0)
  #elif __MWERKS__ == 0x3206
  #  define BOOST_CXX_CW BOOST_VERSION_NUMBER(9,5,0)
  #else
  #  define BOOST_CXX_CW BOOST_VERSION_NUMBER((__MWERKS__&0xFF00)>>8,__MWERKS__&0xFF,0)
  #endif

  // Sun C++
  #define BOOST_CXX_SUNPRO BOOST_VERSION_NUMBER((__SUNPRO_CC & 0xFF00)>>8,(__SUNPRO_CC & 0xF0)>>4,__SUNPRO_CC & 0xF)

  // HP aCC C++
  #define BOOST_CXX_ACC BOOST_VERSION_NUMBER(  \
      __HP_aCC/10000,                          \
      (__HP_aCC%10000)/100,                    \
      __HP_aCC%100)

  // MPW C++
  #define BOOST_CXX_MPW BOOST_VERSION_NUMBER((MPW_CPLUS & 0xFF00)>>8,(MPW_CPLUS & 0xF0)>>4,MPW_CPLUS & 0xF)

  // Visual Age (IBM) C++
  #define BOOST_CXX_IBMCPP ???

  // Microsoft Visual C++
  #if !defined (_MSC_FULL_VER)
  #    define BOOST_CXX_MSVC_BUILD 0
  #else
    // how many digits does the build number have?
    //
  # if _MSC_FULL_VER / 10000 == _MSC_VER
  #    define BOOST_CXX_MSVC_BUILD (_MSC_FULL_VER % 10000)  // four digits
  # elif _MSC_FULL_VER / 100000 == _MSC_VER
  #    define BOOST_CXX_MSVC_BUILD (_MSC_FULL_VER % 100000) // five digits
  # else
  #   error "Cannot determine build number from _MSC_FULL_VER"
  # endif
  #endif
  #define BOOST_CXX_MSVC BOOST_VERSION_NUMBER( \
      _MSC_VER/100-6,                          \
      _MSC_VER%100,                            \
      BOOST_CXX_MSVC_BUILD)

  // Common EDG Frontend
  #define BOOST_CXX_EDG BOOST_VERSION_NUMBER(  \
      __EDG_VERSION__/100,                     \
      (__EDG_VERSION__%100)/10,                \
      __EDG_VERSION__%10)

Each compiler specific header should undef and redef the macro that corresponds to its compiler as early as possible. The goal is to minimize the use of compiler specific macros even in the compiler configuration headers.

The definitions of the default value for all non-present compilers, platforms, and stdlibs macros would go either into a suitably introduced prefix header or into the already existing boost/config/suffix.hpp. The basic reason for this arrangement, pointed out by John Maddock, is that the selection headers can be by-passed by users, whereas prefix/suffix headers cannot.

(3) BOOST_PLATFORM_(platform) macros

Like for compilers we would define a macro for each platform type with a default of 0 or other special value, in boost/config/select_platform_config.hpp:

  // Linux
  #ifndef BOOST_PLATFORM_LINUX
  #define BOOST_PLATFORM_LINUX 0
  #endif
  // BSD
  #ifndef BOOST_PLATFORM_BSD
  #define BOOST_PLATFORM_BSD 0
  #endif
  // Solaris
  #ifndef BOOST_PLATFORM_SUN
  #define BOOST_PLATFORM_SUN 0
  #endif
  // SGI Irix
  #ifndef BOOST_PLATFORM_SGI
  #define BOOST_PLATFORM_SGI 0
  #endif
  // HP Unix
  #ifndef BOOST_PLATFORM_HPUX
  #define BOOST_PLATFORM_HPUX 0
  #endif
  // CYGWIN
  #ifndef BOOST_PLATFORM_CYGWIN
  #define BOOST_PLATFORM_CYGWIN 0
  #endif
  // Windows-32
  #ifndef BOOST_PLATFORM_WIN32
  #define BOOST_PLATFORM_WIN32 0
  #endif
  // BeOS?
  #ifndef BOOST_PLATFORM_BEOS
  #define BOOST_PLATFORM_BEOS 0
  #endif
  // MacOS?
  #ifndef BOOST_PLATFORM_MACOS
  #define BOOST_PLATFORM_MACOS 0
  #endif
  // IBM AIX
  #ifndef BOOST_PLATFORM_AIX
  #define BOOST_PLATFORM_AIX 0
  #endif
  // AmigaOS?
  #ifndef BOOST_PLATFORM_AMIGAOS
  #define BOOST_PLATFORM_AMIGAOS 0
  #endif
  // Posix
  #ifndef BOOST_PLATFORM_POSIX
  #define BOOST_PLATFORM_POSIX 0
  #endif
  // MinGW? Runtime on Windows
  #ifndef BOOST_PLATFORM_MINGW
  #define BOOST_PLATFORM_MINGW 0
  #endif

The specific definitions would go into each of the platform headers boost/config/platform/*.hpp, and for posix into boost/config/posix_features.hpp. For the same reasons as the other defines the earlier they are defined the earlier they can be universally used. The definitions would follow the same format as for compilers. But since platforms are not usually identifiable as a version the version number should normally be defined as 1.0.0 to indicate the presence of the platform. And where available a specific version can be provided. This allows for both simple:

  #if BOOST_PLATFORM_LINUX

checks, as well as more version-specific checks:

  #if (BOOST_PLATFORM_WIN32 >= BOOST_VERSION_NUMBER(5,0,0))

Definitions:

  // MinGW? Runtime on Windows
  #include <_mingw.h>
  #define BOOST_PLATFORM_MINGW BOOST_VERSION_NUMBER(__MINGW32_MAJOR_VERSION,__MINGW32_MINOR_VERSION,0)

(4) BOOST_STDLIB_(stdlib) macros

Like above the standard C++ library used has it's own set of macros for identifying them. We would add appropriate macros for the various libraries to default to 0 in boost/config/select_stdlib_config.hpp:

  // STLport
  #ifndef BOOST_STDLIB_STLPORT
  #define BOOST_STDLIB_STLPORT 0
  #endif
  // Comeau STL
  #ifndef BOOST_STDLIB_LIBCOMO
  #define BOOST_STDLIB_LIBCOMO 0
  #endif
  // Rogue Wave
  #ifndef BOOST_STDLIB_RWSTD
  #define BOOST_STDLIB_RWSTD 0
  #endif
  // GNU libstdc++
  #ifndef BOOST_STDLIB_GLIBCXX
  #define BOOST_STDLIB_GLIBCXX 0
  #endif
  // SGI STL
  #ifndef BOOST_STDLIB_SGISTL
  #define BOOST_STDLIB_SGISTL 0
  #endif
  // Metrowerks Standard Library
  #ifndef BOOST_STDLIB_MSL
  #define BOOST_STDLIB_MSL 0
  #endif
  // IBM VACPP
  #ifndef BOOST_STDLIB_VACPP
  #define BOOST_STDLIB_VACPP 0
  #endif
  // Modena C++ Standard Library
  #ifndef BOOST_STDLIB_MODENA
  #define BOOST_STDLIB_MODENA 0
  #endif
  // Dinkumware
  #ifndef BOOST_STDLIB_DINKUMWARE
  #define BOOST_STDLIB_DINKUMWARE 0
  #endif

Definitions are (contributions are welcome and needed):

  // STLport
  #define BOOST_STDLIB_STLPORT BOOST_VERSION_NUMBER(1,0,0)

  // Comeau STL
  #define BOOST_STDLIB_LIBCOMO BOOST_VERSION_NUMBER(1,0,0)

  // Rogue Wave
  #define BOOST_STDLIB_RWSTD BOOST_VERSION_NUMBER(1,0,0)

  // GNU libstdc++
  #define BOOST_STDLIB_GLIBCXX BOOST_VERSION_NUMBER(1,0,0)

  // SGI STL
  #define BOOST_STDLIB_SGISTL BOOST_VERSION_NUMBER(1,0,0)

  // Metrowerks Standard Library
  #define BOOST_STDLIB_MSL BOOST_VERSION_NUMBER(1,0,0)

  // IBM VACPP
  #define BOOST_STDLIB_VACPP BOOST_VERSION_NUMBER(1,0,0)

  // Modena C++ Standard Library
  #define BOOST_STDLIB_MODENA BOOST_VERSION_NUMBER(1,0,0)

  // Dinkumware
  #define BOOST_STDLIB_DINKUMWARE BOOST_VERSION_NUMBER(_CPPLIB_VER/100,_CPPLIB_VER-_CPPLIB_VER/100*100,0)

Issues

Some compilers, like Comeau C++, try and emulate other compilers. For them it is possible that some workarounds are dependent on the particular version of the compiler they are emulating. Here's some of those issues, as they come up:

The only option I can think of is to go ahead and define the corresponding version macro and have the code deal with the multiple defs.

Some compiler configs already define a semi-standard version macro. For the short term we need to keep those until the code that depends on them changes.


BOOST WIKI | RecentChanges | Preferences | Page List | Links List
Edit text of this page | View other revisions
Last edited July 9, 2006 3:36 am (diff)
Search:
Disclaimer: This site not officially maintained by Boost Developers