Saya mencari cara untuk menentukan dengan andal apakah kode C ++ sedang dikompilasi dalam 32 vs 64 bit. Kami telah menemukan apa yang kami anggap sebagai solusi yang masuk akal menggunakan makro, tetapi ingin tahu apakah orang-orang dapat memikirkan kasus-kasus di mana hal ini mungkin gagal atau apakah ada cara yang lebih baik untuk melakukan ini. Harap dicatat bahwa kami mencoba melakukan ini dalam lingkungan kompilator lintas platform dan multipel.
#if ((ULONG_MAX) == (UINT_MAX))
# define IS32BIT
#else
# define IS64BIT
#endif
#ifdef IS64BIT
DoMy64BitOperation()
#else
DoMy32BitOperation()
#endif
Terima kasih.
c++
32bit-64bit
conditional-compilation
Joe Corkery
sumber
sumber
stdint.h
mungkin teman Anda, atau Anda mungkin perlu mengembangkan beberapa typedefs Anda sendiri.Jawaban:
Sayangnya tidak ada makro lintas platform yang mendefinisikan 32/64 bit di seluruh kompiler utama. Saya telah menemukan cara paling efektif untuk melakukan ini adalah sebagai berikut.
Pertama saya memilih representasi saya sendiri. Saya lebih suka ENVIRONMENT64 / ENVIRONMENT32. Kemudian saya mencari tahu apa yang digunakan semua kompiler utama untuk menentukan apakah itu lingkungan 64 bit atau tidak dan menggunakannya untuk mengatur variabel saya.
// 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
Rute lain yang lebih mudah adalah dengan mengatur variabel-variabel ini dari baris perintah kompilator.
sumber
#if _WIN32 || _WIN64
...#elif __GNUC__
...#else
# error "Missing feature-test macro for 32/64-bit on this compiler."
?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; }
sumber
size_t
cukup besar untuk menampung ukuran objek yang dialokasikan dalam sistem. Biasanya itulah yang ingin Anda ketahui saat kompilasi bersyarat. Jika bukan itu yang Anda inginkan, Anda dapat menggunakan cuplikan ini dengan beberapa jenis lain, bukansize_t
. Misalnya, bisa jadivoid*
.Sayangnya, dalam lingkungan lintas platform, lintas kompiler, tidak ada satu metode yang dapat diandalkan untuk melakukan ini murni pada waktu kompilasi.
Oleh karena itu, satu - satunya metode yang dapat diandalkan adalah menggabungkan 3 pemeriksaan sederhana :
Pemeriksaan sederhana 1/3: Kompilasi pengaturan waktu
Pilih metode apa pun untuk menyetel variabel #define yang diperlukan. Saya menyarankan metode dari @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
Pemeriksaan sederhana 2/3: Pemeriksaan waktu proses
Di main (), periksa kembali untuk melihat apakah sizeof () masuk akal:
#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
Pemeriksaan sederhana 3/3: Pemeriksaan waktu kompilasi yang kuat
Aturan umumnya adalah "setiap #define harus diakhiri dengan #else yang menghasilkan kesalahan".
#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
Perbarui 2017-01-17
Komentar dari
@AI.G
:Lampiran A
Secara tidak langsung, aturan di atas dapat disesuaikan untuk membuat seluruh basis kode Anda lebih andal:
Alasan mengapa ini bekerja dengan baik adalah karena ini memaksa Anda untuk memikirkan setiap kasus terlebih dahulu, dan tidak mengandalkan logika (terkadang cacat) di bagian "lain" untuk menjalankan kode yang benar.
Saya menggunakan teknik ini (di antara banyak lainnya) untuk menulis 30.000 proyek garis yang bekerja dengan sempurna sejak pertama kali digunakan dalam produksi (itu 12 bulan yang lalu).
sumber
sizeof(void*)
apakah itu diselesaikan pada waktu kompilasi atau run-time? jika pada waktu kompilasi maka pada saat run-time pemeriksaan akan selalu dilakukanif(8!=8){...}
.static_assert(sizeof(void*) == 4);
. Sekarang semuanya selesai pada waktu kompilasi :)static_assert(sizeof(void*) * CHAR_BIT == 32)
lebih ekspresif dan benar secara teknis (walaupun saya tidak tahu arsitektur apa pun di mana byte memiliki jumlah bit yang berbeda dari 8)Anda harus dapat menggunakan makro yang ditentukan di
stdint.h
. Secara khususINTPTR_MAX
adalah nilai yang Anda butuhkan.#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
Beberapa (semua?) Versi kompiler Microsoft tidak disertakan
stdint.h
. Tidak yakin mengapa, karena ini adalah file standar. Berikut versi yang dapat Anda gunakan:http://msinttypes.googlecode.com/svn/trunk/stdint.hsumber
<stdint.h>
dan<cstdint>
. Adapun keadaan saat ini - perpustakaan VC ++ berasal dari Dinkumware (masih - TR1 diambil dari sana juga), tetapi dari apa yang saya ingat pernah membaca di VCBlog, ia mengalami refactoring yang cukup signifikan untuk dikompilasi dengan bersih/clr
, bekerja dengan semua MSVC tipe non-standar seperti__int64
, dan sebagainya - itulah sebabnya tidak sesederhana hanya dengan mengambilnya dan memasukkannya ke versi kompiler berikutnya.Itu tidak akan berfungsi pada Windows untuk awalnya. Panjang dan int keduanya 32 bit apakah Anda sedang mengompilasi untuk jendela 32 bit atau 64 bit. Saya akan berpikir memeriksa apakah ukuran pointer 8 byte mungkin merupakan rute yang lebih dapat diandalkan.
sumber
sizeof(void*) == 8 ? Do64Bit() : Do32Bit();
. Itu masih bisa meninggalkan fungsi yang tidak terpakai dalam biner, tetapi ekspresi kemungkinan dikompilasi hanya untuk panggilan ke fungsi "benar".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;
Anda bisa melakukan ini:
#if __WORDSIZE == 64 char *size = "64bits"; #else char *size = "32bits"; #endif
sumber
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
sumber
"Dikompilasi dalam 64 bit" tidak didefinisikan dengan baik di C ++.
C ++ hanya menetapkan batas bawah untuk ukuran seperti int, long dan
void *
. Tidak ada jaminan bahwa int adalah 64 bit meskipun dikompilasi untuk platform 64 bit. Model ini memungkinkan misalnya 23 bitint
dansizeof(int *) != sizeof(char *)
Ada berbagai model pemrograman untuk platform 64 bit.
Taruhan terbaik Anda adalah tes khusus platform. Terbaik kedua, keputusan portabel Anda harus lebih spesifik dalam apa adalah 64 bit.
sumber
Pendekatan Anda tidak terlalu jauh, tetapi Anda hanya memeriksa apakah
long
danint
berukuran sama. Secara teoritis, keduanya bisa 64 bit, dalam hal ini pemeriksaan Anda akan gagal, dengan asumsi keduanya menjadi 32 bit. Berikut adalah pemeriksaan yang benar-benar memeriksa ukuran jenis itu sendiri, bukan ukuran relatifnya:#if ((UINT_MAX) == 0xffffffffu) #define INT_IS32BIT #else #define INT_IS64BIT #endif #if ((ULONG_MAX) == 0xfffffffful) #define LONG_IS32BIT #else #define LONG_IS64BIT #endif
Pada prinsipnya, Anda dapat melakukan ini untuk semua jenis yang Anda miliki makro yang ditentukan sistem dengan nilai maksimal.
Perhatikan, bahwa standar
long long
harus minimal 64 bit bahkan pada sistem 32 bit.sumber
#include <limits.h>
suatu tempat sebelum#if
pengujian Anda .Orang sudah menyarankan metode yang akan mencoba menentukan apakah program sedang dikompilasi di
32-bit
atau64-bit
.Dan saya ingin menambahkan bahwa Anda dapat menggunakan fitur c ++ 11
static_assert
untuk memastikan bahwa arsitekturnya seperti yang Anda pikirkan ("untuk bersantai").Jadi di tempat Anda menentukan makro:
#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
sumber
static_assert(sizeof(void*) * CHAR_BIT == 32)
lebih ekspresif dan benar secara teknis (walaupun saya tidak tahu arsitektur apa pun di mana byte memiliki jumlah bit yang berbeda dari 8)Kode di bawah ini berfungsi dengan baik untuk sebagian besar lingkungan saat ini:
#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
sumber
_WIN64
mengharuskan Anda telah disertakan<windows.h>
. Dengan Visual C ++, lebih baik menggunakan built-in mendefinisikan compiler:_M_IX86
,_M_X64
,_M_ARM
,_M_ARM64
, dll__ppc64__
,__powerpc64__
dan_ARCH_PPC64
. Itu menangkap AIX dan platform lainnya juga.Jika Anda dapat menggunakan konfigurasi proyek di semua lingkungan Anda, itu akan membuat definisi simbol 64- dan 32-bit menjadi mudah. Jadi Anda akan memiliki konfigurasi proyek seperti ini:
Debug
32-bit Rilis
32-bit Debug
64-bit Rilis 64-bit
EDIT: Ini adalah konfigurasi umum, bukan konfigurasi target. Panggil mereka apapun yang kamu mau.
Jika Anda tidak bisa melakukan itu, saya suka ide Jared.
sumber
Saya akan menempatkan sumber 32-bit dan 64-bit di file yang berbeda dan kemudian memilih file sumber yang sesuai menggunakan sistem build.
sumber
-DBUILD_64BIT
. Seringkali, hal-hal tertentu sangat mirip dengan 32 dan 64 bit sehingga menyimpannya dalam file yang sama bisa sangat praktis.Meminjam dari jawaban bagus Contango di atas dan menggabungkannya dengan " Makro Lebih Baik, Bendera Lebih Baik " dari Fluent C ++, Anda dapat melakukan:
// 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
Kemudian Anda bisa menggunakannya seperti:
#if MYPROJ_IS_BITNESS( 64 ) DoMy64BitOperation() #else DoMy32BitOperation() #endif
Atau menggunakan makro ekstra yang saya tambahkan:
sumber
Saya menambahkan jawaban ini sebagai kasus penggunaan dan contoh lengkap untuk pemeriksaan runtime yang dijelaskan dalam jawaban lain .
Ini adalah pendekatan yang saya ambil untuk menyampaikan kepada pengguna akhir apakah program dikompilasi sebagai 64-bit atau 32-bit (atau lainnya, dalam hal ini):
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; }
Kompilasi dan Uji
g++ -g test.cc ./a.out My App v0.09 [64-bit]
sumber