Bersihkan kode ke printf size_t di C ++ (atau: Setara terdekat dengan% z C99 di C ++)

96

Saya memiliki beberapa kode C ++ yang mencetak size_t:

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

Saya ingin ini mengompilasi tanpa peringatan pada arsitektur 32 dan 64-bit.

Jika ini C99, saya bisa menggunakan printf("%z", a);. Tetapi AFAICT %ztidak ada dalam dialek C ++ standar. Jadi, saya harus melakukannya

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

yang sangat jelek.

Jika tidak ada fasilitas untuk mencetak yang ada size_tdi dalam bahasa, saya bertanya-tanya apakah mungkin untuk menulis pembungkus printf atau semacamnya yang akan memasukkan cetakan yang sesuai size_tuntuk menghilangkan peringatan compiler palsu sambil tetap mempertahankan yang baik.

Ada ide?


Edit Untuk memperjelas mengapa saya menggunakan printf: Saya memiliki basis kode yang relatif besar yang saya bersihkan. Ini menggunakan pembungkus printf untuk melakukan hal-hal seperti "menulis peringatan, memasukkannya ke file, dan mungkin keluar dari kode dengan kesalahan". Saya mungkin dapat mengumpulkan cukup C ++ - foo untuk melakukan ini dengan pembungkus cout, tetapi saya lebih suka tidak mengubah setiap panggilan warn () dalam program hanya untuk menghilangkan beberapa peringatan kompiler.

Justin L.
sumber
4
Mengapa Anda menggunakan printf sama sekali harus menjadi pertanyaan.
Ed S.
apakah kompiler Anda memeriksa string dan tipe printf untuk Anda?
Pod
Kompiler saya memang memeriksa string format printf dan ketik periksa untuk saya. Saya ingin tetap mengaktifkan fitur ini.
Justin L.
2
% zu, z adalah penentu lebar bukan penentu tipe. Ia bekerja untuk c printf yang dapat Anda gunakan dengan mulus dari C ++. Saya telah mengomentarinya di bawah, jadi pilihlah;)
Akankah
Jika Anda menggunakan Visual Studio, tidak bisakah Anda menggunakan "%l"? Bukankah itu selalu dalam ukuran yang tepat? Atau apakah portabilitas itu penting?
Mooing Duck

Jawaban:

61

Kebanyakan kompiler memiliki specifier size_tdan ptrdiff_targumennya sendiri, Visual C ++ misalnya menggunakan% Iu dan% Id masing-masing, saya pikir gcc akan memungkinkan Anda untuk menggunakan% zu dan% zd.

Anda bisa membuat makro:

#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

Pemakaian:

size_t a;
printf(JL_SIZE_T_SPECIFIER, a);
printf("The size of a is " JL_SIZE_T_SPECIFIER " bytes", a);
dalle
sumber
5
Tidak semudah itu. Apakah %zdidukung atau tidak tergantung pada runtime, tidak compiler. Menggunakan __GNUC__karena itu sedikit masalah, jika Anda mencampur GCC / mingw dengan msvcrt (dan tanpa menggunakan printf augmented mingw ini).
jørgensen
68

Penentu printfformat %zuakan bekerja dengan baik pada sistem C ++; tidak perlu membuatnya lebih rumit.

Akan
sumber
9
@ChrisMarkle Sebuah tes cepat menunjukkan kepada saya itu tidak bekerja di MinGW. Juga situs MS tidak mencantumkannya ( msdn.microsoft.com/en-us/library/tcxf1dw6%28v=vs.100%29.aspx ). Saya kira jawabannya tidak.
wump
17

C ++ 11

C ++ 11 mengimpor C99 sehingga std::printfharus mendukung penentu %zuformat C99 .

C ++ 98

Pada sebagian besar platform, size_tdan uintptr_tsetara, dalam hal ini Anda dapat menggunakan PRIuPTRmakro yang ditentukan di <cinttypes>:

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

Jika Anda benar - benar ingin aman, transmisikan ke uintmax_tdan gunakan PRIuMAX:

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

Pada Windows dan implementasi Visual Studio printf

 %Iu

bekerja untuk saya. lihat msdn

meissnersd
sumber
Terima kasih. Bekerja VS 2008juga. Juga perlu diingat bahwa seseorang dapat menggunakan %Id, %Ixdan %IXjuga.
c00000fd
11

Karena Anda menggunakan C ++, mengapa tidak menggunakan IOStreams? Itu harus dikompilasi tanpa peringatan dan melakukan hal yang benar-benar sadar jenis, selama Anda tidak menggunakan implementasi C ++ yang tidak mendefinisikan operator <<untuk size_t.

Ketika keluaran sebenarnya harus diselesaikan printf(), Anda masih dapat menggabungkannya dengan IOStreams untuk mendapatkan perilaku tipe-aman:

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

Ini tidak super efisien, tetapi kasus Anda di atas berkaitan dengan file I / O, jadi itu hambatan Anda, bukan kode pemformatan string ini.

Warren Young
sumber
Saya tahu bahwa Google melarang penggunaan cout dalam kode mereka. Mungkin Justin L. bekerja di bawah batasan seperti itu.
Dalam kasus saya (lihat edit di atas), ide menarik mungkin untuk mencoba dan mengimplementasikan fungsi warn () dalam istilah cout. Tapi itu akan melibatkan penguraian string format secara manual, yang ... rumit. :)
Justin L.
Hasil edit terakhir Anda sebenarnya kebalikan dari apa yang menurut saya mungkin berhasil untuk saya. Saya tidak ingin menulis ulang semua kode yang memanggil pembungkus printf, tetapi saya tidak keberatan menulis ulang penerapan pembungkus printf untuk menggunakan cout. Tapi saya tidak berpikir itu akan terjadi. :)
Justin L.
Gunakan std::stringstreamalih-alih aliran IO.
Thomas Eding
1
Aliran memiliki notasi yang canggung. Bandingkan: printf("x=%i, y=%i;\n", x, y);vs cout << "x=" << x << ", y=" << y << ";" << std::endl;.
wonder.mice
7

inilah solusi yang mungkin, tetapi tidak cukup bagus ..

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 ) );
stijn
sumber
2
Nah ... itu mencapai tujuan saya yaitu keselamatan dan tidak ada peringatan. Tapi ... ya. Saya akan menerima peringatan jika itu yang harus saya lakukan. :)
Justin L.
1
Tidak cantik?! Tergantung selera. Ini memungkinkan penggunaan printf yang sepenuhnya portabel dengan beast seperti uintptr_t dan sejenisnya. Bagus!
Slava
@ user877329 Anda dapat membangun string format itu sebagai std :: string dan kemudian menambahkan GetPrintfID <size_t> :: id di tempat yang Anda butuhkan
stijn
@stijn Dengan kata lain: tidak ada penggabungan waktu kompilasi yang tersedia
pengguna877329
@ user877329 tidak (kecuali menggunakan makro, atau saya melewatkan sesuatu). Tetapi mengapa itu menjadi persyaratan yang sulit?
stijn
4

The perpustakaan fmt menyediakan portabel (dan aman) pelaksanaan cepat printftermasuk zpengubah untuk size_t:

#include "fmt/printf.h"

size_t a = 42;

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

Selain itu, ia mendukung sintaks string format seperti Python dan menangkap informasi tipe sehingga Anda tidak perlu menyediakannya secara manual:

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

Ini telah diuji dengan kompiler utama dan memberikan output yang konsisten di seluruh platform.

Penafian : Saya adalah pembuat perpustakaan ini.

vitaut.dll
sumber
3

Jenis efektif yang mendasari size_t bergantung pada implementasi . C Standar mendefinisikannya sebagai tipe yang dikembalikan oleh ukuran operator; Selain unsigned dan semacam tipe integral, size_t bisa menjadi apa saja yang ukurannya bisa menampung nilai terbesar yang diharapkan akan dikembalikan oleh sizeof ().

Akibatnya format string yang akan digunakan untuk size_t dapat bervariasi tergantung pada server. Itu harus selalu memiliki "u", tapi mungkin l atau d atau mungkin sesuatu yang lain ...

Triknya bisa dengan melemparkannya ke tipe integral terbesar di mesin, memastikan tidak ada kerugian dalam konversi, dan kemudian menggunakan string format yang terkait dengan tipe yang diketahui ini.

mjv
sumber
Saya akan mendinginkan casting saya size_tke tipe integral terbesar pada mesin dan menggunakan format string yang terkait dengan tipe ini. Pertanyaan saya adalah: Apakah ada cara saya dapat melakukan ini sambil mempertahankan kode yang bersih (peringatan hanya untuk kesalahan string format printf yang sah, tidak ada pemeran yang jelek, dll.)? Saya bisa menulis pembungkus yang mengubah format string, tapi kemudian GCC tidak akan bisa memberi saya peringatan ketika saya secara sah mengacaukan format string saya.
Justin L.
Gunakan makro CPP untuk menguji ukuran jenis; pilih salah satu yang cocok dan tentukan format string yang sesuai dengan jenis yang cocok.
jelas
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());
    };
};

Nanti di kode:

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

Gelombang Merah
sumber