Prise en charge des types d'entiers C99 dans Visual C++ et autres compilateurs

Cet article présente une solution mise à jour pour intégrer les types d'entiers standard C99 (stdint.h et inttypes.h) dans des environnements de compilation qui ne les supportent pas nativement, tels que Visual C++ (versions 6 à 2012), Borland C++ Builder (BCB) et GCC. La version 1.01 de cette solution résout les problèmes liés à l'inclusion des fichiers d'en-tête.

Problèmes de chemins d'inclusion et Renommage des fichiers

La version précédente utilisait les noms de fichiers stdint.h et inttypes.h, nécessitant une inclusion via #include "filename.h" pour distinguer les fichiers personnalisés des fichiers système. Cette approche présentait un inconvénient majeur : les fichiers personnalisés ne pouvaient pas être placés dans le répertoire d'inclusion du projet. S'ils y étaient placés, l'inclusion via #include <filename.h> aurait pu privilégier les fichiers du projet, entraînant des références circulaires et empêchant l'utilisation des fichiers système corrects.

Pour pallier ce problème, les fichiers ont été renommés en auto_stdint.h et auto_inttypes.h. Cela permet de les placer dans le répertoire d'inclusion standard du projet sans conflits. L'inconvénient est que l'inclusion se fait maintenant via #include "auto_stdint.h" et #include "auto_inttypes.h".

Les macros de préprocesseur ont également été renommées pour refléter ce changement :

  • __AUTO_STDINT_H_INCLUDED (anciennement _STDINT_H_ALL_)
  • __AUTO_STDINT_H_USESYS (anciennement _STDINT_H_SYS_)
  • __AUTO_INTTYPES_H_INCLUDED (anciennement _INTTYPES_H_ALL_)
  • __AUTO_INTTYPES_H_USESYS (anciennement _INTTYPES_H_SYS_)

Compatibilité avec les compilateurs

La compatibilité a été testée et vérifiée pour plusieurs versions de compilateurs :

  • Visual C++ 2008 : Ne supporte pas stdint.h ni inttypes.h.
  • Visual C++ 2012 : Spuporte stdint.h mais pas inttypes.h.

Les autres compilateurs testés incluent BCB6, GCC (Linux, Mac OS X, MinGW) qui supportent nativement ces standards.

Fichiers inclus dans la solution

La distribution de cette solution comprend les fichiers suivants :

  • auto_stdint.h : Implémentation personnalisée de stdint.h.
  • auto_inttypes.h : Implémentation personnalisée de inttypes.h.
  • c99int.c : Un fichier source d'exemple pour tester les types C99.
  • Divers fichiers de projet pour Visual Studio (.sln, .vcproj, .vcxproj) et Borland C++ Builder (.bpf, .bpr).
  • Un makefile pour la compilation avec GCC.

Contenu des fichiers d'en-tête personnalisés

auto_stdint.h

Ce fichier fournit les définitions standard pour les types entiers de largeur fixe, de largeur minimale, de largeur la plus rapide, ainsi que les types capables de contenir des pointeurs d'objets et les types de plus grande largeur. Il inclut également les macros correspondantes pour les limites de ces types.

Les directives de préprocesseur déterminent si le compilateur dispose déjà d'une implémentation de stdint.h. Si c'est le cas (par exemple, avec GCC ou des versions récentes de MSVC), le fichier système est inclus. Sinon, l'implémentation personnalisée est utilisée.


////////////////////////////////////////////////////////////
/*
auto_stdint.h: Compatible C99 stdint.h
Author: zyl910
Version: V1.01
Update: 2013-01-10
*/
////////////////////////////////////////////////////////////

#ifndef __AUTO_STDINT_H_INCLUDED
#define __AUTO_STDINT_H_INCLUDED

#undef __AUTO_STDINT_H_USESYS
#if defined(__GNUC__)
    #define __AUTO_STDINT_H_USESYS
#elif defined(_MSC_VER) && _MSC_VER >= 1600 // VC2010 and later
    #define __AUTO_STDINT_H_USESYS
#elif defined(__BORLANDC__) && __BORLANDC__ >= 0x0560 // BCB6
    #define __AUTO_STDINT_H_USESYS
#endif

#ifdef __AUTO_STDINT_H_USESYS
#include <stdint.h>
#else
#include <limits.h>
#include <wchar.h> // For WCHAR_MIN/MAX definitions

#ifndef _MSC_STDINT_H_
#define _MSC_STDINT_H_

#ifndef _W64
#  if !defined(__midl) && (defined(_X86_) || defined(_M_IX86)) && _MSC_VER >= 1300
#     define _W64 __w64
#  else
#     define _W64
#  endif
#endif

typedef signed __int8     int8_t;
typedef signed __int16    int16_t;
typedef signed __int32    int32_t;
typedef unsigned __int8   uint8_t;
typedef unsigned __int16  uint16_t;
typedef unsigned __int32  uint32_t;
typedef signed __int64    int64_t;
typedef unsigned __int64  uint64_t;

typedef int8_t    int_least8_t;
typedef int16_t   int_least16_t;
typedef int32_t   int_least32_t;
typedef int64_t   int_least64_t;
typedef uint8_t   uint_least8_t;
typedef uint16_t  uint_least16_t;
typedef uint32_t  uint_least32_t;
typedef uint64_t  uint_least64_t;

typedef int8_t    int_fast8_t;
typedef int16_t   int_fast16_t;
typedef int32_t   int_fast32_t;
typedef int64_t   int_fast64_t;
typedef uint8_t   uint_fast8_t;
typedef uint16_t  uint_fast16_t;
typedef uint32_t  uint_fast32_t;
typedef uint64_t  uint_fast64_t;

#ifdef _WIN64
   typedef signed __int64    intptr_t;
   typedef unsigned __int64  uintptr_t;
#else
   typedef _W64 signed int   intptr_t;
   typedef _W64 unsigned int uintptr_t;
#endif

typedef int64_t   intmax_t;
typedef uint64_t  uintmax_t;

#if !defined(__cplusplus) || defined(__STDC_LIMIT_MACROS)
// Limits definitions
#define INT8_MIN     ((int8_t)_I8_MIN)
#define INT8_MAX     _I8_MAX
// ... (other limit definitions) ...
#define UINTMAX_MAX  UINT64_MAX

#define PTRDIFF_MIN  INT32_MIN // Adjust for 64-bit
#define PTRDIFF_MAX  INT32_MAX // Adjust for 64-bit
#define SIG_ATOMIC_MIN  INT_MIN
#define SIG_ATOMIC_MAX  INT_MAX
#ifndef SIZE_MAX
#define SIZE_MAX     UINT32_MAX // Adjust for 64-bit
#endif
#ifndef WCHAR_MIN
#define WCHAR_MIN  0
#endif
#ifndef WCHAR_MAX
#define WCHAR_MAX  _UI16_MAX
#endif
#define WINT_MIN  0
#define WINT_MAX  _UI16_MAX
#endif

#if !defined(__cplusplus) || defined(__STDC_CONSTANT_MACROS)
// Constant macros definitions
#define INT8_C(val)  val##i8
#define INT16_C(val) val##i16
#define INT32_C(val) val##i32
#define INT64_C(val) val##i64
#define UINT8_C(val)  val##ui8
#define UINT16_C(val) val##ui16
#define UINT32_C(val) val##ui32
#define UINT64_C(val) val##ui64
#define INTMAX_C   INT64_C
#define UINTMAX_C  UINT64_C
#endif

#endif // _MSC_STDINT_H_
#endif // __AUTO_STDINT_H_USESYS
#endif // __AUTO_STDINT_H_INCLUDED
</wchar.h></limits.h></stdint.h>

auto_inttypes.h

Ce fichier fournit les macros de spécification de format pour printf et scanf, ainsi que les fonctions utilitaires pour les types d'entiers de plus grande largeur, telles que imaxabs, imaxdiv, strtoimax et wcstoimax.

Comme pour auto_stdint.h, il vérifie d'abord si le compilateur fournit nativement inttypes.h. Sinon, il utilise une implémentation personnalisée.


////////////////////////////////////////////////////////////
/*
auto_inttypes.h: Compatible C99 inttypes.h
Author: zyl910
Version: V1.01
Update: 2013-01-10
*/
////////////////////////////////////////////////////////////

#ifndef __AUTO_INTTYPES_H_INCLUDED
#define __AUTO_INTTYPES_H_INCLUDED

#undef __AUTO_INTTYPES_H_USESYS
#if defined(__GNUC__)
    #define __AUTO_INTTYPES_H_USESYS
#elif defined(_MSC_VER) // VC2012 does not support inttypes.h
#elif defined(__BORLANDC__) // BCB6 does not support inttypes.h
#else
    #define __AUTO_INTTYPES_H_USESYS // Assume other compilers support C99
#endif

#ifdef __AUTO_INTTYPES_H_USESYS
#include <inttypes.h>
#else
#include "auto_stdint.h" // Include our custom stdint.h

#ifndef _MSC_INTTYPES_H_
#define _MSC_INTTYPES_H_

// Format specifier macros
#if !defined(__cplusplus) || defined(__STDC_FORMAT_MACROS)
// Signed integers
#define PRId8       "d"
#define PRIi8       "i"
// ... (other PRI macros for various widths) ...
#define PRId64       "I64d"
#define PRIi64       "I64i"
#define PRIdMAX     "I64d"
#define PRIiMAX     "I64i"
#define PRIdPTR     "Id"
#define PRIiPTR     "Ii"

// Unsigned integers
#define PRIo8       "o"
#define PRIu8       "u"
// ... (other PRI macros for various widths) ...
#define PRIo64       "I64o"
#define PRIu64       "I64u"
#define PRIoMAX     "I64o"
#define PRIuMAX     "I64u"
#define PRIoPTR     "Io"
#define PRIuPTR     "Iu"

// Scanf format specifier macros
#define SCNd8       "d"
#define SCNi8       "i"
// ... (other SCN macros for various widths) ...
#define SCNd64       "I64d"
#define SCNi64       "I64i"
#define SCNdMAX     "I64d"
#define SCNiMAX     "I64i"
#ifdef _WIN64
#  define SCNdPTR     "I64d"
#  define SCNiPTR     "I64i"
#else
#  define SCNdPTR     "ld"
#  define SCNiPTR     "li"
#endif
// ... (SCN macros for unsigned integers) ...
#endif // __STDC_FORMAT_MACROS

// Functions for greatest-width integer types
typedef struct {
   intmax_t quot;
   intmax_t rem;
} imaxdiv_t;

#define imaxabs _abs64 // For MSVC

// imaxdiv function implementation for MSVC
#ifdef _MSC_VER
// ... (imaxdiv implementation) ...
#endif

#define strtoimax _strtoi64 // For MSVC
#define strtoumax _strtoui64 // For MSVC
#define wcstoimax _wcstoi64 // For MSVC
#define wcstoumax _wcstoui64 // For MSVC

#endif // _MSC_INTTYPES_H_
#endif // __AUTO_INTTYPES_H_USESYS
#endif // __AUTO_INTTYPES_H_INCLUDED

Exemple de compilation et de test

Le fichier c99int.c contient un exemple simple qui utilise ces types et macros pour afficher des valeurs. Le makefile fourni permet de compiler ce code avec GCC.

Les tests de compilation ont été effectués avec succès sur une variété de compilateurs et d'architectures, y compris différentes versions de Visual Studio (32 bits et 64 bits), BCB6, et GCC.

La solution garantit la compatibilité avec les types d'entiers C99, améliorant ainsi la portabilité du code C entre différents compilateurs.

Étiquettes: C99 stdint.h inttypes.h Visual C++ GCC

Publié le 23 juillet à 22h44