boost/cstdint.hpp definition of INTMAX_C conflicts with MPFR

Hi, I'm a MPFR developer and the following problem with Boost has been reported in the MPFR mailing-list. As of Boost 1.42, the macros INTMAX_C and UINTMAX_C (in particular) are always defined, and on a system without <stdint.h> (Microsoft C++ compiler), the build of a program using Boost 1.42 and MPFR failed. The reason is that mpfr.h supports systems with and without intmax_t (so that it does not force <stdint.h> inclusion), thus it must have a way to determine when intmax_t is available, without requiring the user to do anything else than including <stdint.h> first. So, it has several heuristics, one of them being the test of the existence of INTMAX_C and UINTMAX_C. Indeed, in C, which does not have namespaces, it is rather intuitive that the existence of INTMAX_C and UINTMAX_C implies that the corresponding types intmax_t and uintmax_t are available. However, in C++ with Boost, intmax_t and uintmax_t are not necessarily available (in the global namespace). Even though one could specify a macro for MPFR that would allow the user to disable (u)intmax_t support explicitly, I think that Boost's behavior is rather awkward, and may break other software too (in particular libraries that would do the same kind of things as Boost). Indeed (correct me if I'm wrong, I don't know very much about C++), while types have namespaces, macros don't, but there are conventions under which using a prefix such as BOOST_ can be used in place of a namespace (to avoid conflicts with other software). So, I wonder why Boost doesn't do the following: since it defines the intmax_t and uintmax_t types in the boost namespace, it should use the BOOST_ prefix for the corresponding macros, e.g. BOOST_INTMAX_C and BOOST_UINTMAX_C. The only acceptable artifact concerning these types would be that additional types and macros be defined due to the inclusion of standard headers such as <stdint.h>; but in such a case, the behavior would remain consistent (i.e. INTMAX_C and UINTMAX_C would be defined only if intmax_t and uintmax_t are available). If, for some reason, Boost really needs to define the INTMAX_C and UINTMAX_C macros, then it should also define intmax_t and uintmax_t in the global namespace. But I don't think that's a good idea. Regards, -- Vincent Lefèvre <vincent@vinc17.net> - Web: <http://www.vinc17.net/> 100% accessible validated (X)HTML - Blog: <http://www.vinc17.net/blog/> Work: CR INRIA - computer arithmetic / Arénaire project (LIP, ENS-Lyon)

The reason is that mpfr.h supports systems with and without intmax_t (so that it does not force <stdint.h> inclusion), thus it must have a way to determine when intmax_t is available, without requiring the user to do anything else than including <stdint.h> first. So, it has several heuristics, one of them being the test of the existence of INTMAX_C and UINTMAX_C. Indeed, in C, which does not have namespaces, it is rather intuitive that the existence of INTMAX_C and UINTMAX_C implies that the corresponding types intmax_t and uintmax_t are available. However, in C++ with Boost, intmax_t and uintmax_t are not necessarily available (in the global namespace).
Hmm, how can you use INTMAX_C to check for the existance of stdint.h when INTMAX_C is *defined in stdint.h*. Note also that historically C++ systems have not defined INTMAX_C when including stdint.h (or any other header) unless a specific "magic macro" is defined (__STDC_CONSTANT_MACROS). Which brings us on to C++0x and the next C++ std. In there INTMAX_C is always defined after including <cstdint>, but there is no intmax_t in global namespace (only namespace std) - it's this behaviour that Boost's stdint.hpp is trying to emulate. We're also not alone in this - for example ICU will define some of the INT#_C macros if they're not already supplied. And finally... the behaviour was introduced in response to user requests/bug reports, so I don't think we'll change, sorry! BTW Doesn't mpfr already use an autoconf script for configuration? If so isn't it trivial to check for that header in that? HTH, John.

On 2010-02-15 17:17:57 -0000, John Maddock wrote:
The reason is that mpfr.h supports systems with and without intmax_t (so that it does not force <stdint.h> inclusion), thus it must have a way to determine when intmax_t is available, without requiring the user to do anything else than including <stdint.h> first. So, it has several heuristics, one of them being the test of the existence of INTMAX_C and UINTMAX_C. Indeed, in C, which does not have namespaces, it is rather intuitive that the existence of INTMAX_C and UINTMAX_C implies that the corresponding types intmax_t and uintmax_t are available. However, in C++ with Boost, intmax_t and uintmax_t are not necessarily available (in the global namespace).
Hmm, how can you use INTMAX_C to check for the existance of stdint.h when INTMAX_C is *defined in stdint.h*.
Perhaps I wasn't clear enough: if the user wants to use intmax_t, he needs to make it available explicitly by including <stdint.h> before mpfr.h (see below). Then INTMAX_C gets defined (in C).
Note also that historically C++ systems have not defined INTMAX_C when including stdint.h (or any other header) unless a specific "magic macro" is defined (__STDC_CONSTANT_MACROS).
Yes, this is documented in the MPFR manual, as some users use a C++ compiler even to compile C programs. So, for better portability, a user should do: #if defined(__cplusplus) # define __STDC_CONSTANT_MACROS #endif #include <stdint.h> #include <mpfr.h> That's at least for C programs. Now, specifically for C++ programs...
Which brings us on to C++0x and the next C++ std. In there INTMAX_C is always defined after including <cstdint>, but there is no intmax_t in global namespace (only namespace std) - it's this behaviour that Boost's stdint.hpp is trying to emulate. We're also not alone in this - for example ICU will define some of the INT#_C macros if they're not already supplied. And finally... the behaviour was introduced in response to user requests/bug reports, so I don't think we'll change, sorry!
I don't think there are much problems with the next C++ standard. We don't know very much about users who use MPFR with their C++ programs, but I think that in practice, such users who want the stdint types always include <stdint.h> as documented in the MPFR manual, and in this case, intmax_t is available in the global namespace, according to <http://en.wikipedia.org/wiki/Stdint.h>. The problem is with systems that don't have a <stdint.h> header. On such systems at least, Boost can now break MPFR. But I think that if mpfr.h is included *before* C++/Boost-related headers, then everything should work fine, because when the MPFR test on INTMAX_C occurs, this macro hasn't been defined yet by Boost or other C++-related headers.
BTW Doesn't mpfr already use an autoconf script for configuration?
The autoconf script is used to build the MPFR library, not for using the MPFR library in external programs. Moreover the mpfr.h file is the same for all platforms (it is not generated[*]). That's why MPFR functions that have (u)intmax_t as an argument or return type should be declared in mpfr.h only if (u)intmax_t is available. [*] We probably wouldn't like to do that anyway, as the conventional tools are not designed for architecture-dependent headers files. -- Vincent Lefèvre <vincent@vinc17.net> - Web: <http://www.vinc17.net/> 100% accessible validated (X)HTML - Blog: <http://www.vinc17.net/blog/> Work: CR INRIA - computer arithmetic / Arénaire project (LIP, ENS-Lyon)

Perhaps I wasn't clear enough: if the user wants to use intmax_t, he needs to make it available explicitly by including <stdint.h> before mpfr.h (see below). Then INTMAX_C gets defined (in C).
OK understood.
I don't think there are much problems with the next C++ standard. We don't know very much about users who use MPFR with their C++ programs, but I think that in practice, such users who want the stdint types always include <stdint.h> as documented in the MPFR manual, and in this case, intmax_t is available in the global namespace, according to <http://en.wikipedia.org/wiki/Stdint.h>.
No the problem with the next C++ std is *exactly* the same as with Boost: #include <cstdint> // perhaps indirectly via some other #include or std lib header. #include <mpfr.h> // error: no ::intmax_t even though INTMAX_C is defined (in practice may depend on the implementation - but this is what the std says should happen). Contrary to your expectations it is normal in C++ to include the C++ headers such as <cstdint> and *not* the "legacy" C headers such as <stdint.h> to avoid polluting the global namespace. Sorry, but I think you're going to have to deal with this going forward whatever Boost does, HTH, John.

On 2010-02-16 09:34:22 -0000, John Maddock wrote:
No the problem with the next C++ std is *exactly* the same as with Boost:
#include <cstdint> // perhaps indirectly via some other #include or std lib header. #include <mpfr.h> // error: no ::intmax_t even though INTMAX_C is defined (in practice may depend on the implementation - but this is what the std says should happen).
Contrary to your expectations it is normal in C++ to include the C++ headers such as <cstdint> and *not* the "legacy" C headers such as <stdint.h> to avoid polluting the global namespace.
OK, but in practice, that's a bit more complex: users may need legacy C headers. Note that MPFR is first a library written in C, for developers writing their programs in C (there are also third-party interfaces to other languages, though). Some conventional additions to mpfr.h have been done to allow one to use <mpfr.h> in C++ software, but since MPFR is for C primarily, this means that all the types are in the global namespace. This may not be ideal, but some users accept this way of doing (rather than using a MPFR C++ interface). I've looked at the C++0x draft N3000 (November 2009), and <stdint.h> is mentioned (Section 18.4.2); so, it is still part of the standard. But even though Boost appears to work as if <cstdint> were included, the <stdint.h> legacy C header is still unavailable on some platforms (according to the bug report we got) if the user wants to include it too, and I now think that's the main problem. So, either you should tell your users that Boost may no longer be compatible with legacy C headers and if they want to use C libraries, they should use only pure C++ interfaces to them, or a way to use legacy C headers should be documented. Perhaps a general rule should be (I haven't tried): if the user needs to include legacy C headers, he should always include them before C++ headers. -- Vincent Lefèvre <vincent@vinc17.net> - Web: <http://www.vinc17.net/> 100% accessible validated (X)HTML - Blog: <http://www.vinc17.net/blog/> Work: CR INRIA - computer arithmetic / Arénaire project (LIP, ENS-Lyon)
participants (2)
-
John Maddock
-
Vincent Lefevre