web-dev-qa-db-fra.com

Détermination de 32 vs 64 bits en C++

Je cherche un moyen de déterminer de manière fiable si le code C++ est compilé en version 32 vs 64 bits. Nous avons proposé ce que nous pensons être une solution raisonnable en utilisant des macros, mais nous étions curieux de savoir si les gens pouvaient penser aux cas où cela pourrait échouer ou s'il existe une meilleure façon de le faire. Veuillez noter que nous essayons de le faire dans un environnement de compilateur multiplate-forme et multiplate-forme.

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

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

Merci.

119
Joe Corkery

Malheureusement, il n’existe pas de macro multiplate-forme définissant 32/64 bits entre les principaux compilateurs. J'ai trouvé le moyen le plus efficace de procéder est le suivant. 

Je choisis d'abord ma propre représentation. Je préfère ENVIRONMENT64/ENVIRONMENT32. Ensuite, je découvre ce que tous les principaux compilateurs utilisent pour déterminer s’il s’agit ou non d’un environnement 64 bits et l’utilise pour définir mes 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

Une autre solution plus simple consiste à définir simplement ces variables à partir de la ligne de commande du compilateur. 

92
JaredPar
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;
}
94

Malheureusement, dans un environnement multi-plateformes et multi-compilateur, il n’existe pas de méthode fiable permettant de le faire uniquement au moment de la compilation.

  • _WIN32 et _WIN64 peuvent parfois être les deux non définis, si les paramètres du projet sont erronés ou corrompus (en particulier dans Visual Studio 2008 SP1).
  • Un projet intitulé "Win32" peut être défini sur 64 bits en raison d'une erreur de configuration du projet.
  • Selon Visual Studio 2008 SP1, intellisense ne grise pas les bonnes parties du code, selon le #define en cours. Cela rend difficile de savoir exactement quelle #define est utilisée au moment de la compilation.

Par conséquent, la méthode only fiable consiste à combiner 3 contrôles simples:

  • 1) Réglage du temps de compilation, et;
  • 2) vérification de l'exécution, et;
  • 3) Vérification robuste du temps de compilation.

Contrôle simple 1/3: Réglage du temps de compilation

Choisissez n'importe quelle méthode pour définir la variable #define requise. Je suggère la méthode de @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

Contrôle simple 2/3: Contrôle d'exécution

Dans main (), vérifiez si sizeof () est logique:

#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

Vérification simple 3/3: vérification robuste du temps de compilation

La règle générale est "chaque #define doit se terminer par un #else qui génère une erreur".

#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

Mise à jour 2017-01-17

Commentaire de @AI.G

4 ans plus tard (je ne sais pas si c'était possible auparavant), vous pouvez convertir la vérification à l'exécution lors de la compilation en utilisant l'assert statique: static_assert (sizeof (void *) == 4) ;. Maintenant tout est fait au moment de la compilation :)

Annexe A

Incidemment, les règles ci-dessus peuvent être adaptées pour rendre votre base de code plus fiable:

  • Chaque instruction if () se termine par un "else" qui génère un avertissement ou une erreur.
  • Chaque instruction switch () se termine par un "default:" qui génère un avertissement ou une erreur.

La raison pour laquelle cela fonctionne bien est que cela vous oblige à penser à chaque cas à l’avance et à ne pas vous fier à la logique (parfois imparfaite) de la partie "sinon" pour exécuter le code correct.

J'ai utilisé cette technique (parmi beaucoup d'autres) pour écrire un projet de 30 000 lignes qui a fonctionné sans faille depuis le jour où il a été déployé pour la première fois en production (c'était il y a 12 mois).

40
Contango

Vous devriez pouvoir utiliser les macros définies dans stdint.h . INTPTR_MAX est en particulier exactement la valeur dont vous avez besoin.

#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

Certaines versions (toutes?) Du compilateur de Microsoft ne viennent pas avec stdint.h. Vous ne savez pas pourquoi, car c'est un fichier standard. Voici une version que vous pouvez utiliser:  http://msinttypes.googlecode.com/svn/trunk/stdint.h

24
alex tingle

Cela ne fonctionnera pas sous Windows pour un début. Les longueurs et les bits sont en 32 bits, que vous compiliez pour des fenêtres 32 bits ou 64 bits. Je pense que vérifier si la taille d'un pointeur est de 8 octets est probablement un itinéraire plus fiable.

15
mattnewport

Vous pourriez faire ceci:

#if __WORDSIZE == 64
char *size = "64bits";
#else
char *size = "32bits";
#endif
8
Anoop
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
6
emj8321

"Compilé en 64 bits" n'est pas bien défini en C++.

C++ ne définit que des limites inférieures pour les tailles telles que int, long et void *. Il n'y a aucune garantie que int soit en 64 bits même lorsqu'il est compilé pour une plateforme 64 bits. Le modèle permet par exemple de 23 bits ints et sizeof(int *) != sizeof(char *) 

Il existe différents modèles de programmation pour les plates-formes 64 bits. 

Votre meilleur pari est un test spécifique à la plate-forme. Votre deuxième choix, la décision portable doit être plus spécifique en quoi est de 64 bits.

4
peterchen

Des personnes ont déjà suggéré des méthodes pour tenter de déterminer si le programme est en cours de compilation en 32-bit ou 64-bit.

Et je tiens à ajouter que vous pouvez utiliser la fonctionnalité c ++ 11 static_assert pour vous assurer que l’architecture correspond à ce que vous pensez ("se détendre").

Donc, à l'endroit où vous définissez les 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
3
Ameen

Votre approche n’est pas très éloignée, mais vous ne faites que vérifier si long et int sont de la même taille. Théoriquement, ils pourraient tous deux être de 64 bits, auquel cas votre vérification échouerait, en supposant que les deux soient de 32 bits. Voici une vérification qui vérifie réellement la taille des types eux-mêmes, pas leur taille relative:

#if ((UINT_MAX) == 0xffffffffu)
    #define INT_IS32BIT
#else
    #define INT_IS64BIT
#endif
#if ((ULONG_MAX) == 0xfffffffful)
    #define LONG_IS32BIT
#else
    #define LONG_IS64BIT
#endif

En principe, vous pouvez le faire pour tous les types pour lesquels vous avez une macro définie par le système avec la valeur maximale.

Notez que la norme exige que long long soit au moins 64 bits, même sur les systèmes 32 bits.

2
cmaster

Le code ci-dessous fonctionne bien pour la plupart des environnements actuels:

  #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
2
Alex Byrth

Si vous pouvez utiliser des configurations de projet dans tous vos environnements, cela facilitera la définition d'un symbole 64 et 32 ​​bits. Donc, vous auriez des configurations de projet comme ceci:

Débogage 32 bits
Version 32 bits
Débogage 64 bits
Version 64 bits

EDIT: Ce sont des configurations génériques, pas des configurations ciblées. Appelez-les comme vous voulez.

Si vous ne pouvez pas faire ça, j'aime bien l'idée de Jared.

1
Jon Seigel

Je placerais les sources 32 bits et 64 bits dans des fichiers différents, puis sélectionnerais les fichiers source appropriés à l'aide du système de construction.

1
big-z

J'ajoute cette réponse en tant que cas d'utilisation et complète l'exemple de la vérification d'exécution décrite dans une autre réponse .

C’est l’approche que j’ai choisie pour indiquer à l’utilisateur final si le programme a été compilé au format 64 bits ou 32 bits (ou autre, d’ailleurs):

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;
}

Compiler et tester

g++ -g test.cc
./a.out
My App v0.09 [64-bit]
0
vallismortis