Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Preprocessor conditionals and size of type

41 views
Skip to first unread message

Juha Nieminen

unread,
Oct 19, 2016, 12:22:57 PM10/19/16
to
For a project I'm developing, I would want to conditionally compile
something depending on whether unsigned long is (at least) 64-bit
or not. And this has to be done in a manner that allows the code
to be compiled with a C++98 compiler. 100% standard C++ compliance
as well, obviously.

The sizeof operator cannot be used in preprocessor macro conditionals,
so that's not a working solution.

The next approach I considered is to use ULONG_MAX from <climits>,
which is perfectly C++98-compatible. The problem with this, however,
and unlike one could perhaps hastily think, is how to actually check
if ULONG_MAX is a 64-bit value or not.

You see, if the compiler doesn't actually support 64-bit integers,
specifying one in the preprocessor conditional probably won't work
(so you probably can't just do a ULONG_MAX >= 0xFFFFFFFFFFFFFFFFUL).

I suppose that you could do a

#if ULONG_MAX > 0xFFFFFFFFUL

and it might probably work (if it's larger than 32 bits, it's
practically certainly 64-bit), but somehow it doesn't feel completely
right.

Is there a proper way of doing this?

--- news://freenews.netfront.net/ - complaints: ne...@netfront.net ---

Marcel Mueller

unread,
Oct 19, 2016, 12:49:29 PM10/19/16
to
On 19.10.16 18.22, Juha Nieminen wrote:
> For a project I'm developing, I would want to conditionally compile
> something depending on whether unsigned long is (at least) 64-bit
> or not. And this has to be done in a manner that allows the code
> to be compiled with a C++98 compiler. 100% standard C++ compliance
> as well, obviously.
>
> The sizeof operator cannot be used in preprocessor macro conditionals,
> so that's not a working solution.

Some compilers support this, but the standard does not require the
preprocessor to be aware of language features.
AFAIK newer standards allow more at this point.

> The next approach I considered is to use ULONG_MAX from <climits>,
> which is perfectly C++98-compatible. The problem with this, however,
> and unlike one could perhaps hastily think, is how to actually check
> if ULONG_MAX is a 64-bit value or not.

(ULONG_MAX & 0xffffffffL) != ULONG_MAX ?

Of course, it wont tell you how many bits ULONG_MAX actually has.
But you may cascade several of this conditions and execute the ones with
more bits only if the condition before is true to avoid overflows in
integer constants.

> You see, if the compiler doesn't actually support 64-bit integers,
> specifying one in the preprocessor conditional probably won't work
> (so you probably can't just do a ULONG_MAX >= 0xFFFFFFFFFFFFFFFFUL).

It probably won't compile on pure 32 bit platforms. (integer constant
out of range)

> Is there a proper way of doing this?

I found nothing better, even at stackoverflow.

But do you really need the size of long? Or is it more constructive to
use int64_t or something like that where 64 bits are required. In fact
not every 64 bit platform have 64 bit for long int (although this is
likely). You may need long long int in this case. And some 64 bit
platforms use 64 bit even for int. AFAIR DEC Alpha was one of them,
because the Alpha 64 could not access 32 bit int reasonably fast.


Marcel

Alf P. Steinbach

unread,
Oct 19, 2016, 1:58:32 PM10/19/16
to
On 19.10.2016 18:22, Juha Nieminen wrote:
> For a project I'm developing, I would want to conditionally compile
> something depending on whether unsigned long is (at least) 64-bit
> or not. And this has to be done in a manner that allows the code
> to be compiled with a C++98 compiler. 100% standard C++ compliance
> as well, obviously.
>
> The sizeof operator cannot be used in preprocessor macro conditionals,
> so that's not a working solution.
>
> The next approach I considered is to use ULONG_MAX from <climits>,
> which is perfectly C++98-compatible. The problem with this, however,
> and unlike one could perhaps hastily think, is how to actually check
> if ULONG_MAX is a 64-bit value or not.

(ULONG_MAX >> 32) != 0


> You see, if the compiler doesn't actually support 64-bit integers,
> specifying one in the preprocessor conditional probably won't work
> (so you probably can't just do a ULONG_MAX >= 0xFFFFFFFFFFFFFFFFUL).
>
> I suppose that you could do a
>
> #if ULONG_MAX > 0xFFFFFFFFUL
>
> and it might probably work (if it's larger than 32 bits, it's
> practically certainly 64-bit), but somehow it doesn't feel completely
> right.
>
> Is there a proper way of doing this?

I think the most proper way is to factor this code out as different
headers, and let the compiler's include path take care of including the
right one for the compiler at hand.

If not that, then let the compiler invocation define a macro as 0 or 1,
and require that macro symbol.

Only if neither of these solutions are acceptable, consider the simple
checking shown earlier, e.g.,

[code]
#include <limits.h>
#define HAS_BIG_LONG ((ULONG_MAX >> 32) != 0)

#if HAS_BIG_LONG
char const s[] = "Oooh, big long!";
#else
char const s[] = "No big long :-(";
#endif

#include <stdint.h>
#define HAS_BIG_PTR ((INTPTR_MAX >> 32) != 0 )

#if HAS_BIG_PTR
char const t[] = "Oooh, big ptr!";
#else
char const t[] = "No big ptr :-(";
#endif

#include <stdio.h>
auto main() -> int
{ printf( "%s\n%s\n", ::s, ::t ); }
[/code]

[example]
[C:\my\forums\clc++\042]
> g++ foo.cpp && a
No big long :-(
Oooh, big ptr!

[C:\my\forums\clc++\042]
> cl foo.cpp /Feb && b
foo.cpp
No big long :-(
No big ptr :-(

[C:\my\forums\clc++\042]
> _
[/example]

There was a pretty good article once about the evils of using
conditional compilation, it can be worth the effort to find it.


Cheers & hth.,

- Alf

Tim Rentsch

unread,
Oct 19, 2016, 4:00:44 PM10/19/16
to
Juha Nieminen <nos...@thanks.invalid> writes:

> For a project I'm developing, I would want to conditionally compile
> something depending on whether unsigned long is (at least) 64-bit
> or not. And this has to be done in a manner that allows the code
> to be compiled with a C++98 compiler. 100% standard C++ compliance
> as well, obviously.

Here is a way using a rather remarkable macro definition:

#if defined __cplusplus
#include <climits>
#else
#include <limits.h>
#endif

#define MASK_WIDTH( m ) ( 0U + (unsigned)+( \
(m) /((m)%32767 +1) /32767 %32767 *15 \
+ (m) %32767 /((m)%31 +1) /31 %31 *5 \
+ 4 - 12 / ((m)%31 + 3) \
))

#if MASK_WIDTH( ULONG_MAX ) < 64
/* ... unsigned long less than 64 bits ... */
#else
/* ... unsigned long at least 64 bits ... */
#endif

Just to be sure I ran tests with C90, C99, C11, C++98, C++03,
C++11, and C++14. The results were as desired in every case.

Don't be thrown off by what appears to be a cast in the macro
definition. It has no effect when the macro is used in
preprocessor tests, and causes the value of a call to be of type
'unsigned' if and when the macro is used in regular code. It
does not interfere with using the value a MASK_WIDTH() call as an
expression for a 'constexpr' variable (tested with type 'unsigned').

By the way, I can't claim any credit for the original idea behind
the macro. The definition here is my revision of a macro called
IMAX_BITS, which showed up in one of the C newsgroups (sorry I
don't have a reference handy). The revised version here limits
its use of constants to values that are guaranteed to be of type
int (not counting of course the constant 0U which is of type
unsigned int rather than int).

Tim Rentsch

unread,
Oct 19, 2016, 4:09:03 PM10/19/16
to
"Alf P. Steinbach" <alf.p.stein...@gmail.com> writes:

> On 19.10.2016 18:22, Juha Nieminen wrote:
>> For a project I'm developing, I would want to conditionally compile
>> something depending on whether unsigned long is (at least) 64-bit
>> or not. And this has to be done in a manner that allows the code
>> to be compiled with a C++98 compiler. 100% standard C++ compliance
>> as well, obviously.
>>
>> The sizeof operator cannot be used in preprocessor macro conditionals,
>> so that's not a working solution.
>>
>> The next approach I considered is to use ULONG_MAX from <climits>,
>> which is perfectly C++98-compatible. The problem with this, however,
>> and unlike one could perhaps hastily think, is how to actually check
>> if ULONG_MAX is a 64-bit value or not.
>
> (ULONG_MAX >> 32) != 0

In C this expression can be undefined behavior. I'm not
familiar enough with the rules of all the different C++'s
to know if that is also true in C++.

Barry Schwarz

unread,
Oct 19, 2016, 4:53:24 PM10/19/16
to
(((ULONG_MAX >> 15) >> 15) >> 2) != 0
should eliminate any UB issue.

--
Remove del for email

David Brown

unread,
Oct 20, 2016, 3:52:37 AM10/20/16
to
On 19/10/16 18:49, Marcel Mueller wrote:

>
> But do you really need the size of long? Or is it more constructive to
> use int64_t or something like that where 64 bits are required. In fact
> not every 64 bit platform have 64 bit for long int (although this is
> likely). You may need long long int in this case. And some 64 bit
> platforms use 64 bit even for int. AFAIR DEC Alpha was one of them,
> because the Alpha 64 could not access 32 bit int reasonably fast.
>

64-bit Windows has 32-bit "long int", while (AFAIK) all 64-bit *nix
systems have 64-bit "long int".

Using int64_t or int_least64_t is far and away the best choice for a
task like this - /if/ you are using a compiler that supports them. I
don't remember which version of C++ first included them in the
standards, but since they come from C99 I would guess that C++98 did not
include them. Good luck finding a compiler that supports C++98 and does
not have <cstdint> or <stdint.h>, but it's up to the OP to decide how
strict to be about relying only on standards-mandated features.


mark

unread,
Oct 20, 2016, 4:09:38 AM10/20/16
to
On 2016-10-20 09:52, David Brown wrote:
> Good luck finding a compiler that supports C++98 and does
> not have <cstdint> or <stdint.h>,

Visual C++ 2003 - 2008.

David Brown

unread,
Oct 20, 2016, 5:07:03 AM10/20/16
to
Really? I know it had poor or missing C99 support, but I thought the
sized integer types were already in C++03. And several of the C90
compilers I have used had those headers.

I have avoided the dubious pleasure of using MSVC, so don't know all its
limitations.

Öö Tiib

unread,
Oct 20, 2016, 8:16:16 AM10/20/16
to
The <cstdint> was AFAIK added by C++11.
The <stdint.h> was AFAIK added by C99.
To MSVC both were AFAIK added in VS2010.

Tim Rentsch

unread,
Oct 20, 2016, 11:41:33 AM10/20/16
to
Yes, that is one way. I didn't post a fix because I thought it
would be obvious how to avoid the UB once it had been pointed
out.

Now that I look at this more closely, there is another problem,
which is the test isn't quite right: what it tests is if
ULONG_MAX is at least 33 bits, not at least 64 bits. A more
accurate test is

#if ULONG_MAX / 4294967295UL > 4294967295UL
/* at least 64 bits */
#else
/* 63 bits or fewer */
#endif

which I believe is guaranteed to work in all conforming
implementations of C and C++, even those whose integer
datatypes are no more than 32 bits.

Juha Nieminen

unread,
Oct 21, 2016, 5:47:45 AM10/21/16
to
David Brown <david...@hesbynett.no> wrote:
> 64-bit Windows has 32-bit "long int", while (AFAIK) all 64-bit *nix
> systems have 64-bit "long int".

On Windows (even with a compiler like mingw/gcc, which also has a
32-bit long even when compiling a 64-bit executable) it's actually
better to use std::size_t instead of unsigned long, as the former
will be 64-bit (when compiling to a 64-bit executable) while the
latter won't.

But of course checking the size of std::size_t in the preprocessor
is even more problematic.

Juha Nieminen

unread,
Oct 21, 2016, 5:48:37 AM10/21/16
to
Marcel Mueller <news.5...@spamgourmet.org> wrote:
> But do you really need the size of long? Or is it more constructive to
> use int64_t or something like that where 64 bits are required.

It would be, except it's not compatible with C++98.
0 new messages