views:

1361

answers:

7

I'm looking for a way to reliably determine whether C++ code is being compiled in 32 vs 64 bit. We've come up with what we think is a reasonable solution using macros, but was curious to know if people could think of cases where this might fail or if there is a better way to do this. Please note we are trying to do this in a cross-platform, multiple compiler environment.

#if ((ULONG_MAX) == (UINT_MAX))
#define IS32BIT
#else
#define IS64BIT
#endif

#ifdef IS64BIT
DoMy64BitOperation()
#else
DoMy32BitOperation()
#endif

Thanks.

+7  A: 

That won't work on Windows for a start. Longs and ints are both 32 bits whether you're compiling for 32 bit or 64 bit windows. I would think checking if the size of a pointer is 8 bytes is probably a more reliable route.

mattnewport
Unfortunately sizeof is prohibited in #if directive (if you think about it preprocessor has no way to tell)
EFraim
Yep, that's why I left it at suggesting checking the size of a pointer rather than using sizeof - I can't think of a portable way to do it off the top of my head...
mattnewport
Question doesn't (yet) say it *has* to be done at pre-processor time. Many/most compilers with optimisation on will do a decent job of eliminating dead code, even if you "leave it until run time" with a test like `sizeof(void*) == 8 ? Do64Bit() : Do32Bit();`. That could still leave an unused function in the binary, but the expression is likely compiled just to a call to the "right" function.
Steve Jessop
@onebyone that solves the problem of function calls, but what if I want to declare a variable a different type based on platform, that would need to be done at preprocessor unless you want to declare multiple variables and use them based on an if statement (which would also be optimized out if they're unused, but wouldn't be very pleasant in the code)
Falaina
Then you're right, a constant expression in a conditional is no good. Kirill's approach can do what you want, though: `template<int> struct Thing; template<> struct Thing<4> { typedef uint32_t type; }; template<> struct Thing<8> { typedef uint64_t type; }; typedef Thing<sizeof(void*)>::type thingtype;`
Steve Jessop
+13  A: 

Unfortunately there is no cross platform macro which defines 32 / 64 bit across the major compilers. I've found the most effective way to do this is the following.

First I pick my own representation. I prefer ENVIRONMENT64 / ENVIRONMENT32. Then I find out what all of the major compilers use for determining if it's a 64 bit environment or not and use that to set my variables.

// Check windows
#if _WIN32 || _WIN64
#if _WIN64
#define ENVIRONMENT64
#else
#define ENVIRONMENT32
#endif
#endif

// Check GCC
#if __GNUC__
#if __x86_64__ || __ppc64__
#define ENVIRONMENT64
#else
#define ENVIRONMENT32
#endif
#endif

Another easier route is to simply set these variables from the compiler command line.

JaredPar
well, there exist other compilers besides GCC and VS. For example QNX and GHS come to mind (although I suspect QNX has similar build-time defines to GCC). Also you forgot MIPS64 and IA64 architectures in your GCC check
Rom
@Rom, definitely more than 2 compilers and architectures. This is just meant to be a sample of how to approach this problem, not a complete solution.
JaredPar
Usually the "notes for porting this application/library to a new platform" will contain a list of all the header files that need clauses added in order to support a new compiler...
Steve Jessop
I say "usually". "Ideally" is probably more realistic.
Steve Jessop
Thanks, this is pretty close to what we were looking for. We'll add additional compiler options as we encounter them, but this covers 90% of cases right now.
Joe Corkery
_ILP32 and _LP64 on Solaris, although to be pedantic this macro states the size of Int Long and Pointer rather than true 32/64.
Chris Huang-Leaver
+29  A: 
template<int> void DoMyOperationHelper();

template<> void DoMyOperationHelper<4>() 
{
  // do 32-bits operations
}

template<> void DoMyOperationHelper<8>() 
{
  // do 64-bits operations
}

// helper function just to hide clumsy syntax
inline void DoMyOperation() { DoMyOperationHelper<sizeof(size_t)>(); }

int main()
{
  // appropriate function will be selected at compile time 
  DoMyOperation(); 

  return 0;
}
Kirill V. Lyadvinsky
What happens if the size_t is neither 4 nor 8?
Jesper
@Jesper, Then you'll get link error in the sample above. Or you could implement DoMyOperation for that case
Kirill V. Lyadvinsky
Slick use of templates, and kudos for testing what matters (the size of some particular type) rather than a correlate.
Novelocrat
Careful with using size_t for this. You can have issues where it doesn't correspond to the pointer size for instance (eg on platforms with more than one pointer size).
Logan Capaldo
Standard says that size of `size_t` is large enough to hold size of any allocated object in system. Usually it is what you want to know while conditional compiling. If it is not what you want, you could use this snippet with some other type instead of `size_t`. For instance, it could be `void*`.
Kirill V. Lyadvinsky
A: 

If you can use project configurations in all your environments, that would make defining a 64- and 32-bit symbol easy. So you'd have project configurations like this:

32-bit Debug
32-bit Release
64-bit Debug
64-bit Release

EDIT: These are generic configurations, not targetted configurations. Call them whatever you want.

If you can't do that, I like Jared's idea.

Jon Seigel
Or combine the two: auto-detect the configuration on the compilers you know about, but fall back to looking at a #define specified in the project/command-line/whatever on unrecognised compilers.
Steve Jessop
How is your VisualStudio-specific solution going to help with the OP's cross platform question??
alex tingle
I said *if* project configurations are supported.
Jon Seigel
@Jon: Hmm. They are NOT supported in any kind of cross-platform environment *by definition*. Unless it is MS's definition of cross-platform - works on newer flavors of Windows.
EFraim
@EFraim: Yes, you can TARGET 32- or 64-bit using VS, but that is not what I am talking about. Generic project configurations, and the names I assign them, have absolutely nothing to do with platform. If project configurations are VS-specific, then that's a shame because they're very handy.
Jon Seigel
I think this is the right answer. It's more reliable than trying to autodetect things. All IDEs I've ever seen support this feature in some form, and I bet the ones I've never seen support it too. If you use make, or jam, you can set the variables from the command line when invoked, in the usual fashion.
brone
+2  A: 

You should be able to use the macros defined in stdint.h. In particular INTPTR_MAX is exactly the value you need.

#include <cstdint>
#if INTPTR_MAX == INT32_MAX
    #define THIS_IS_32_BIT_ENVIRONMENT
#elif INTPTR_MAX == INT64_MAX
    #define THIS_IS_64_BIT_ENVIRONMENT
#else
    #error "Environment not 32 or 64-bit."
#endif

Some (all?) versions of Microsoft's compiler don't come with stdint.h. Not sure why, since it's a standard file. Here's a version you can use: http://msinttypes.googlecode.com/svn/trunk/stdint.h

alex tingle
Why no stdint.h for Microsoft? Because it was introduced with the C99 standard, and Microsoft seems to have an active aversion to implementing even the easiest of stuff from C99. Even the easy library stuff that requires no compiler change. Even the stuff that's already being done when compiling for C++ (like declarations after statements). I know it needs testing, etc., but I also know that MS gets (or once got) a fair chunk of its library from Dinkumware/Plauger, and Dinkumware's had the C99 library stuff around for years.
Michael Burr
VC++2010 (beta 1, anyway) has `<stdint.h>` and `<cstdint>`. As for the present state of affairs - VC++ library originates from Dinkumware (still does - TR1 was taken from there as well), but from what I recall reading on VCBlog, it undergoes a fairly significant refactoring to compile cleanly with `/clr`, work with all MSVC non-standard types like `__int64`, and so on - which is why it's not as simple as just taking it and putting it into next compiler version.
Pavel Minaev
+1  A: 

"Compiled in 64 bit" is not well defined in C++.

C++ sets only lower limits for sizes such as int, long and void *. There is no guarantee that int is 64 bit even when compiled for a 64 bit platform. The model allows for e.g. 23 bit ints and sizeof(int *) != sizeof(char *)

There are different programming models for 64 bit platforms.

Your best bet is a platform specific test. Your second best, portable decision must be more specific in what is 64 bit.

peterchen
A: 

I'd place 32-bit and 64-bit sources in different files and then select appropriate source files using the build system.

big-z