web-dev-qa-db-fra.com

Nettoyer le code pour printf size_t en C ++ (ou: équivalent le plus proche de% z de C99 en C ++)

J'ai du code C++ qui imprime un size_t:

size_t a;
printf("%lu", a);

J'aimerais que cela compile sans avertissements sur les architectures 32 et 64 bits.

Si c'était C99, je pourrais utiliser printf("%z", a);. Mais AFAICT %z N'existe dans aucun dialecte C++ standard. Donc à la place, je dois faire

printf("%lu", (unsigned long) a);

ce qui est vraiment moche.

S'il n'y a pas de possibilité d'imprimer size_t S dans le langage, je me demande s'il est possible d'écrire un wrapper printf ou un tel tel qui insérera les transtypages appropriés sur size_t S afin d'éliminer les parasites avertissements du compilateur tout en conservant les bons.

Des idées?


Modifier
94
Justin L.

La plupart des compilateurs ont leur propre spécificateur pour size_t et ptrdiff_t arguments, Visual C++ utilise par exemple% Iu et% Id respectivement, je pense que gcc vous permettra d'utiliser% zu et% zd.

Vous pouvez créer une macro:

#if defined(_MSC_VER) || defined(__MINGW32__) //__MINGW32__ should goes before __GNUC__
  #define JL_SIZE_T_SPECIFIER    "%Iu"
  #define JL_SSIZE_T_SPECIFIER   "%Id"
  #define JL_PTRDIFF_T_SPECIFIER "%Id"
#Elif defined(__GNUC__)
  #define JL_SIZE_T_SPECIFIER    "%zu"
  #define JL_SSIZE_T_SPECIFIER   "%zd"
  #define JL_PTRDIFF_T_SPECIFIER "%zd"
#else
  // TODO figure out which to use.
  #if NUMBITS == 32
    #define JL_SIZE_T_SPECIFIER    something_unsigned
    #define JL_SSIZE_T_SPECIFIER   something_signed
    #define JL_PTRDIFF_T_SPECIFIER something_signed
  #else
    #define JL_SIZE_T_SPECIFIER    something_bigger_unsigned
    #define JL_SSIZE_T_SPECIFIER   something_bigger_signed
    #define JL_PTRDIFF_T_SPECIFIER something-bigger_signed
  #endif
#endif

Usage:

size_t a;
printf(JL_SIZE_T_SPECIFIER, a);
printf("The size of a is " JL_SIZE_T_SPECIFIER " bytes", a);
60
dalle

Le spécificateur de format printf%zu fonctionnera bien sur les systèmes C++; il n'est pas nécessaire de le rendre plus compliqué.

66
Will

C++ 11

C++ 11 importe C99 donc std::printf devrait prendre en charge le C99 %zu spécificateur de format.

C++ 98

Sur la plupart des plateformes, size_t et uintptr_t sont équivalents, auquel cas vous pouvez utiliser la macro PRIuPTR définie dans <cinttypes>:

size_t a = 42;
printf("If the answer is %" PRIuPTR " then what is the question?\n", a);

Si vous vraiment voulez être en sécurité, transformez en uintmax_t et utilisez PRIuMAX:

printf("If the answer is %" PRIuMAX " then what is the question?\n", static_cast<uintmax_t>(a));
17
Oktalist

Sur Windows et l'implémentation Visual Studio de printf

 %Iu

travaille pour moi. voir msdn

16
meissnersd

Puisque vous utilisez C++, pourquoi ne pas utiliser IOStreams? Cela devrait se compiler sans avertissements et faire la bonne chose en fonction du type, tant que vous n'utilisez pas une implémentation C++ morte de cerveau qui ne définit pas un operator << Pour size_t.

Lorsque la sortie réelle doit être effectuée avec printf(), vous pouvez toujours la combiner avec IOStreams pour obtenir un comportement de type sécurisé:

size_t foo = bar;
ostringstream os;
os << foo;
printf("%s", os.str().c_str());

Ce n'est pas super efficace, mais votre cas ci-dessus concerne les E/S de fichiers, c'est donc votre goulot d'étranglement, pas ce code de formatage de chaîne.

10
Warren Young

voici une solution possible, mais ce n'est pas tout à fait une jolie ..

template< class T >
struct GetPrintfID
{
  static const char* id;
};

template< class T >
const char* GetPrintfID< T >::id = "%u";


template<>
struct GetPrintfID< unsigned long long > //or whatever the 64bit unsigned is called..
{
  static const char* id;
};

const char* GetPrintfID< unsigned long long >::id = "%lu";

//should be repeated for any type size_t can ever have


printf( GetPrintfID< size_t >::id, sizeof( x ) );
7
stijn

Le type effectif sous-jacent size_t dépend de l'implémentation. C Standard le définit comme le type renvoyé par l'opérateur sizeof; en plus d'être non signé et une sorte de type intégral, la taille_t peut être à peu près tout ce dont la taille peut contenir la plus grande valeur attendue par sizeof ().

Par conséquent, la chaîne de format à utiliser pour un size_t peut varier en fonction du serveur. Il devrait toujours avoir le "u", mais peut être l ou d ou peut-être autre chose ...

Une astuce pourrait être de le convertir en le plus grand type intégral sur la machine, en assurant aucune perte dans la conversion, puis en utilisant la chaîne de format associée à ce type connu.

3
mjv

La bibliothèque Format C++ fournit une implémentation portable rapide (et sûre) de printf y compris le modificateur z pour size_t:

#include "format.h"

size_t a = 42;

int main() {
  fmt::printf("%zu", a);
}

En plus de cela, il prend en charge la syntaxe de chaîne de format de type Python et capture les informations de type afin que vous n'ayez pas à les fournir manuellement:

fmt::print("{}", a);

Il a été testé avec les principaux compilateurs et fournit une sortie cohérente sur toutes les plateformes.

Avertissement : Je suis l'auteur de cette bibliothèque.

3
vitaut