C ++ 'da size_t'yi yazdırmak için temiz kod (veya: C ++' da C99'un% z'sinin en yakın eşdeğeri)


96

A: yazan bazı C ++ kodum var size_t:

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

Bunun hem 32 hem de 64 bit mimarilerde uyarı olmadan derlenmesini istiyorum.

Bu C99 olsaydı kullanabilirdim printf("%z", a);. Ancak AFAICT %zherhangi bir standart C ++ lehçesinde mevcut değildir. Yani bunun yerine yapmalıyım

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

ki bu gerçekten çirkin.

size_tDilde yerleşik yazdırma olanağı yoksa size_t, iyi olanları korurken sahte derleyici uyarılarını ortadan kaldırmak için s üzerine uygun dökümleri ekleyecek bir printf wrapper veya böyle bir şey yazmanın mümkün olup olmadığını merak ediyorum .

Herhangi bir fikir?


Düzenle printf'i neden kullandığımı açıklığa kavuşturmak için: Temizlediğim nispeten büyük bir kod tabanım var. "Bir uyarı yaz, bir dosyaya kaydet ve muhtemelen bir hata ile koddan çık" gibi şeyler yapmak için printf sarmalayıcıları kullanır. Bunu bir cout sarmalayıcısı ile yapmak için yeterli C ++ - foo toplayabilirim, ancak bazı derleyici uyarılarından kurtulmak için programdaki her warn () çağrısını değiştirmeyi tercih etmem.


4
Neden printf kullanıyorsunuz sorusu olmalı.
Ed S.

derleyiciniz printf dizesini inceliyor ve sizin için tür kontrolü yapıyor mu?
Pod

Derleyicim gerçekten printf format dizesini inceliyor ve benim için kontrol et. Bu özelliği açık tutmak istiyorum.
Justin L.

2
% zu, z bir genişlik belirticisi değil, tür belirticisi. C ++ 'dan sorunsuz bir şekilde kullanabileceğiniz c printf için çalışır. ); Aşağıda buna yorum, yani bunun için oy ettik
Will

Visual Studio kullanıyorsanız, kullanamaz "%l"mısınız? Bu her zaman doğru boyutta olmayacak mı? Veya taşınabilirlik önemli mi?
Mooing Duck

Yanıtlar:


61

Çoğu derleyicinin kendi belirticisi size_tve ptrdiff_tbağımsız değişkenleri vardır, örneğin Visual C ++ sırasıyla% Iu ve% Id kullanır, gcc'nin% zu ve% zd kullanmanıza izin vereceğini düşünüyorum.

Bir makro oluşturabilirsiniz:

#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

Kullanım:

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

5
O kadar kolay değil. İster %zdesteklemediği veya çalışma zamanını değil, derleyici bağlıdır. Bu __GNUC__nedenle, GCC / mingw'yi msvcrt ile karıştırırsanız (ve mingw'nin artırılmış printf'i kullanmadan) kullanmak biraz problemdir.
jørgensen


17

C ++ 11

C ++ 11 std::printf, C99'u içe aktarır, bu nedenle C99 %zubiçim belirticisini desteklemelidir .

C ++ 98

Çoğu platformda size_tve uintptr_teşdeğerdir, bu durumda PRIuPTRşurada tanımlanan makroyu kullanabilirsiniz <cinttypes>:

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

Eğer varsa gerçekten güvende olmak istiyorum, döküm uintmax_tve kullanım PRIuMAX:

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

16

Windows ve printf'nin Visual Studio uygulaması üzerinde

 %Iu

benim için çalışıyor. msdn'ye bakın


Teşekkürler. İçinde VS 2008de çalışıyor. Ayrıca birini kullanabilirsiniz unutmayın %Id, %Ixve %IXde.
c00000fd

11

C ++ kullandığınız için neden IOStreams kullanmıyorsunuz? Bir operator <<for tanımlamayan beyin ölümü C ++ uygulaması kullanmadığınız sürece, bu uyarılar olmadan derlenmeli ve doğru tipe duyarlı şeyi yapmalıdır .size_t .

Gerçek çıktının yapılması gerektiğinde printf(), tür açısından güvenli davranış elde etmek için yine de IOStreams ile birleştirebilirsiniz:

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

Süper verimli değil, ancak yukarıdaki durumunuz dosya G / Ç ile ilgilidir, bu nedenle bu dize biçimlendirme kodu değil, sizin darboğazınızdır.


Google'ın kodlarında cout kullanımını yasakladığını biliyorum. Belki Justin L. böyle bir kısıtlama altında çalışıyor.

Benim durumumda (yukarıdaki düzenlemeye bakın), warn () işlevini cout açısından denemek ve uygulamak ilginç bir fikir olabilir. Ancak bu, biçim dizelerini manuel olarak ayrıştırmayı içerir, bu da ... zordur. :)
Justin L.

Son düzenlemeniz aslında benim için işe yarayacağını düşündüğüm şeyin tam tersi. Bir printf sarmalayıcısını çağıran tüm kodu yeniden yazmak istemiyorum, ancak cout kullanmak için printf sarmalayıcısının uygulamasını yeniden yazmaktan çekinmem. Ama bunun olacağını sanmıyorum. :)
Justin L.

std::stringstreamIO akışları yerine kullanın .
Thomas Eding

1
Akışların tuhaf gösterimleri var. Karşılaştır: printf("x=%i, y=%i;\n", x, y);ile cout << "x=" << x << ", y=" << y << ";" << std::endl;.
wonder.mice

7

İşte olası bir çözüm, ama pek de hoş değil ..

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
Şey ... bu benim güvenlik hedefime ulaşıyor ve uyarı yok. Ama ... evet. Yapmam gereken buysa uyarıları alacağım. :)
Justin L.

1
Güzel değil?! Zevke bağlıdır. Printf'in uintptr_t ve benzeri canavarlarla tamamen taşınabilir kullanımına izin verir. Harika!
Slava

@ user877329 bu biçim dizesini bir std :: string olarak oluşturabilir ve ardından GetPrintfID <size_t> ::
id'yi

@stijn Başka bir deyişle: derleme zamanı bitiştirme mevcut değil
user877329

@ user877329 hayır (makro kullanmadıkça veya bir şeyi kaçırıyorum). Ama bu neden zor bir gereklilik olsun?
stijn

4

Fmt kütüphane hızlı bir taşınabilir (ve güvenli) uygulamasını sağlar printfdahil ziçin değiştirici size_t:

#include "fmt/printf.h"

size_t a = 42;

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

Buna ek olarak, Python benzeri biçim dizesi sözdizimini destekler ve manuel olarak sağlamanız gerekmemesi için tür bilgilerini yakalar:

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

Başlıca derleyicilerle test edilmiştir ve platformlar arasında tutarlı çıktı sağlar.

Feragatname : Bu kütüphanenin yazarıyım.


3

Size_t'nin altında yatan etkili tür uygulamaya bağlıdır . C Standard bunu sizeof operatörünün döndürdüğü tür olarak tanımlar; işaretsiz ve bir tür integral türü bir yana, size_t, sizeof () tarafından döndürülmesi beklenen en büyük değeri barındırabilen boyutun hemen hemen her şeyi olabilir.

Sonuç olarak size_t için kullanılacak biçim dizgisi sunucuya bağlı olarak değişebilir. Her zaman "u" olmalıdır, ama l veya d veya başka bir şey olabilir ...

İşin püf noktası, onu makinedeki en büyük integral türüne dönüştürmek, dönüştürmede hiçbir kayıp olmamasını sağlamak ve ardından bu bilinen türle ilişkili biçim dizesini kullanmak olabilir.


Ben e-postalarımı size_tmakinedeki en büyük integral tipine çevirmek ve bu türle ilişkili biçim dizesini kullanmak harika olurdu . Sorum şu: Temiz kodu korurken bunu yapmamın bir yolu var mı (yalnızca yasal printf format dizesi hataları için uyarılar, çirkin yayınlar vb.) Biçim dizesini değiştiren bir sarmalayıcı yazabilirdim, ancak bu durumda GCC biçim dizgemi yasal olarak karıştırdığımda bana uyarı veremezdi.
Justin L.

Türlerin boyutunu test etmek için CPP makrolarını kullanın; eşleşen olanı seçin ve eşleşen türle uyumlu biçim dizesini belirtin.
daha açık

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

Daha sonra kodda:

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

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.