For example, let's say I have a function that will swap bytes in a 32 bit value for you:
uint32_t byte_swap(uint32_t in);
Well it seems silly to push that 32-bit value onto the stack and pop it off again, especially if we're going to be calling this function a lot, so let's pass it in through ECX:
#if __FASTCALL_SUPPORTED_ /* Whatever this may be */
#define FASTCALL __attribute__((fastcall))
#else
#define FASTCALL
#endif
uint32_t FASTCALL byte_swap(uint32_t in);
Now my question is, is it safe to compile that function into a shared library for distribution? If the user uses a different compiler to compile their program and links against this, will the function still be called properly?
__attribute__((fastcall)) is a gcc extension; as such, it may not be usable if the caller is not using gcc as well. Moreover, in the sample you gave, if __FASTCALL_SUPPORTED_ is not defined, you'll end up with a call with the wrong calling convention - bad idea.
One way to deal with this may be using a fallback wrapper. In your .c file:
#include "foo.h"
uint32_t FASTCALL byte_swap(uint32_t in) {
/* code ... */
}
uint32_t byte_swap__slowcall(uint32_t in) {
return byte_swap(in);
}
And in your .h file:
#if __FASTCALL_SUPPORTED_ /* Whatever this may be */
#define FASTCALL __attribute__((fastcall))
#else
#define FASTCALL
#define byte_swap byte_swap__slowcall
#endif
uint32_t FASTCALL byte_swap(uint32_t in);
Also, note that on Linux, a fast byteswap implementation is available in <byteswap.h> as bswap_32. On x86 machines, it will compile down to inline assembler, and a single instruction on high enough -march= settings.
Related
Motivating background info: I maintain a C++ library, and I spent way too much time this weekend tracking down a mysterious memory-corruption problem in an application that links to this library. The problem eventually turned out to be caused by the fact that the C++ library was built with a particular -DBLAH_BLAH compiler-flag, while the application's code was being compiled without that -DBLAH_BLAH flag, and that led to the library-code and the application-code interpreting the classes declared in the library's header-files differently in terms of data-layout. That is: sizeof(ThisOneParticularClass) would return a different value when invoked from a .cpp file in the application than it would when invoked from a .cpp file in the library.
So far, so unfortunate -- I have addressed the immediate problem by making sure that the library and application are both built using the same preprocessor-flags, and I also modified the library so that the presence or absence of the -DBLAH_BLAH flag won't affect the sizeof() its exported classes... but I feel like that wasn't really enough to address the more general problem of a library being compiled with different preprocessor-flags than the application that uses that library. Ideally I'd like to find a mechanism that would catch that sort of problem at compile-time, rather than allowing it to silently invoke undefined behavior at runtime. Is there a good technique for doing that? (All I can think of is to auto-generate a header file with #ifdef/#ifndef tests for the application code to #include, that would deliberately #error out if the necessary #defines aren't set, or perhaps would automatically-set the appropriate #defines right there... but that feels a lot like reinventing automake and similar, which seems like potentially opening a big can of worms)
One way of implementing such a check is to provide definition/declaration pairs for global variables that change, according to whether or not particular macros/tokens are defined. Doing so will cause a linker error if a declaration in a header, when included by a client source, does not match that used when building the library.
As a brief illustration, consider the following section, to be added to the "MyLibrary.h" header file (included both when building the library and when using it):
#ifdef FOOFLAG
extern int fooflag;
static inline int foocheck = fooflag; // Forces a reference to the above external
#else
extern int nofooflag;
static inline int foocheck = nofooflag; // <ditto>
#endif
Then, in your library, add the following code, either in a separate ".cpp" module, or in an existing one:
#include "MyLibrary.h"
#ifdef FOOFLAG
int fooflag = 42;
#else
int nofooflag = 42;
#endif
This will (or should) ensure that all component source files for the executable are compiled using the same "state" for the FOOFLAG token. I haven't actually tested this when linking to an object library, but it works when building an EXE file from two separate sources: it will only build if both or neither have the -DFOOFLAG option; if one has but the other doesn't, then the linker fails with (in Visual Studio/MSVC):
error LNK2001: unresolved external symbol "int fooflag"
(?fooflag##3HA)
The main problem with this is that the error message isn't especially helpful (to a third-party user of your library); that can be ameliorated (perhaps) by appropriate use of names for those check variables.1
An advantage is that the system is easily extensible: as many such check variables as required can be added (one for each critical macro token), and the same idea can also be used to check for actual values of said macros, with code like the following:
#if FOOFLAG == 1
int fooflag1 = 42;
#elif FOOFLAG == 2
int fooflag2 = 42;
#elif FOOFLAG == 3
int fooflag3 = 42;
#else
int fooflagX = 42;
#endif
1 For example, something along these lines (with suitable modifications in the header file):
#ifdef FOOFLAG
int CANT_DEFINE_FOOFLAG = 42;
#else
int MUST_DEFINE_FOOFLAG = 42;
#endif
Important Note: I have just tried this technique using the clang-cl compiler (in Visual Studio 2019) and the linker failed to catch a mismatch, because it is completely optimizing away all references to the foocheck variable (and, thus, to the dependent fooflag). However, there is a fairly trivial workaround, using clang's __attribute__((used)) directive (which also works for the GCC C++ compiler). Here is the header section for the last code snippet shown, with that workaround added:
#if defined(__clang__) || defined(__GNUC__)
#define KEEPIT __attribute__((used))
// Equivalent directives may be available for other compilers ...
#else
#define KEEPIT
#endif
#ifdef FOOFLAG
extern int CANT_DEFINE_FOOFLAG;
KEEPIT static inline int foocheck = CANT_DEFINE_FOOFLAG; // Forces reference to above
#else
extern int MUST_DEFINE_FOOFLAG;
KEEPIT static inline int foocheck = MUST_DEFINE_FOOFLAG; // <ditto>
#endif
In the Microsoft C++ frontend and linker, the #pragma detect_mismatch directive can be used in a very similar spirit as the solution presented in Adrian Mole's answer. Like that answer, mismatches are detected at link time, not at compilation time. It "places a record in an object. The linker checks these records for potential mismatches."
Say something like this is in a header file that is included in different compilation units:
#ifdef BLAH_BLAH
#pragma detect_mismatch("blah_blah_enabled", "true")
#else
#pragma detect_mismatch("blah_blah_enabled", "false")
#endif
Attempting to link object files with differing values of "blah_blah_enabled" will fail with LNK2038:
mismatch detected for 'name': value 'value_1' doesn't match value 'value_2' in filename.obj
Based on the mention of automake in the question, I assume that the asker isn't using the Microsoft C++ toolchain. I'm posting this here in case it helps someone in a similar situation who is using that toolchain.
I believe the closest MSVC analogue to the __attribute__((used)) in Adrian Mole's answer is the /INCLUDE:symbol-name linker option, which can be injected from a compilation unit via #pragma comment(linker, "/include:symbol-name").
As an alternative to #adrian's (excellent) answer, here's a suggestion for a runtime check which might be of interest.
For the sake of example, let's assume there are two flags, FOO1 and FOO2. First of all, for my scheme to work, and since the OP seems to be using #ifdef rather than #if, the library needs to provide a header file that looks like this (header guards omitted for clarity):
// MyLibrary_config_check.h
#ifdef FOO1
#define FOO1_VAL 1
#else
#define FOO1_VAL 0
#endif
#ifdef FOO2
#define FOO2_VAL 1
#else
#define FOO2_VAL 0
#endif
... etc ...
Then, the same header file declares the following function:
bool CheckMyLibraryConfig (int expected_flag1, int expected_flag2 /* , ... */);
The library then implements this like so:
bool CheckMyLibraryConfig (int expected_flag1, int expected_flag2 /* , ... */)
{
static const int configured_flag1 = FOO1_VAL;
static const int configured_flag2 = FOO2_VAL;
// ...
if (expected_flag1 != configured_flag1)
return false;
if (expected_flag2 != configured_flag2)
return false;
// ...
return true;
}
And the consumer of the library can then do:
if (!CheckMyLibraryConfig (FOO1_VAL, FOO2_VAL /* , ... */))
halt_and_catch_fire ();
On the downside, it's a runtime check, and that's not what was asked for. On the upside, CheckMyLibraryConfig could instead be implemented something like this:
std::string CheckMyLibraryConfig (int expected_flag1, int expected_flag2 /* , ... */)
{
if (expected_flag1 != configured_flag1)
return std::string ("Expected value of FOO1 does not match configured value, expected: ") + std::to_string (expected_flag1) + ", configured: " + std::to_string (expected_flag2);
...
return "";
}
And the consumer can then check for and display any non-empty string returned. Get as fancy as you like (that code could certainly be factored better) and check all the flags before returning a string reporting all the mis-matches, go crazy.
So, a vendor that we use has provided a library (primarily for C, with some C++ support) that does the following:
#ifndef int64_t
#define int64_t s_int64
#endif
#ifndef int32_t
#define int32_t s_int32
#endif
#ifndef int16_t
#define int16_t s_int16
#endif
#ifndef int8_t
#define int8_t s_int8
#endif
In one of their headers deep inside their library. Now the problem is that once their library is included in simple C++11 code such as:
#include <iostream>
#include <vendor/library.h>
int main(void)
{
std::int32_t std_i = 0;
return std_i;
}
There is immediately a compiler error, (s_int32 is not in std::). So question is, short of nagging the vendor to this fix this, is there anyway to workaround this in our code? (btw. things that I have tried, #include <cstdint> before their headers, no luck; extern "C" wrapper, no luck. The headers are installed in /usr/include/ so no control over order of inclusion I guess as well...)
You can undefine their definitions.
#undef int64_t
#undef int32_t
#undef int16_t
#undef int8_t
The preprocessor has a clever property: Recursion while replacing macros is prevented! Hence you can define them this way:
#define int64_t int64_t
#define int32_t int32_t
#define int16_t int16_t
#define int8_t int8_t
before including the vendor's header. That way they won't be redefined (as they are using #ifndef) and they keep their intended meaning for the rest of your code.
Without seeing what else is in the file it's tricky to find a solution that would work 100%.
However, one idea that you could try would be the following (note - I've tried this vaguelly in my compiler, but I make no guarantees that it'll work in any other one).
Create a file containing their defines, but as typedefs:
typedef s_int64 int64_t;
...
Then some compilers allow you to specify a file to include before any others on the command line... for example, gcc has the -include switch, so you specify to include this file.
Then, also on your command line, you can make defines that map to themselves:
-Dint64_t=int64_t
or even put the defines in the above file:
#define int64_t int64_t
The result is, in theory, the #defines won't interfere with any other code (they shouldn't do anything - tho' I'm not sure if you could end up with recursive expansion?). And as you've created a typedef to the correct type, the vendors code should still work.
The reason you put the typedefs into a file that's force-included, is that you want to ensure it gets included before anything else.
Then you should badger the vendor to update their code to use typedef. You're a customer, they want to sell you a product.
All theory mind.
they are using #ifndef macro which means : if not defined, then the value is ...
Just define it first with your values :
#define int64_t (xx)
#define int32_t (xx)
#define int16_t (xx)
#define int8_t (xx)
I have a code that runs on an embedded system and it has to run really fast. I know C and macros, and this particular project is coded mostly in C but it also uses C++ templates [increasingly more]. There is an inline function:
inline my_t read_memory(uint32 addr) {
#if (CURRENT_STATE & OPTIMIZE_BITMAP)
return readOptimized(addr);
#else
return MEMORY[addr];
#endif
}
This function reads from memory in an optimized fashion or conventional fashion based on the current state and a bitmap that tells whether to use optimization in a specific state or not.
#define STATE_A 0x0001
#define STATE_B 0x0010
#define STATE_C 0x0100
#define STATE_D 0x1000
#define OPTIMIZE_BITMAP 0x1010 // optimize states d and b
and during the course of execution (ok, compilation), I tried to redefine CURRENT_STATE like so:
int main(){
#define CURRENT_STATE STATE_A
do_a();
#undef CURRENT_STATE
#define CURRENT_STATE STATE_B
do_b();
....
}
All do_X() functions make read_memory() calls. I could not make this approach work. The value of current state is always STATE_A as I can see when I use #warning statements. This is not my question, although if you can help me with this I'll be double happy. So, my question is, is there a way to do this kind of thing using templates instead of macros?
Some more info: I have to use an inline function because I cannot export MEMORY[] and this is a library function. I really prefer not to modify the function prototype (like read_memory()...) but it will do. Also, pardon my obscurity.
many thanks,
The inline function will be parsed once, at the point in the translation unit where it is declared, and the state of the macros at that point will be used. Calling the function multiple times with the macros defined differently will not change the definition of the function.
You can do this with a template though --- if you pass the "current state" as a template parameter then you can use a different instantiation at each call point:
template<unsigned state>
inline my_t read_memory(uint32 addr) {
if(state & OPTIMIZE_BITMAP)
return readOptimized(addr);
else
return MEMORY[addr];
}
int main(){
read_memory<STATE_A>(some_addr);
read_memory<STATE_B>(some_addr);
....
}
The compiler will realise that state & OPTIMIZE_BITMAP is a constant and optimize out one or other branch of the if for each template instantiation.
I think you may be misunderstanding what the compiler (or rather, the preprocessor) does with #defines.
Your example (quoted below) is not useful, because CURRENT_STATE is not used between the #define and the #undef. The preprocessor isn't 'executing' your code, or expanding do_a() inline at this point. #defines and macro expansion can only occur EXACTLY IN THE ORDER OF THE LINES IN YOUR SOURCE.
#define CURRENT_STATE STATE_A
do_a();
#undef CURRENT_STATE
Here's a preprocessor-based solution, if templates fill you with horror. Let's assume that do_a() should use the optimised version.
inline my_t read_memory(uint32 addr)
{
return MEMORY[addr];
}
inline my_t read_memory_optimized(uint32 addr)
{
return readOptimized(addr);
}
Now, create DO_CONFIG.H
#if defined(DO_A) || (defined(DO_C) || ...)
#define READ_MEMORY read_memory_optimized
#else
#define READ_MEMORY read_memory
In DO_A.C, add this at the top
#define DO_A
#include DO_CONFIG.H
...and use x=READ_MEMORY(addr) instead of x=read_memory(addr). To switch from optimised to non-optimised, just change DO_CONFIG.H
I've a very basic class, name it Basic, used in nearly all other files in a bigger project. In some cases, there needs to be debug output, but in release mode, this should not be enabled and be a NOOP.
Currently there is a define in the header, which switches a makro on or off, depending on the setting. So this is definetely a NOOP, when switched off. I'm wondering, if I have the following code, if a compiler (MSVS / gcc) is able to optimize out the function call, so that it is again a NOOP. (By doing that, the switch could be in the .cpp and switching will be much faster, compile/link time wise).
--Header--
void printDebug(const Basic* p);
class Basic {
Basic() {
simpleSetupCode;
// this should be a NOOP in release,
// but constructor could be inlined
printDebug(this);
}
};
--Source--
// PRINT_DEBUG defined somewhere else or here
#if PRINT_DEBUG
void printDebug(const Basic* p) {
// Lengthy debug print
}
#else
void printDebug(const Basic* p) {}
#endif
As with all questions like this, the answer is - if it really matters to you, try the approach and examine the emitted assembly language.
Compiler possibly may optimize this code, if it knows printDebug function implementation at compilation time. If printDebug is in another object module, this possibly may be optimized only by linker, using the whole program optimization. But the only way to test this is to read compiler-generated Assembly code.
If you already have PRINT_DEBUG macro, you can extend it by the way as TRACE is defined:
#define PRINT_DEBUG // optional
#ifdef PRINT_DEBUG
#define PRINT_DEBUG_CALL(p) printDebug(p)
#else
#define PRINT_DEBUG_CALL(p)
#endif
void printDebug(const Basic* p);
class Basic {
Basic() {
simpleSetupCode;
// this should be a NOOP in release,
// but constructor could be inlined
PRINT_DEBUG_CALL(this);
}
};
--Source--
// PRINT_DEBUG defined somewhere else or here
#if PRINT_DEBUG
void printDebug(const Basic* p) {
// Lengthy debug print
}
#endif
#if PRINT_DEBUG
#define printDebug _real_print_debug
#else
#define printDebug(...)
#endif
This way the preprocessor will strip all debug code before it even gets to the compiler.
Currently most of the optimizations are done at compile time. Some compilers as LLVM are able to optimize at link time. This is a really interesting idea. I suggest you to take a look at.
Waiting for these kind of optimization, what you can do is the following. Define a macro that let you include the following statement depending on whether DEBUG is defined or not.
#ifdef DEBUG
#define IF_DEBUG (false) {} else
#else
#define IF_DEBUG
#endif
You can the use it like this
Basic() {
simpleSetupCode;
// this should be a NOOP in release,
// but constructor could be inlined
IF_DEBUG printDebug(this);
}
which is already much more readable than
Basic() {
simpleSetupCode;
// this should be a NOOP in release,
// but constructor could be inlined
#if DEBUG
printDebug(this);
#endif
}
Note that you can use it as if it was a keyword
IF_DEBUG {
printDebug(this);
printDebug(thas);
}
errm, why not use the pre-processor macro differently?
Just of the top of my head, something like:
#define DEBUG_TRACE(p)
#ifdef PRINT_DEBUG
printDebug(p);
#else
;
#endif
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]