Proposal for cleanup of INT64 macros

Hello, currently we have a number of macros in boost which deal with whether INT64 support is available of not and in what form it is available. To my knowledge these are (in no particular order): - BOOST_NO_INTRINSIC_INT64_T, - BOOST_NO_INTRINSIC_UINT64_T, - BOOST_NO_INT64, - BOOST_HAS_MS_INT64, and - BOOST_NO_INTEGRAL_INT64_T. I propose to change these as follows. - BOOST_HAS_INT64_T - BOOST_HAS_INTEGRAL_INT64_T - BOOST_HAS_INTRINSIC_INT_64_T - BOOST_HAS_MS_INT64_T Where the following would hold: BOOST_HAS_INTRINSIC_INT_64_T => BOOST_HAS_INTEGRAL_INT64_T => BOOST_HAS_INT64_T. (I don't know where BOOST_HAS_MS_INT64_T would fit in above but I'm sure some MS expert will know.) The reasons for this proposal are: 1. I think the BOOST_NO_.. macros are misnamed. They don't describe a defect (INT64 support isn't mandated by the standard) but rather an extension. Therefore they should be named BOOST_HAS_... 2. Most uses of the above macros are #ifndef(BOOST_NO_INT64) or #ifndef(BOOST_NO_INTEGRAL_INT64_T) or the like. This double negation makes the use harder to understand than neccessary. 3. I think it is safe to assume that when an intrinsic INT64 type is available, the UINT64 type will be intrinsic, too. 4. If one currently wants to know wheter an integral INT64 type is available, the correct way to check is #if !(defined(BOOST_NO_INT64)) && !(defined(BOOST_NO_INTEGRAL_INT64_T)). This would be facilitated to #ifdef BOOST_HAS_INTEGRAL_INT64_T . Is there any interest in this proposal? Any comments and thoughts are welcome. Markus

Hi, Markus Schöpflin wrote:
Hello,
currently we have a number of macros in boost which deal with whether INT64 support is available of not and in what form it is available.
To my knowledge these are (in no particular order):
- BOOST_NO_INTRINSIC_INT64_T, - BOOST_NO_INTRINSIC_UINT64_T, - BOOST_NO_INT64, - BOOST_HAS_MS_INT64, and - BOOST_NO_INTEGRAL_INT64_T.
What version of boost are you referring to ?? <snip> $ find /cvs/boost/boost/config -type f |xargs grep INTRINSIC |grep 64 $ # whoops - no 'INTRINSIC' and '64' at the same line in any config file $ # current state of the Boost main-CVS (head revision) <snap> Wishful thinking ? Well, I would like it, too ;+). According to the docs there is no Boost.Config macro telling me if 'int64_t' in fact is an alias to 'long long' or not. Is one of the above possibilities always and for every compiler guaranteed ? The documentation does not say too much about MS_INT64_T, either. Is it about the name '__int64' instead of 'int64_t' ? Does a Boost.Config user really have to check all these different spellings of 64 bit types like for example: #ifdef BOOST_HAS_LONG_LONG support long_long_type support ulong_long_type #elif defined(BOOST_HAS_INT64_T) support int64_t support uint64_t #elif defined(BOOST_HAS_MS_INT64_T) support __int64 support unsigned __int64 #endif The example assumes there is only one signed/unsigned pair of intrinsic 64 bit integers - as I mentioned before I have no idea if this is always the case. Consider the "support for type" in this example is in fact something like template specialization or overloading a function, where it would trigger an ambiguity situation using separate #if(n)defS ... However, is there a possibility to reduce the painful work a user has to go through to support 64 bit types properly, somehow (probably by using a typedef alias like done for long_long_type, already) ?
I propose to change these as follows.
- BOOST_HAS_INT64_T - BOOST_HAS_INTEGRAL_INT64_T - BOOST_HAS_INTRINSIC_INT_64_T - BOOST_HAS_MS_INT64_T
Where the following would hold: BOOST_HAS_INTRINSIC_INT_64_T => BOOST_HAS_INTEGRAL_INT64_T => BOOST_HAS_INT64_T.
Well, I would not be too sure that the fact that there is an intrinsic int64 implies its vaildity within integral contant expressions.
(I don't know where BOOST_HAS_MS_INT64_T would fit in above but I'm sure some MS expert will know.)
The reasons for this proposal are:
1. I think the BOOST_NO_.. macros are misnamed. They don't describe a defect (INT64 support isn't mandated by the standard) but rather an extension. Therefore they should be named BOOST_HAS_...
2. Most uses of the above macros are #ifndef(BOOST_NO_INT64) or #ifndef(BOOST_NO_INTEGRAL_INT64_T) or the like. This double negation makes the use harder to understand than neccessary.
IMHO this is correct.
3. I think it is safe to assume that when an intrinsic INT64 type is available, the UINT64 type will be intrinsic, too.
4. If one currently wants to know wheter an integral INT64 type is available, the correct way to check is #if !(defined(BOOST_NO_INT64)) && !(defined(BOOST_NO_INTEGRAL_INT64_T)). This would be facilitated to #ifdef BOOST_HAS_INTEGRAL_INT64_T .
Really ? According to the documentation the INTEGRAL macro is about ICEs.
Is there any interest in this proposal? Any comments and thoughts are welcome.
Regards, Tobias

Tobias Schwinger wrote:
Markus Schöpflin wrote:
currently we have a number of macros in boost which deal with whether INT64 support is available of not and in what form it is available.
To my knowledge these are (in no particular order):
- BOOST_NO_INTRINSIC_INT64_T, - BOOST_NO_INTRINSIC_UINT64_T, - BOOST_NO_INT64, - BOOST_HAS_MS_INT64, and - BOOST_NO_INTEGRAL_INT64_T.
What version of boost are you referring to ??
<snip> $ find /cvs/boost/boost/config -type f |xargs grep INTRINSIC |grep 64 $ # whoops - no 'INTRINSIC' and '64' at the same line in any config file $ # current state of the Boost main-CVS (head revision) <snap>
Wishful thinking ? Well, I would like it, too ;+).
It was used in "boost/archive/polymorphic_[io]archive.hpp" but was removed yesterday. Honestly. :-)
According to the docs there is no Boost.Config macro telling me if 'int64_t' in fact is an alias to 'long long' or not.
True, the only reference I discovered was in the serialization library, it was defined there.
Is one of the above possibilities always and for every compiler guaranteed ?
Which possibilities?
The documentation does not say too much about MS_INT64_T, either. Is it about the name '__int64' instead of 'int64_t' ?
Don't know.
Does a Boost.Config user really have to check all these different spellings of 64 bit types like for example:
#ifdef BOOST_HAS_LONG_LONG support long_long_type support ulong_long_type #elif defined(BOOST_HAS_INT64_T) support int64_t support uint64_t #elif defined(BOOST_HAS_MS_INT64_T) support __int64 support unsigned __int64 #endif
Well, I would hope that <boost/cstdint.hpp> takes care of this, always giving you a type called [u]int64_t. Only when the special properties of this type matter you would have to use the macros.
The example assumes there is only one signed/unsigned pair of intrinsic 64 bit integers - as I mentioned before I have no idea if this is always the case.
Consider the "support for type" in this example is in fact something like template specialization or overloading a function, where it would trigger an ambiguity situation using separate #if(n)defS ...
However, is there a possibility to reduce the painful work a user has to go through to support 64 bit types properly, somehow (probably by using a typedef alias like done for long_long_type, already) ?
I'm not sure if I understand you correctly? You mean "typedef uint64_t unsigned long long" where it is not already available?
I propose to change these as follows.
- BOOST_HAS_INT64_T - BOOST_HAS_INTEGRAL_INT64_T - BOOST_HAS_INTRINSIC_INT_64_T - BOOST_HAS_MS_INT64_T
Where the following would hold: BOOST_HAS_INTRINSIC_INT_64_T => BOOST_HAS_INTEGRAL_INT64_T => BOOST_HAS_INT64_T.
Well, I would not be too sure that the fact that there is an intrinsic int64 implies its vaildity within integral contant expressions.
It's not guaranteed, that is true. But do you know of a compiler which has an intrinsic 64 bit type which can't be used in integral constant expressions? As a side note, which compilers actually would have BOOST_HAS_INTRINSIC_INT_64_T defined? The only 64 bit platform I have used is Tru64/Alpha and there int64_t is just an alias for long long.
(I don't know where BOOST_HAS_MS_INT64_T would fit in above but I'm sure some MS expert will know.)
The reasons for this proposal are:
1. I think the BOOST_NO_.. macros are misnamed. They don't describe a defect (INT64 support isn't mandated by the standard) but rather an extension. Therefore they should be named BOOST_HAS_...
2. Most uses of the above macros are #ifndef(BOOST_NO_INT64) or #ifndef(BOOST_NO_INTEGRAL_INT64_T) or the like. This double negation makes the use harder to understand than neccessary.
IMHO this is correct.
3. I think it is safe to assume that when an intrinsic INT64 type is available, the UINT64 type will be intrinsic, too.
4. If one currently wants to know wheter an integral INT64 type is available, the correct way to check is #if !(defined(BOOST_NO_INT64)) && !(defined(BOOST_NO_INTEGRAL_INT64_T)). This would be facilitated to #ifdef BOOST_HAS_INTEGRAL_INT64_T .
Really ? According to the documentation the INTEGRAL macro is about ICEs.
Of course, you are right. Change the above sentence to "... whether the INT64 type can be used in an integral constant expression, ...". Markus

According to the docs there is no Boost.Config macro telling me if 'int64_t' in fact is an alias to 'long long' or not.
Correct: int64_t and uint64_t are only defined by us if there is no platform <stdint.h>, and if we don't define those types then we don't know what the "real" type of these are. They are defined to be the smallest 64-bit integral type BTW, so int is prefered over long is prefered over long long.
Is one of the above possibilities always and for every compiler guaranteed ?
The documentation does not say too much about MS_INT64_T, either. Is it about the name '__int64' instead of 'int64_t' ?
No, it's used to to help implement int64_t. It tells you that __int64 is an intrinsic type, that's all.
Does a Boost.Config user really have to check all these different spellings of 64 bit types like for example:
#ifdef BOOST_HAS_LONG_LONG support long_long_type support ulong_long_type #elif defined(BOOST_HAS_INT64_T) support int64_t support uint64_t #elif defined(BOOST_HAS_MS_INT64_T) support __int64 support unsigned __int64 #endif
NO!!!!!! Just check BOOST_NO_INT64_T after including <boost/cstdint.hpp> and then use [u]stdint_t if you want a 64-bit integer type.
The example assumes there is only one signed/unsigned pair of intrinsic 64 bit integers - as I mentioned before I have no idea if this is always the case.
Do you mean more than one type? If so then yes there may be many 64-bit integral types - especially if int or long are 64-bit types.
Consider the "support for type" in this example is in fact something like template specialization or overloading a function, where it would trigger an ambiguity situation using separate #if(n)defS ...
That's what BOOST_HAS_LONG_LONG and BOOST_HAS_MS_INT64 are for - see is_integer for an example of use: template <class T> is_integral{...}; template<> is_integral<unsigned char>{...}; // etc #ifdef BOOST_HAS_LONG_LONG template<> is_integral<long long>{...}; template<> is_integral<unsigned long long>{...}; #elif defined(BOOST_HAS_MS_INT64) template<> is_integral<__int64>{...}; template<> is_integral<unsigned __int64>{...}; #endif Note these macros should only be used when overloading/specialising for all intrinsic types, such usage should probably be infequent IMO.
However, is there a possibility to reduce the painful work a user has to go through to support 64 bit types properly, somehow (probably by using a typedef alias like done for long_long_type, already) ?
I doubt it, and it's not painful once you get your head around what the macros actually mean, honestly ;-) John.

That's what BOOST_HAS_LONG_LONG and BOOST_HAS_MS_INT64 are for - see is_integer for an example of use:
template <class T> is_integral{...};
template<> is_integral<unsigned char>{...}; // etc #ifdef BOOST_HAS_LONG_LONG template<> is_integral<long long>{...}; template<> is_integral<unsigned long long>{...}; #elif defined(BOOST_HAS_MS_INT64) template<> is_integral<__int64>{...}; template<> is_integral<unsigned __int64>{...}; #endif
Note these macros should only be used when overloading/specialising for all intrinsic types, such usage should probably be infequent IMO.
I forgot to say: this use case is potentially more complex still: 1) It's only a matter of time before we see 128-bit integer types (assuming that their not here already), these will not necessarily be spelled "long long". 2) For platforms where sizeof(int) == 8 then one of int32_t or int16_t will be type "short", and the other will be some vendor specific type (I believe this is already the case on some Cray systems, but don't have the details, and we don't have a macro for this either... yet!). I've seen someone half jokingly suggest "short short" for the name of this type :-) 3) There are two compilers (MSVC6 and compatible Intel versions), where __int8, __int16, __int32 are distinct types from the standard and same sized integral types (short, int etc). Fortunately MS fixed that one with VC7. In other words overloading/specialising on all intrinsic integer types is complex and in general the full case needs vendor specific code, sorry :-( John.

John Maddock wrote:
Does a Boost.Config user really have to check all these different spellings of 64 bit types like for example:
#ifdef BOOST_HAS_LONG_LONG support long_long_type support ulong_long_type #elif defined(BOOST_HAS_INT64_T) support int64_t support uint64_t #elif defined(BOOST_HAS_MS_INT64_T) support __int64 support unsigned __int64 #endif
NO!!!!!!
Just check BOOST_NO_INT64_T after including <boost/cstdint.hpp> and then use [u]stdint_t if you want a 64-bit integer type.
Sounds good. "Assertion": "The existence of _any_ 64-bit integer type implies the definedness of [u]int64_t and in this case the macro BOOST_NO_INT64_T is not set." Correct ?
The example assumes there is only one signed/unsigned pair of intrinsic 64 bit integers - as I mentioned before I have no idea if this is always the case.
Do you mean more than one type? If so then yes there may be many 64-bit integral types - especially if int or long are 64-bit types.
I meant: "... more than one unsigned/signed pair of 64-bit wide types with distinct identities".
Consider the "support for type" in this example is in fact something like template specialization or overloading a function, where it would trigger an ambiguity situation using separate #if(n)defS ...
That's what BOOST_HAS_LONG_LONG and BOOST_HAS_MS_INT64 are for - see is_integer for an example of use:
template <class T> is_integral{...};
template<> is_integral<unsigned char>{...}; // etc #ifdef BOOST_HAS_LONG_LONG template<> is_integral<long long>{...}; template<> is_integral<unsigned long long>{...}; #elif defined(BOOST_HAS_MS_INT64) template<> is_integral<__int64>{...}; template<> is_integral<unsigned __int64>{...}; #endif
Note these macros should only be used when overloading/specialising for all intrinsic types, such usage should probably be infequent IMO.
Indeed. Unfortunately I have to overload for all intrinsic types ;-( , that's why I ask these strange questions. (After reading your following post and 'is_integral.hpp', I figure working around it with 'is_integral' - although unhandy in my particular situation - may be the lesser of two evils...)
However, is there a possibility to reduce the painful work a user has to go through to support 64 bit types properly, somehow (probably by using a typedef alias like done for long_long_type, already) ?
I doubt it, and it's not painful once you get your head around what the macros actually mean, honestly ;-)
Agreed. How does Boost.Config provide me with information how these types behave in integral promotion/conversion, then ? Just kidding ;-) ... Thanks a lot for your help ! Tobias

Sounds good. "Assertion":
"The existence of _any_ 64-bit integer type implies the definedness of [u]int64_t and in this case the macro BOOST_NO_INT64_T is not set."
Correct ?
Correct.
Indeed. Unfortunately I have to overload for all intrinsic types ;-( , that's why I ask these strange questions.
(After reading your following post and 'is_integral.hpp', I figure working around it with 'is_integral' - although unhandy in my particular situation - may be the lesser of two evils...)
OK understood, in that case you need to go down the is_integral route. John.

Marcus, There seems to be a lot of confusion about these macros, so let me try and give you the full lowdown: BOOST_HAS_LONG_LONG Indicates that [unsigned] long long is a valid type, use only when overloading a function or specialising a template for all intrinsic integral types (see is_integral for example). Do not use this macro (or long long) if you want a 64-bit interal type. BOOST_HAS_MS_INT64 Indicates that [unsigned] __int64 is a valid type, use only when overloading a function or specialising a template for all intrinsic integral types (see is_integral for example). Do not use this macro (or long long) if you want a 64-bit interal type. Do not use if BOOST_HAS_LONG_LONG is set since __int64 is the same type as long long on all compilers that have both. BOOST_NO_INT64 Indicates that boost::int64_t and boost_uint64_t do not exist in boost/cstdint.hpp. It's a defect macro because these types are documented as existing; it doesn't indicate a defect in the compiler, although if we're being picky, neither these types nor *any* of the exact width types in boost/cstdint.hpp are required to be supported by C99. If you need a 64-bit integer type then this macro is the one to check (and then use [u]int64_t). BOOST_NO_INTEGRAL_INT64_T Indicates that int64_t and uint64_t can not be used in integral constant expressions (that includes their use as non-type template parameters). Contrary to what Marcus suggested there are several compilers that have this defect - a quick grep through the config system will show you which ones - in fact until recently is was the exception rather than the rule for compilers to accept types wider than int in integral constant expressions, and especially in enum types. Those macros are needed IMO, and should also be sufficient. The macros BOOST_NO_INTRINSIC_INT64_T and BOOST_NO_INTRINSIC_UINT64_T appear to have been removed, so no cleanup necessary :-) John.

John Maddock wrote:
There seems to be a lot of confusion about these macros, so let me try and give you the full lowdown:
[snip detailed and very helpful explanation] John, thank you very much for this explanation. Would you mind adding this wonderful documentation to the configuration reference page? I think it would help people like me a lot to better understand what those macros are actually meant for and how they should be used. Markus

John, thank you very much for this explanation. Would you mind adding this wonderful documentation to the configuration reference page? I think it would help people like me a lot to better understand what those macros are actually meant for and how they should be used.
I had a suspicion you would say that ;-) Will do. John.
participants (3)
-
John Maddock
-
Markus Schöpflin
-
Tobias Schwinger