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


96

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

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

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

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

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

これは本当に醜いです。

size_t言語に組み込まれたs を印刷する機能がない場合、適切なキャストをsize_tsに挿入して、適切なキャストを維持しながら、誤ったコンパイラ警告を排除できるprintfラッパーなどを作成できるかどうか疑問に思います。

何か案は?


編集私がprintfを使用している理由を明確にするために、私は整理している比較的大きなコードベースを持っています。printfラッパーを使用して、「警告を書き込み、それをファイルに記録し、場合によってはエラーでコードを終了する」などの処理を行います。coutラッパーを使用してこれを行うのに十分なC ++-fooを集めることはできるかもしれませんが、コンパイラの警告を取り除くためだけに、プログラムのすべてのwarn()呼び出しを変更したくありません。


4
なぜprintfを使用するのかが問題になります。
Ed S.

コンパイラはprintf文字列を検査し、タイプチェックを行いますか?
ポッド

私のコンパイラは実際にprintf形式の文字列を検査し、タイプチェックしてくれます。この機能をオンのままにしておきたい。
ジャスティンL.

2
%zu、zは幅指定子であり、タイプ指定子ではありません。C ++からシームレスに使用できるc printfで動作します。私は以下のことについてコメントし、それのために投票しました;)
ウィル

Visual Studioを使用している場合は、単に使用できません"%l"か?それは常に適切なサイズではありませんか?または携帯性は重要ですか?
Mooing Duck 2013

回答:


61

ほとんどのコンパイラには独自の指定子size_tptrdiff_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);

5
それは簡単ではありません。%zがサポートされているかどうかは、コンパイラではなく、ランタイムに依存します。__GNUC__したがって、GCC / mingwとmsvcrtを混在させる場合(およびmingwの拡張されたprintfを使用しない場合)、使用は少し問題になります。
ヨルゲンセン


17

C ++ 11

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

C ++ 98

ほとんどのプラットフォームでは、size_tuintptr_t等価であり、あなたが使用することができ、その場合PRIuPTRに定義されたマクロを<cinttypes>

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

16

WindowsおよびprintfのVisual Studio実装

 %Iu

私のために働く。msdnを参照


ありがとう。VS 2008でも動作します。また、1が使用できることに注意してください%Id%Ixそして%IX、あまりにも。
c00000fd 2016

11

C ++を使用しているので、IOStreamsを使用してみませんか?operator <<forを定義しない頭の悪いC ++実装を使用していない限り、警告なしにコンパイルされ、型に対応した適切な処理が行われますsize_t

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

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

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


Googleがコードでのcoutの使用を禁止していることを知っています。おそらく、ジャスティンLはそのような制限の下で働いています。

私の場合(上記の編集を参照)、興味深いアイデアは、coutの観点からwarn()関数を実装することです。ただし、フォーマット文字列を手動で解析する必要があります。これは...トリッキーです。:)
ジャスティンL.

あなたの最新の編集は、実際には私がうまくいくと私が思うこととは正反対です。printfラッパーを呼び出すすべてのコードを書き直したくありませんが、coutを使用するようにprintfラッパーの実装を書き直してもかまいません。しかし、そうなるとは思いません。:)
ジャスティンL.

std::stringstreamIOストリームの代わりに使用します。
Thomas Eding 2013

1
ストリームには、扱いにくい表記があります。比較:printf("x=%i, y=%i;\n", x, y);cout << "x=" << x << ", y=" << y << ";" << std::endl;
wonder.mice 2015年

7

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

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

2
まあ...それは私の安全という目標と警告なしを達成します。しかし...そうです。私がしなければならないことがあるなら、私は警告を受けます。:)
ジャスティンL.

1
きれいじゃない?味に依存します。uintptr_tなどの獣と完全に移植可能なprintfを使用できます。すごい!
スラバ2013年

@ user877329そのフォーマット文字列をstd :: stringとして構築し、必要な場所にGetPrintfID <size_t> :: idを追加できます
stijn

@stijn言い換えれば、利用可能なコンパイル時連結はありません
user877329

@ user877329いいえ(マクロを使用しない限り、または何か不足している場合を除く)。しかし、なぜそれが難しい要件なのでしょうか?
stijn 2014年

4

fmtライブラリーはの速いポータブル(かつ安全)の実装を提供printfなどのzための修飾子をsize_t

#include "fmt/printf.h"

size_t a = 42;

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

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

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

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

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


3

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

したがって、size_tに使用されるフォーマット文字列は、サーバーによって異なる場合があります。常に「u」が必要ですが、l、d、またはその他の場合があります...

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


size_tマシンで最大の整数型にsをキャストし、この型に関連付けられたフォーマット文字列を使用すると便利です。私の質問は:クリーンなコードを維持しながらこれを行う方法はありますか(正当なprintf形式の文字列エラーのみに対する警告、醜いキャストなどはありません)?書式文字列を変更するラッパーを作成することはできますが、書式文字列を正当に乱用した場合、GCCは警告を表示できません。
ジャスティンL.

CPPマクロを使用して、型のサイズをテストします。一致するものを探し、一致するタイプに対応するフォーマット文字列を指定します。
2014年

0

#include <cstdio>
#include <string>
#include <type_traits>

namespace my{
    template<typename ty>
    auto get_string(ty&& arg){
        using rty=typename::std::decay_t<::std::add_const_t<ty>>;
        if constexpr(::std::is_same_v<char, rty>)
            return ::std::string{1,arg};
        else if constexpr(::std::is_same_v<bool, rty>)
            return ::std::string(arg?"true":"false");
        else if constexpr(::std::is_same_v<char const*, rty>)
            return ::std::string{arg};
        else if constexpr(::std::is_same_v<::std::string, rty>)
            return ::std::forward<ty&&>(arg);
        else
            return ::std::to_string(arg);
    };

    template<typename T1, typename ... Args>
    auto printf(T1&& a1, Args&&...arg){
        auto str{(get_string(a1)+ ... + get_string(arg))};
        return ::std::printf(str.c_str());
    };
};

後でコード:

my::printf("test ", 1, '\t', 2.0);

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.