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.
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.
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;
}
Unfortunately, in a cross platform, cross compiler environment, there is no single reliable method to do this purely at compile time.
Both _WIN32 and _WIN64 can sometimes both be undefined, if the project settings are flawed or corrupted (particularly on Visual Studio 2008 SP1).
A project labelled "Win32" could be set to 64-bit, due to a project configuration error.
On Visual Studio 2008 SP1, sometimes the intellisense does not grey out the correct parts of the code, according to the current #define. This makes it difficult to see exactly which #define is being used at compile time.
Therefore, the only reliable method is to combine 3 simple checks:
1) Compile time setting, and;
2) Runtime check, and;
3) Robust compile time checking.
Simple check 1/3: Compile time setting
Choose any method to set the required #define variable. I suggest the method from #JaredPar:
// Check windows
#if _WIN32 || _WIN64
#if _WIN64
#define ENV64BIT
#else
#define ENV32BIT
#endif
#endif
// Check GCC
#if __GNUC__
#if __x86_64__ || __ppc64__
#define ENV64BIT
#else
#define ENV32BIT
#endif
#endif
Simple check 2/3: Runtime check
In main(), double check to see if sizeof() makes sense:
#if defined(ENV64BIT)
if (sizeof(void*) != 8)
{
wprintf(L"ENV64BIT: Error: pointer should be 8 bytes. Exiting.");
exit(0);
}
wprintf(L"Diagnostics: we are running in 64-bit mode.\n");
#elif defined (ENV32BIT)
if (sizeof(void*) != 4)
{
wprintf(L"ENV32BIT: Error: pointer should be 4 bytes. Exiting.");
exit(0);
}
wprintf(L"Diagnostics: we are running in 32-bit mode.\n");
#else
#error "Must define either ENV32BIT or ENV64BIT".
#endif
Simple check 3/3: Robust compile time checking
The general rule is "every #define must end in a #else which generates an error".
#if defined(ENV64BIT)
// 64-bit code here.
#elif defined (ENV32BIT)
// 32-bit code here.
#else
// INCREASE ROBUSTNESS. ALWAYS THROW AN ERROR ON THE ELSE.
// - What if I made a typo and checked for ENV6BIT instead of ENV64BIT?
// - What if both ENV64BIT and ENV32BIT are not defined?
// - What if project is corrupted, and _WIN64 and _WIN32 are not defined?
// - What if I didn't include the required header file?
// - What if I checked for _WIN32 first instead of second?
// (in Windows, both are defined in 64-bit, so this will break codebase)
// - What if the code has just been ported to a different OS?
// - What if there is an unknown unknown, not mentioned in this list so far?
// I'm only human, and the mistakes above would break the *entire* codebase.
#error "Must define either ENV32BIT or ENV64BIT"
#endif
Update 2017-01-17
Comment from #AI.G:
4 years later (don't know if it was possible before) you can convert
the run-time check to compile-time one using static assert:
static_assert(sizeof(void*) == 4);. Now it's all done at compile time
:)
Appendix A
Incidentially, the rules above can be adapted to make your entire codebase more reliable:
Every if() statement ends in an "else" which generates a warning or error.
Every switch() statement ends in a "default:" which generates a warning or error.
The reason why this works well is that it forces you to think of every single case in advance, and not rely on (sometimes flawed) logic in the "else" part to execute the correct code.
I used this technique (among many others) to write a 30,000 line project that worked flawlessly from the day it was first deployed into production (that was 12 months ago).
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
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.
You could do this:
#if __WORDSIZE == 64
char *size = "64bits";
#else
char *size = "32bits";
#endif
Try this:
#ifdef _WIN64
// 64 bit code
#elif _WIN32
// 32 bit code
#else
if(sizeof(void*)==4)
// 32 bit code
else
// 64 bit code
#endif
Below code works fine for most current environments:
#if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__) ) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(__powerpc64__)
#define IS64BIT 1
#else
#define IS32BIT 1
#endif
"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.
Your approach was not too far off, but you are only checking whether long and int are of the same size. Theoretically, they could both be 64 bits, in which case your check would fail, assuming both to be 32 bits. Here is a check that actually checks the size of the types themselves, not their relative size:
#if ((UINT_MAX) == 0xffffffffu)
#define INT_IS32BIT
#else
#define INT_IS64BIT
#endif
#if ((ULONG_MAX) == 0xfffffffful)
#define LONG_IS32BIT
#else
#define LONG_IS64BIT
#endif
In principle, you can do this for any type for which you have a system defined macro with the maximal value.
Note, that the standard requires long long to be at least 64 bits even on 32 bit systems.
People already suggested methods that will try to determine if the program is being compiled in 32-bit or 64-bit.
And I want to add that you can use the c++11 feature static_assert to make sure that the architecture is what you think it is ("to relax").
So in the place where you define the macros:
#if ...
# define IS32BIT
static_assert(sizeof(void *) == 4, "Error: The Arch is not what I think it is")
#elif ...
# define IS64BIT
static_assert(sizeof(void *) == 8, "Error: The Arch is not what I think it is")
#else
# error "Cannot determine the Arch"
#endif
Borrowing from Contango's excellent answer above and combining it with "Better Macros, Better Flags" from Fluent C++, you can do:
// Macro for checking bitness (safer macros borrowed from
// https://www.fluentcpp.com/2019/05/28/better-macros-better-flags/)
#define MYPROJ_IS_BITNESS( X ) MYPROJ_IS_BITNESS_PRIVATE_DEFINITION_##X()
// Bitness checks borrowed from https://stackoverflow.com/a/12338526/201787
#if _WIN64 || ( __GNUC__ && __x86_64__ )
# define MYPROJ_IS_BITNESS_PRIVATE_DEFINITION_64() 1
# define MYPROJ_IS_BITNESS_PRIVATE_DEFINITION_32() 0
# define MYPROJ_IF_64_BIT_ELSE( x64, x86 ) (x64)
static_assert( sizeof( void* ) == 8, "Pointer size is unexpected for this bitness" );
#elif _WIN32 || __GNUC__
# define MYPROJ_IS_BITNESS_PRIVATE_DEFINITION_64() 0
# define MYPROJ_IS_BITNESS_PRIVATE_DEFINITION_32() 1
# define MYPROJ_IF_64_BIT_ELSE( x64, x86 ) (x86)
static_assert( sizeof( void* ) == 4, "Pointer size is unexpected for this bitness" );
#else
# error "Unknown bitness!"
#endif
Then you can use it like:
#if MYPROJ_IS_BITNESS( 64 )
DoMy64BitOperation()
#else
DoMy32BitOperation()
#endif
Or using the extra macro I added:
MYPROJ_IF_64_BIT_ELSE( DoMy64BitOperation(), DoMy32BitOperation() );
Here are a few more ways to do what you want in modern C++.
You can create a variable that defines the number of system bits:
static constexpr size_t sysbits = (CHAR_BIT * sizeof(void*));
And then in C++17 you can do something like:
void DoMy64BitOperation() {
std::cout << "64-bit!\n";
}
void DoMy32BitOperation() {
std::cout << "32-bit!\n";
}
inline void DoMySysBitOperation()
{
if constexpr(sysbits == 32)
DoMy32BitOperation();
else if constexpr(sysbits == 64)
DoMy64BitOperation();
/*else - other systems. */
}
Or in C++20:
template<void* = nullptr>
// template<int = 32> // May be clearer, pick whatever you like.
void DoMySysBitOperation()
requires(sysbits == 32)
{
std::cout << "32-bit!\n";
}
template<void* = nullptr>
// template<int = 64>
void DoMySysBitOperation()
requires(sysbits == 64)
{
std::cout << "64-bit!\n";
}
template<void* = nullptr>
void DoMySysBitOperation()
/* requires(sysbits == OtherSystem) */
{
std::cout << "Unknown System!\n";
}
The template<...> is usually not needed, but since those functions will have the same mangling name, we must enforce the compiler to pick the correct ones. Also, template<void* = nullptr> may be confusing ( The other template may be better and more logically correct ), I only used it as a workaround to satisfy the compiler name mangling.
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.
I'd place 32-bit and 64-bit sources in different files and then select appropriate source files using the build system.
I'm adding this answer as a use case and complete example for the runtime-check described in another answer.
This is the approach I've been taking for conveying to the end-user whether the program was compiled as 64-bit or 32-bit (or other, for that matter):
version.h
#ifndef MY_VERSION
#define MY_VERSION
#include <string>
const std::string version = "0.09";
const std::string arch = (std::to_string(sizeof(void*) * 8) + "-bit");
#endif
test.cc
#include <iostream>
#include "version.h"
int main()
{
std::cerr << "My App v" << version << " [" << arch << "]" << std::endl;
}
Compile and Test
g++ -g test.cc
./a.out
My App v0.09 [64-bit]
Related
I have written a benchmark and test it with different compilers (MinGW, Visual, 32-bit and 64-bit and want to print out the type of compiler used with the benchmark data.
I have found QSysInfo::buildCpuArchitecture() to get info wether 32-bit or 64-bit build has been performed.
How can I get the info about the compiler name or vendor?
I did the following:
// get compiler used
#if defined(_MSC_VER)
# define CPL "VC++"
#elif defined(__GNUC__)
# define CPL "Mingw"
#else
# define CPL "other CPL"
#endif
inline QString compilerInfo() { return( QString(CPL) + " " + (( QSysInfo::buildCpuArchitecture() == "i386")? "32-bit":"64-bit")); }
I found some lines of code, those are dimmed in my preprocessor block of source code in C. My compiler, MS Visual Studio, naming it "inactive preprocessor block". What does this mean, will my compile do not consider these lines of code,
and how to make it active block?
An inactive preprocessor block is a block of code that is deactivated because of a preprocessor directive. The simplest example is:
#if 0
//everytyhing here is inactive and will be ignored during compilation
#endif
A more common example would be
#ifdef SOME_VAR
// code
#else
// other code
#endif
In this case either the first or the second block of code will be inactive depending on whether SOME_VAR is defined.
Please check this hypothetical example created to elaborate your question.
#include <iostream>
#include <windef.h>
#define _WIN32
int add(int n1, int n2){return n1 + n2;}
LONGLONG add(LONGLONG n1, LONGLONG n2){return n1 + n2;}
int _tmain(int argc, _TCHAR* argv[])
{
#ifdef _WIN32
int val = add(10, 12);
#else
LONGLONG = add(100L, 120L);//Inactive code
#endif // _WIN32
return 0;
}
You can see as _WIN32 is defined the code in #else pre-processor directive is disabled and would not be compiled. You can undefine _WIN32 to see reverse in action. See the screen shot of MS Visual Studio attached. The line in red is disabled code.
Hope this would help.
The preprocessor is one the earliest stages of a programs translation. It can modify the source of the program before the compilation stage begins. That way you can configure the source to build differently, depending on various constraints.
Uses of preprocessor condition blocks include:
Completely commenting out code:
#if 0
// The code here is never compiled. It's "commented" away
#endif
Provide different implementations based on various constraints, like platfrom
#if defined(WIN32)
//Implement widget with Win32Api
#elif defined(MOTIF)
// Implement widget with Motif framework
#else
#error "Unknown platform"
#endif
Have a macro like assert behave in different ways.
Make sure a useful abstraction is defined appropriately:
#if PLATFORM_A
typedef long int32_t;
#elif PLATFORM_B
typedef int int32_t;
I used the answer from Determining 32 vs 64 bit in C++ to make this:
#ifndef AVUNA_CFG
#define AVUNA_CFG
#if _WIN32 || _WIN64
#if _WIN64
#define BIT64
#else
#define BIT32
#endif
#endif
// Check GCC
#if __GNUC__
#if __x86_64__ || __ppc64__
#define BIT64
#else
#define BIT32
#endif
#endif
#endif
However, this doesn't seem to work when specifying -m32 to GCC for cross compiling, so it always says BIT64. Is there any defines I can use for this purpose?
I ended up using an Eclipse-define because I have two different run configurations for 32/64-bit cross compile. Works well.
I have encountered the #define pre-processor directive before while learning C, and then also encountered it in some code I read. But apart from using it to definite substitutions for constants and to define macros, I've not really understook the special case where it is used without a "body" or token-string.
Take for example this line:
#define OCSTR(X)
Just like that! What could be the use of this or better, when is this use of #define necessary?
This is used in two cases. The first and most frequent involves
conditional compilation:
#ifndef XYZ
#define XYZ
// ...
#endif
You've surely used this yourself for include guards, but it can also be
used for things like system dependencies:
#ifdef WIN32
// Windows specific code here...
#endif
(In this case, WIN32 is more likely defined on the command line, but it
could also be defined in a "config.hpp" file.) This would normally
only involve object-like macros (without an argument list or
parentheses).
The second would be a result of conditional compilation. Something
like:
#ifdef DEBUG
#define TEST(X) text(X)
#else
#define TEST(X)
#endif
That allows writing things like:
TEST(X);
which will call the function if DEBUG is defined, and do nothing if it
isn't.
Such macro usually appears in pair and inside conditional #ifdef as:
#ifdef _DEBUG
#define OCSTR(X)
#else
#define OCSTR(X) SOME_TOKENS_HERE
#endif
Another example,
#ifdef __cplusplus
#define NAMESPACE_BEGIN(X) namespace X {
#define NAMESPACE_END }
#else
#define NAMESPACE_BEGIN(X)
#define NAMESPACE_END
#endif
One odd case that I recently dug up to answer a question turned out to be simply commentary in nature. The code in question looked like:
void CLASS functionName(){
//
//
//
}
I discovered it was just an empty #define, which the author had chosen to document that the function accessed global variables in the project:
C++ syntax: void CLASS functionName()?
So not really that different from if it said /* CLASS */, except not allowing typos like /* CLAAS */...some other small benefits perhaps (?)
I agree with every answer, but I'd like to point out a small trivial thing.
Being a C purist I've grown up with the assertion that EACH AND EVERY #define should be an expression, so, even if it's common practice using:
#define WHATEVER
and test it with
#ifdef WHATEVER
I think it's always better writing:
#define WHATEVER (1)
also #debug macros shall be expressions:
#define DEBUG (xxx) (whatever you want for debugging, value)
In this way, you are completely safe from misuse of #macros and prevents nasty problems (especially in a 10 million line C project)
This can be used when you may want to silent some function. For example in debug mode you want to print some debug statements and in production code you want to omit them:
#ifdef DEBUG
#define PRINT(X) printf("%s", X)
#else
#define PRINT(X) // <----- silently removed
#endif
Usage:
void foo ()
{
PRINT("foo() starts\n");
...
}
#define macros are simply replaced, literally, by their replacement text during preprocessing. If there is no replacement text, then ... they're replaced by nothing! So this source code:
#define FOO(x)
print(FOO(hello world));
will be preprocessed into just this:
print();
This can be useful to get rid of things you don't want, like, say, assert(). It's mainly useful in conditional situations, where under some conditions there's a non-empty body, though.
As you can see in the above responses, it can be useful when debugging your code.
#ifdef DEBUG
#define debug(msg) fputs(__FILE__ ":" (__LINE__) " - " msg, stderr)
#else
#define debug(msg)
#endif
So, when you are debugging, the function will print the line number and file name so you know if there is an error. And if you are not debugging, it will just produce no output
There are many uses for such a thing.
For example, one is for the macro to have different behavior in different builds. For example, if you want debug messages, you could have something like this:
#ifdef _DEBUG
#define DEBUG_LOG(X, ...) however_you_want_to_print_it
#else
#define DEBUG_LOG(X, ...) // nothing
#endif
Another use could be to customize your header file based on your system. This is from my mesa-implemented OpenGL header in linux:
#if !defined(OPENSTEP) && (defined(__WIN32__) && !defined(__CYGWIN__))
# if defined(__MINGW32__) && defined(GL_NO_STDCALL) || defined(UNDER_CE) /* The generated DLLs by MingW with STDCALL are not compatible with the ones done by Microsoft's compilers */
# define GLAPIENTRY
# else
# define GLAPIENTRY __stdcall
# endif
#elif defined(__CYGWIN__) && defined(USE_OPENGL32) /* use native windows opengl32 */
# define GLAPIENTRY __stdcall
#elif defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__) >= 303
# define GLAPIENTRY
#endif /* WIN32 && !CYGWIN */
#ifndef GLAPIENTRY
#define GLAPIENTRY
#endif
And used in header declarations like:
GLAPI void GLAPIENTRY glClearIndex( GLfloat c );
GLAPI void GLAPIENTRY glClearColor( GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha );
GLAPI void GLAPIENTRY glClear( GLbitfield mask );
...
(I removed the part for GLAPI)
So you get the picture, a macro that is used in some cases and not used in other cases could be defined to something on those cases and nothing to those other cases.
Other cases could be as follows:
If the macro doesn't take parameters, it could be just to declare some case. A famous example is to guard header files. Another example would be something like this
#define USING_SOME_LIB
and later could be used like this:
#ifdef USING_SOME_LIB
...
#else
...
#endif
Could be that the macro was used at some stage to do something (for example log), but then on release the owner decided the log is not useful anymore and simply removed the contents of the macro so it becomes empty. This is not recommended though, use the method I mentioned in the very beginning of the answer.
Finally, it could be there just for more explanation, for example you can say
#define DONT_CALL_IF_LIB_NOT_INITIALIZED
and you write functions like:
void init(void);
void do_something(int x) DONT_CALL_IF_LIB_NOT_INITIALIZED;
Although this last case is a bit absurd, but it would make sense in such a case:
#define IN
#define OUT
void function(IN char *a, OUT char *b);
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.
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.
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;
}
Unfortunately, in a cross platform, cross compiler environment, there is no single reliable method to do this purely at compile time.
Both _WIN32 and _WIN64 can sometimes both be undefined, if the project settings are flawed or corrupted (particularly on Visual Studio 2008 SP1).
A project labelled "Win32" could be set to 64-bit, due to a project configuration error.
On Visual Studio 2008 SP1, sometimes the intellisense does not grey out the correct parts of the code, according to the current #define. This makes it difficult to see exactly which #define is being used at compile time.
Therefore, the only reliable method is to combine 3 simple checks:
1) Compile time setting, and;
2) Runtime check, and;
3) Robust compile time checking.
Simple check 1/3: Compile time setting
Choose any method to set the required #define variable. I suggest the method from #JaredPar:
// Check windows
#if _WIN32 || _WIN64
#if _WIN64
#define ENV64BIT
#else
#define ENV32BIT
#endif
#endif
// Check GCC
#if __GNUC__
#if __x86_64__ || __ppc64__
#define ENV64BIT
#else
#define ENV32BIT
#endif
#endif
Simple check 2/3: Runtime check
In main(), double check to see if sizeof() makes sense:
#if defined(ENV64BIT)
if (sizeof(void*) != 8)
{
wprintf(L"ENV64BIT: Error: pointer should be 8 bytes. Exiting.");
exit(0);
}
wprintf(L"Diagnostics: we are running in 64-bit mode.\n");
#elif defined (ENV32BIT)
if (sizeof(void*) != 4)
{
wprintf(L"ENV32BIT: Error: pointer should be 4 bytes. Exiting.");
exit(0);
}
wprintf(L"Diagnostics: we are running in 32-bit mode.\n");
#else
#error "Must define either ENV32BIT or ENV64BIT".
#endif
Simple check 3/3: Robust compile time checking
The general rule is "every #define must end in a #else which generates an error".
#if defined(ENV64BIT)
// 64-bit code here.
#elif defined (ENV32BIT)
// 32-bit code here.
#else
// INCREASE ROBUSTNESS. ALWAYS THROW AN ERROR ON THE ELSE.
// - What if I made a typo and checked for ENV6BIT instead of ENV64BIT?
// - What if both ENV64BIT and ENV32BIT are not defined?
// - What if project is corrupted, and _WIN64 and _WIN32 are not defined?
// - What if I didn't include the required header file?
// - What if I checked for _WIN32 first instead of second?
// (in Windows, both are defined in 64-bit, so this will break codebase)
// - What if the code has just been ported to a different OS?
// - What if there is an unknown unknown, not mentioned in this list so far?
// I'm only human, and the mistakes above would break the *entire* codebase.
#error "Must define either ENV32BIT or ENV64BIT"
#endif
Update 2017-01-17
Comment from #AI.G:
4 years later (don't know if it was possible before) you can convert
the run-time check to compile-time one using static assert:
static_assert(sizeof(void*) == 4);. Now it's all done at compile time
:)
Appendix A
Incidentially, the rules above can be adapted to make your entire codebase more reliable:
Every if() statement ends in an "else" which generates a warning or error.
Every switch() statement ends in a "default:" which generates a warning or error.
The reason why this works well is that it forces you to think of every single case in advance, and not rely on (sometimes flawed) logic in the "else" part to execute the correct code.
I used this technique (among many others) to write a 30,000 line project that worked flawlessly from the day it was first deployed into production (that was 12 months ago).
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
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.
You could do this:
#if __WORDSIZE == 64
char *size = "64bits";
#else
char *size = "32bits";
#endif
Try this:
#ifdef _WIN64
// 64 bit code
#elif _WIN32
// 32 bit code
#else
if(sizeof(void*)==4)
// 32 bit code
else
// 64 bit code
#endif
Below code works fine for most current environments:
#if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__) ) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(__powerpc64__)
#define IS64BIT 1
#else
#define IS32BIT 1
#endif
"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.
Your approach was not too far off, but you are only checking whether long and int are of the same size. Theoretically, they could both be 64 bits, in which case your check would fail, assuming both to be 32 bits. Here is a check that actually checks the size of the types themselves, not their relative size:
#if ((UINT_MAX) == 0xffffffffu)
#define INT_IS32BIT
#else
#define INT_IS64BIT
#endif
#if ((ULONG_MAX) == 0xfffffffful)
#define LONG_IS32BIT
#else
#define LONG_IS64BIT
#endif
In principle, you can do this for any type for which you have a system defined macro with the maximal value.
Note, that the standard requires long long to be at least 64 bits even on 32 bit systems.
People already suggested methods that will try to determine if the program is being compiled in 32-bit or 64-bit.
And I want to add that you can use the c++11 feature static_assert to make sure that the architecture is what you think it is ("to relax").
So in the place where you define the macros:
#if ...
# define IS32BIT
static_assert(sizeof(void *) == 4, "Error: The Arch is not what I think it is")
#elif ...
# define IS64BIT
static_assert(sizeof(void *) == 8, "Error: The Arch is not what I think it is")
#else
# error "Cannot determine the Arch"
#endif
Borrowing from Contango's excellent answer above and combining it with "Better Macros, Better Flags" from Fluent C++, you can do:
// Macro for checking bitness (safer macros borrowed from
// https://www.fluentcpp.com/2019/05/28/better-macros-better-flags/)
#define MYPROJ_IS_BITNESS( X ) MYPROJ_IS_BITNESS_PRIVATE_DEFINITION_##X()
// Bitness checks borrowed from https://stackoverflow.com/a/12338526/201787
#if _WIN64 || ( __GNUC__ && __x86_64__ )
# define MYPROJ_IS_BITNESS_PRIVATE_DEFINITION_64() 1
# define MYPROJ_IS_BITNESS_PRIVATE_DEFINITION_32() 0
# define MYPROJ_IF_64_BIT_ELSE( x64, x86 ) (x64)
static_assert( sizeof( void* ) == 8, "Pointer size is unexpected for this bitness" );
#elif _WIN32 || __GNUC__
# define MYPROJ_IS_BITNESS_PRIVATE_DEFINITION_64() 0
# define MYPROJ_IS_BITNESS_PRIVATE_DEFINITION_32() 1
# define MYPROJ_IF_64_BIT_ELSE( x64, x86 ) (x86)
static_assert( sizeof( void* ) == 4, "Pointer size is unexpected for this bitness" );
#else
# error "Unknown bitness!"
#endif
Then you can use it like:
#if MYPROJ_IS_BITNESS( 64 )
DoMy64BitOperation()
#else
DoMy32BitOperation()
#endif
Or using the extra macro I added:
MYPROJ_IF_64_BIT_ELSE( DoMy64BitOperation(), DoMy32BitOperation() );
Here are a few more ways to do what you want in modern C++.
You can create a variable that defines the number of system bits:
static constexpr size_t sysbits = (CHAR_BIT * sizeof(void*));
And then in C++17 you can do something like:
void DoMy64BitOperation() {
std::cout << "64-bit!\n";
}
void DoMy32BitOperation() {
std::cout << "32-bit!\n";
}
inline void DoMySysBitOperation()
{
if constexpr(sysbits == 32)
DoMy32BitOperation();
else if constexpr(sysbits == 64)
DoMy64BitOperation();
/*else - other systems. */
}
Or in C++20:
template<void* = nullptr>
// template<int = 32> // May be clearer, pick whatever you like.
void DoMySysBitOperation()
requires(sysbits == 32)
{
std::cout << "32-bit!\n";
}
template<void* = nullptr>
// template<int = 64>
void DoMySysBitOperation()
requires(sysbits == 64)
{
std::cout << "64-bit!\n";
}
template<void* = nullptr>
void DoMySysBitOperation()
/* requires(sysbits == OtherSystem) */
{
std::cout << "Unknown System!\n";
}
The template<...> is usually not needed, but since those functions will have the same mangling name, we must enforce the compiler to pick the correct ones. Also, template<void* = nullptr> may be confusing ( The other template may be better and more logically correct ), I only used it as a workaround to satisfy the compiler name mangling.
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.
I'd place 32-bit and 64-bit sources in different files and then select appropriate source files using the build system.
I'm adding this answer as a use case and complete example for the runtime-check described in another answer.
This is the approach I've been taking for conveying to the end-user whether the program was compiled as 64-bit or 32-bit (or other, for that matter):
version.h
#ifndef MY_VERSION
#define MY_VERSION
#include <string>
const std::string version = "0.09";
const std::string arch = (std::to_string(sizeof(void*) * 8) + "-bit");
#endif
test.cc
#include <iostream>
#include "version.h"
int main()
{
std::cerr << "My App v" << version << " [" << arch << "]" << std::endl;
}
Compile and Test
g++ -g test.cc
./a.out
My App v0.09 [64-bit]