web-dev-qa-db-ja.com

C ++のprintf size_tへのクリーンコード(または、C ++のC99の%zに最も近いもの)

_size_t_を出力するC++コードがいくつかあります。

_size_t a;
printf("%lu", a);
_

32ビットアーキテクチャと64ビットアーキテクチャの両方で、警告なしにこれをコンパイルしたいと思います。

これがC99であれば、printf("%z", a);を使用できます。しかし、AFAICT _%z_は、標準C++方言には存在しません。だから代わりに、私はしなければならない

_printf("%lu", (unsigned long) a);
_

これは本当にいです。

言語に組み込まれた_size_t_ sを印刷する機能がない場合、printfラッパーまたは適切なキャストを_size_t_ sに挿入して、偽のコンパイラ警告を排除するようなものを作成することは可能だろうか良いものを維持します。

何か案は?


編集
94
Justin L.

ほとんどのコンパイラは、size_tおよびptrdiff_t引数に独自の指定子を持っています。たとえば、Visual C++はそれぞれ%Iuと%Idを使用します。gccを使用すると%zuと%zdを使用できると思います。

マクロを作成できます:

#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

使用法:

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

printf形式指定子%zuはC++システムで正常に動作します。より複雑にする必要はありません。

66
Will

C++ 11

C++ 11はC99をインポートするため、std::printfはC99 %zu形式指定子をサポートする必要があります。

C++ 98

ほとんどのプラットフォームでは、size_tuintptr_tは同等です。この場合、<cinttypes>で定義されている PRIuPTR マクロを使用できます。

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

本当に安全にしたい場合は、uintmax_tにキャストしてPRIuMAXを使用します:

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

WindowsおよびVisual Studioでのprintfの実装

 %Iu

私のために働く。 msdn を参照してください

16
meissnersd

C++を使用しているので、なぜIOStreamsを使用しないのですか? _operator <<_に対して_size_t_を定義しない脳死のC++実装を使用していない限り、警告なしでコンパイルし、適切な型認識機能を実行する必要があります。

実際の出力をprintf()で行う必要がある場合でも、IOStreamsと組み合わせて、タイプセーフな動作を取得できます。

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

これは非常に効率的ではありませんが、上記のケースではファイルI/Oを扱っているため、この文字列フォーマットコードではなく、それがボトルネックです。

10
Warren Young

ここに可能な解決策がありますが、それはかなりきれいなものではありません。

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

基礎となる有効なタイプsize_tは実装依存。 C標準では、sizeof演算子によって返される型として定義しています。符号なしと整数型のほかに、size_tは、sizeof()によって返されると予想される最大値に対応できるサイズであれば、ほとんど何でもかまいません。

したがって、size_tに使用されるフォーマット文字列は、サーバーによって異なる場合があります。それは常に「u」を持っているはずですが、lまたはdまたは多分何かかもしれません...

トリックは、マシン上で最大の整数型にキャストし、変換の損失がないことを確認してから、この既知の型に関連付けられたフォーマット文字列を使用することです。

3
mjv

C++ Format ライブラリは、size_tprintf修飾子を含むzの高速で移植可能な(かつ安全な)実装を提供します。

#include "format.h"

size_t a = 42;

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

それに加えて、Pythonのような形式文字列構文をサポートし、型情報をキャプチャするため、手動で入力する必要はありません。

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

主要なコンパイラでテストされており、プラットフォーム間で一貫した出力を提供します。

免責事項:私はこのライブラリの著者です。

3
vitaut