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_t, 좋은 것을 유지하면서 가짜 컴파일러 경고를 제거하기 위해 s에 적절한 캐스트를 삽입 할 printf 래퍼를 작성하는 것이 가능한지 궁금 합니다.

어떤 아이디어?


편집 내가 printf를 사용하는 이유를 명확히하기 위해 : 나는 정리하고있는 비교적 큰 코드베이스를 가지고 있습니다. printf 래퍼를 사용하여 "경고를 작성하고, 파일에 기록하고, 오류와 함께 코드를 종료"와 같은 작업을 수행합니다. cout 래퍼로이 작업을 수행하기에 충분한 C ++-foo를 수집 할 수 있지만 컴파일러 경고를 제거하기 위해 프로그램의 모든 warn () 호출을 변경하는 것은 아닙니다.


4
왜 printf를 사용하고 있습니까?
Ed S.

컴파일러가 printf 문자열을 검사하고 유형 검사를 수행합니까?
포드

내 컴파일러는 실제로 printf 형식 문자열을 검사하고 유형 검사를 수행합니다. 이 기능을 계속 사용하고 싶습니다.
Justin L.

2
% zu, z는 유형 지정자가 아닌 너비 지정자입니다. C ++에서 원활하게 사용할 수있는 c printf에서 작동합니다. 아래에 댓글을 달았으므로 투표하세요;)
Will

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 / MSVCRT와와 Mingw (과와 Mingw의 증강의 printf를 사용하지 않고)을 혼합하는 경우, 따라서 약간의 문제이다.
jørgensen


17

C ++ 11

C ++ 11 std::printf은 C99를 가져 오므로 C99 %zu형식 지정자를 지원해야 합니다.

C ++ 98

대부분의 플랫폼에서, size_t그리고 uintptr_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 및 Visual Studio의 printf 구현에서

 %Iu

나를 위해 작동합니다. msdn 참조


감사. VS 2008너무 작동합니다 . 또한 사람이 사용할 수있는 것을 명심 %Id, %Ix%IX도.
c00000fd

11

C ++를 사용하고 있으므로 IOStreams를 사용하지 않는 이유는 무엇입니까? 당신은 정의하지 않습니다 뇌사 C ++ 구현을 사용하지 않는로 즉 한, 경고없이 컴파일하고 오른쪽 종류를 인식하는 일을해야합니다 operator <<위해를 size_t.

실제 출력이으로 완료되어야 printf()하는 경우에도이를 IOStreams와 결합하여 유형 안전 동작을 얻을 수 있습니다.

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

매우 효율적이지는 않지만 위의 사례는 파일 I / O를 다루기 때문에이 문자열 형식화 코드가 아닌 병목 현상입니다.


Google은 코드에서 cout 사용을 금지한다는 것을 알고 있습니다. 아마도 Justin L.은 그러한 제한 하에서 일하고있을 것입니다.

필자의 경우 (위의 편집 참조) 흥미로운 아이디어는 cout 측면에서 warn () 함수를 시도하고 구현하는 것입니다. 그러나 그것은 수동으로 형식 문자열을 구문 분석하는 것을 포함합니다. :)
Justin L.

귀하의 최근 편집은 실제로 저에게 효과가 있다고 생각하는 것과 반대입니다. printf 래퍼를 호출하는 모든 코드를 다시 작성하고 싶지는 않지만 cout을 사용하도록 printf 래퍼의 구현을 다시 작성하는 것은 괜찮습니다. 그러나 나는 그것이 일어날 것이라고 생각하지 않습니다. :)
Justin L.

std::stringstreamIO 스트림 대신 사용하십시오 .
Thomas Eding 2013-06-06

1
스트림에는 어색한 표기법이 있습니다. 비교 : printf("x=%i, y=%i;\n", x, y);cout << "x=" << x << ", y=" << y << ";" << std::endl;.
wonder.mice nov.

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
글쎄요 ... 그건 내 안전 목표를 달성하고 경고도 없습니다. 하지만 ... 그래. 내가해야 할 일이라면 경고를받을 게요. :)
Justin L.

1
예쁘지 않은?! 취향에 따라 다릅니다. 그것은 uintptr_t와 같은 짐승들과 함께 printf의 완전히 이식 가능한 사용을 허용합니다. 큰!
Slava

@ user877329 형식 문자열을 std :: string으로 빌드 한 다음 필요한 곳에 GetPrintfID <size_t> :: id를 추가 할 수 있습니다.
stijn

즉 @stijn : 없음 컴파일시 연결의 availible
user877329

@ user877329 아니요 (매크로를 사용하지 않는 경우 또는 누락 된 항목이 있음). 그러나 그것이 왜 어려운 요구 사항일까요?
stijn

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 Standard는이를 sizeof 연산자에 의해 반환 된 유형으로 정의합니다. unsigned 및 일종의 정수 유형을 제외하고 size_t는 sizeof ()에 의해 반환 될 것으로 예상되는 가장 큰 값을 수용 할 수있는 거의 모든 크기가 될 수 있습니다.

따라서 size_t에 사용되는 형식 문자열은 서버에 따라 다를 수 있습니다. 항상 "u"가 있어야하지만 l 또는 d 또는 다른 것일 수 있습니다.

트릭은 시스템에서 가장 큰 정수 유형으로 변환하여 변환 손실이 없도록 한 다음이 알려진 유형과 관련된 형식 문자열을 사용하는 것입니다.


나는 내 size_t를 기계에서 가장 큰 정수 유형으로 캐스팅 하고이 유형과 관련된 형식 문자열을 사용하는 것이 좋습니다. 내 질문은 다음과 같습니다. 깨끗한 코드를 유지하면서이 작업을 수행 할 수있는 방법이 있습니까 (합법적 인 printf 형식 문자열 오류에 대해서만 경고, 추악한 캐스트 없음 등)? 형식 문자열을 변경하는 래퍼를 작성할 수 있지만 합법적으로 형식 문자열을 엉망으로 만들면 GCC가 경고를 줄 수 없습니다.
Justin L.

CPP 매크로를 사용하여 유형의 크기를 테스트하십시오. 일치하는 항목을 찾아 일치하는 유형에 맞는 형식 문자열을 지정하십시오.
선명

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.