Pytanie Oczyść kod do printf size_t w C ++ (lub: Najbliższy odpowiednik C99% z C ++)


Mam trochę kodu C ++, który drukuje a size_t:

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

Chciałbym to skompilować bez ostrzeżeń na architekturach 32- i 64-bitowych.

Gdyby to było C99, mógłbym użyć printf("%z", a);. Ale AFAICT %z nie istnieje w żadnym standardowym dialekcie C ++. Zamiast tego muszę zrobić

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

który jest naprawdę brzydki.

Jeśli nie ma możliwości drukowania size_ts wbudowane w język, zastanawiam się, czy można napisać wrapper lub somesuch tak, że wstawi odpowiednie rzuty na size_ts, aby wyeliminować niepotrzebne ostrzeżenia kompilatora, a jednocześnie zachować dobre.

Jakieś pomysły?


Edytować Aby wyjaśnić, dlaczego używam printf: mam stosunkowo dużą bazę kodu, którą czyszczę. Używa wrapperów printf do robienia rzeczy takich jak "napisz ostrzeżenie, zaloguj się do pliku i ewentualnie wyjdź z kodu z błędem". Być może uda mi się zebrać wystarczająco dużo C ++ - foo, aby zrobić to z wrapperem cout, ale wolałbym nie zmieniać wszystkich wywołań warn () w programie tylko po to, aby pozbyć się ostrzeżeń kompilatora.


93
2017-10-10 01:37


pochodzenie


Dlaczego w ogóle używasz printf, powinno być to pytanie. - Ed S.
czy twój kompilator sprawdza ciąg printf i typ check dla ciebie? - Pod
Mój kompilator rzeczywiście sprawdza łańcuch formatu printf i typ, czy to dla mnie. Chciałbym włączyć tę funkcję. - Justin L.
% zu, z to specyfikator szerokości nie typu specyfikator. Działa dla c printf, którego możesz używać płynnie z C ++. Skomentowałem to poniżej, więc zagłosuj na to;) - Will
Jeśli używasz programu Visual Studio, nie możesz po prostu użyć "%l"? Czy to nie zawsze będzie odpowiedni rozmiar? Czy przenośność ma znaczenie? - Mooing Duck


Odpowiedzi:


Większość kompilatorów ma własny specyfikator size_t i ptrdiff_t argumenty, Visual C ++ na przykład używają odpowiednio% Iu i% Id, myślę, że gcc pozwoli ci na użycie% zu i% zd.

Możesz utworzyć 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

Stosowanie:

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

60
2017-11-03 18:26



To nie jest takie łatwe. Czy %z jest obsługiwane lub nie zależy od środowiska wykonawczego, a nie od kompilatora. Za pomocą __GNUC__ jest więc trochę problematyczny, jeśli miksujesz GCC / mingw z msvcrt (i bez używania rozszerzonego printf mingw). - jørgensen
% zu pracował dla mnie na gcc :) dziękuję


The printf specyfikator formatu %zu działa dobrze na systemach C ++; nie ma potrzeby, aby było to bardziej skomplikowane.


64
2017-11-03 18:28



Czy% z działa na kompilatorze Windows C? - Chris Markle
@ChrisMarkle Szybki test pokazuje, że nie działa w MinGW. Również strona MS nie wyświetla jej (msdn.microsoft.com/en-us/library/tcxf1dw6%28v=vs.100%29.aspx). Przypuszczam, że odpowiedź brzmi "nie". - wump
Nie działa w visual-C ++ - Manu Evans


W oknach i implementacji printf w Visual Studio

 %Iu

pracuje dla mnie. widzieć msdn


16
2018-02-25 14:57



Pomocny, %zu nie działa dla mnie. - nullspace
Dzięki. Pracuje w VS 2008 także. Pamiętaj też, że można z niego korzystać %Id, %Ixi %IX także. - c00000fd


C ++ 11

C ++ 11 importuje C99 tak std::printf powinien wspierać C99 %zu specyfikator formatu.

C ++ 98

Na większości platform size_t i uintptr_t są równoważne, w takim przypadku możesz użyć PRIuPTR makro zdefiniowane w <cinttypes>:

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

Jeśli ty naprawdę chcesz być bezpieczny, obsadzony uintmax_t I użyć PRIuMAX:

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

13
2018-06-21 13:32



To jedyna poprawna odpowiedź tutaj :) - justin.m.chase


Skoro używasz C ++, dlaczego nie używać IOStreams? To powinno się kompilować bez ostrzeżeń i robić właściwe informacje o typie, o ile nie używasz pozbawionej mózgu implementacji C ++, która nie definiuje operator << dla size_t.

Kiedy rzeczywiste wyjście musi zostać wykonane printf(), nadal możesz łączyć go z IOStreams, aby uzyskać zachowanie typu:

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

To nie jest super-wydajne, ale powyższy przypadek dotyczy pliku I / O, więc to jest wąskie gardło, a nie ten kod formatujący ciągi znaków.


10
2017-10-10 01:40



Wiem, że Google zabrania używania cout w ich kodzie. Być może Justin L. pracuje pod takim ograniczeniem.
W moim przypadku (patrz edycja powyżej), ciekawym pomysłem może być próba zastosowania funkcji warn () w kategoriach cout. Ale to wymagałoby ręcznego formatowania parsowania, co jest ... trudne. :) - Justin L.
Twoja ostatnia edycja jest w rzeczywistości przeciwieństwem tego, co moim zdaniem może dla mnie zadziałać. Nie chcę przepisywać całego kodu, który wywołuje wrapf z printf, ale nie miałbym nic przeciwko przepisywaniu implementacji opakowania printf do użycia cout. Ale nie sądzę, że to się stanie. :) - Justin L.
Posługiwać się std::stringstream zamiast strumieni IO. - Thomas Eding
Strumienie mają niezręczny zapis. Porównać: printf("x=%i, y=%i;\n", x, y); vs cout << "x=" << x << ", y=" << y << ";" << std::endl;. - wonder.mice


Oto możliwe rozwiązanie, ale nie jest to całkiem ładne ...

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
2017-10-10 09:11



Cóż ... to osiąga mój cel bezpieczeństwa i żadnych ostrzeżeń. Ale ... yeesh. Wezmę ostrzeżenia, jeśli to właśnie muszę zrobić. :) - Justin L.
Nie ładne ?! Zależy od gustu. Pozwala to na w pełni przenośne korzystanie z printf z bestiami takimi jak uintptr_t i podobne. Wspaniały! - Slava
@stijn Jak by to działało dla ciągów formatów innych niż trival? - user877329
@ user877329 można zbudować ten ciąg formatu jako std :: string, a następnie dołączyć GetPrintfID <size_t> :: id w miejscu, które go potrzebuje - stijn
@stijn Innymi słowy: nie jest dostępna żadna konkatenacja podczas kompilacji - user877329


Efektywny typ leżący u podstaw size_t zależy od implementacji. C Standard definiuje go jako typ zwracany przez operatora sizeof; oprócz tego, że jest niepodpisany i jest typem całkowitym, size_t może być prawie dowolną wielkością, która może pomieścić największą wartość, która ma być zwrócona przez sizeof ().

W związku z tym ciąg formatu, który ma być użyty dla size_t, może się różnić w zależności od serwera. Powinno zawsze mieć "u", ale może być l lub d, a może coś innego ...

Sztuczką może być rzucenie go do największego typu integralnego na maszynie, zapewniając brak strat w konwersji, a następnie użycie ciągu formatu skojarzonego z tym znanym typem.


3
2017-10-10 01:48



Byłbym fajny rzucając moje size_ts do największego typu całki na maszynie i używając ciągu formatów skojarzonego z tym typem. Moje pytanie brzmi: czy jest sposób, w jaki mogę to zrobić, zachowując czysty kod (ostrzeżenia tylko dla poprawnych błędów w formacie printf, bez brzydkich rzutów itp.)? Mógłbym napisać wrapper, który zmienia ciąg formatu, ale wtedy GCC nie byłby w stanie dać mi ostrzeżeń, gdy prawnie zawiedli mój ciąg formatu. - Justin L.
Użyj makr CPP, aby przetestować rozmiar typów; przejdź do tego, który pasuje i podaj ciąg formatu, który pasuje do pasującego typu. - Clearer


The Format C ++ biblioteka zapewnia szybką przenośną (i bezpieczną) implementację printf włączając z modyfikator dla size_t:

#include "format.h"

size_t a = 42;

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

Oprócz tego obsługuje składnię łańcuchów formatu podobną do Pythona i przechwytuje informacje o typie, dzięki czemu nie trzeba jej podawać ręcznie:

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

Został przetestowany z większymi kompilatorami i zapewnia spójne wyniki na różnych platformach.

Zrzeczenie się: Jestem autorem tej biblioteki.


2
2017-10-27 03:11