Làm sạch mã thành printf size_t trong C ++ (hoặc: Tương đương gần nhất với% z của C99 trong C ++)


96

Tôi có một số mã C ++ in ra size_t:

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

Tôi muốn điều này được biên dịch mà không có cảnh báo trên cả kiến ​​trúc 32-bit và 64-bit.

Nếu đây là C99, tôi có thể sử dụng printf("%z", a);. Nhưng AFAICT %zkhông tồn tại trong bất kỳ phương ngữ C ++ chuẩn nào. Vì vậy, thay vào đó, tôi phải làm

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

mà thực sự là xấu xí.

Nếu không có tiện ích nào để in size_tđược tích hợp sẵn trong ngôn ngữ, tôi tự hỏi liệu có thể viết một trình bao bọc printf hoặc somes như vậy sẽ chèn các phôi thích hợp vào size_tđể loại bỏ các cảnh báo trình biên dịch giả trong khi vẫn duy trì các phôi tốt hay không.

Bất kỳ ý tưởng?


Chỉnh sửa Để làm rõ lý do tại sao tôi sử dụng printf: Tôi có một cơ sở mã tương đối lớn mà tôi đang dọn dẹp. Nó sử dụng trình bao bọc printf để thực hiện những việc như "viết cảnh báo, đăng nhập vào tệp và có thể thoát mã khi gặp lỗi". Tôi có thể tập hợp đủ C ++ - foo để làm điều này với trình bao bọc cout, nhưng tôi không muốn thay đổi mọi lệnh gọi warning () trong chương trình chỉ để loại bỏ một số cảnh báo trình biên dịch.


4
Tại sao bạn lại sử dụng printf sẽ là một câu hỏi.
Ed S.

trình biên dịch của bạn có kiểm tra chuỗi printf và kiểm tra kiểu cho bạn không?
Pod

Trình biên dịch của tôi thực sự kiểm tra chuỗi định dạng printf và gõ kiểm tra nó cho tôi. Tôi muốn tiếp tục bật tính năng này.
Justin L.

2
% zu, z là mã xác định độ rộng không phải mã định kiểu. Nó hoạt động với c printf mà bạn có thể sử dụng liền mạch từ C ++. Tôi đã nhận xét về nó bên dưới, vì vậy hãy bình chọn cho nó;)
Sẽ

Nếu bạn đang sử dụng Visual Studio, bạn không thể sử dụng "%l"? Đó sẽ không phải là luôn luôn đúng kích thước? Hay tính di động có quan trọng không?
Vịt kêu rên

Câu trả lời:


61

Hầu hết các trình biên dịch đều có bộ chỉ định riêng cho size_tptrdiff_t đối số , ví dụ Visual C ++ sử dụng% Iu và% Id tương ứng, tôi nghĩ rằng gcc sẽ cho phép bạn sử dụng% zu và% zd.

Bạn có thể tạo một macro:

#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

Sử dụng:

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

5
Nó không dễ như thế đâu. Có %zđược hỗ trợ hay không phụ thuộc vào thời gian chạy chứ không phải trình biên dịch. __GNUC__Do đó, việc sử dụng có một chút vấn đề, nếu bạn trộn GCC / mingw với msvcrt (và không sử dụng mingw's augmented printf).
jørgensen

68

Bộ printfđịnh dạng %zusẽ hoạt động tốt trên các hệ thống C ++; không cần phải làm cho nó phức tạp hơn.


9
@ChrisMarkle Một bài kiểm tra nhanh cho tôi thấy nó không hoạt động trong MinGW. Ngoài ra, trang MS không liệt kê nó ( msdn.microsoft.com/en-us/library/tcxf1dw6%28v=vs.100%29.aspx ). Tôi cho rằng câu trả lời là không.
wump

17

C ++ 11

C ++ 11 nhập C99 std::printfnên hỗ trợ %zuđịnh dạng C99 .

C ++ 98

Trên hầu hết các nền tảng size_tuintptr_ttương đương, trong trường hợp đó bạn có thể sử dụng PRIuPTRmacro được xác định trong <cinttypes>:

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

Nếu bạn thực sự muốn an toàn, hãy truyền đến uintmax_tvà sử dụngPRIuMAX :

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

16

Trên windows và triển khai Visual Studio của printf

 %Iu

làm việc cho tôi. xem msdn


Cảm ơn. Hoạt động VS 2008quá. Cũng nên nhớ rằng người ta có thể sử dụng %Id, %Ix%IXquá.
c00000fd

11

Vì bạn đang sử dụng C ++, tại sao không sử dụng IOStreams? Điều đó sẽ biên dịch mà không có cảnh báo và thực hiện đúng loại nhận biết, miễn là bạn không sử dụng triển khai C ++ chết não không định nghĩa operator <<for size_t.

Khi đầu ra thực tế phải được thực hiện với printf(), bạn vẫn có thể kết hợp nó với IOStreams để có được hành vi an toàn kiểu:

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

Nó không phải là siêu hiệu quả, nhưng trường hợp của bạn ở trên liên quan đến I / O tệp, vì vậy đó là nút cổ chai của bạn, không phải mã định dạng chuỗi này.


Tôi biết rằng Google cấm sử dụng cout trong mã của họ. Có lẽ Justin L. đang làm việc dưới sự hạn chế như vậy.

Trong trường hợp của tôi (xem phần chỉnh sửa ở trên), một ý tưởng thú vị có thể là thử và triển khai hàm warning () trong điều kiện cout. Nhưng điều đó sẽ liên quan đến việc phân tích cú pháp các chuỗi định dạng theo cách thủ công, điều này ... khó. :)
Justin L.

Chỉnh sửa mới nhất của bạn thực sự trái ngược với những gì tôi nghĩ có thể hiệu quả với tôi. Tôi không muốn viết lại tất cả mã gọi trình bao bọc printf, nhưng tôi sẽ không ngại viết lại việc triển khai trình bao bọc printf để sử dụng cout. Nhưng tôi không nghĩ điều đó sẽ xảy ra. :)
Justin L.

Sử dụng std::stringstreamthay cho luồng IO.
Thomas Eding

1
Các luồng có ký hiệu khó hiểu. Hãy so sánh: printf("x=%i, y=%i;\n", x, y);vs cout << "x=" << x << ", y=" << y << ";" << std::endl;.
wonder.mice

7

đây là một giải pháp khả thi, nhưng nó không hoàn toàn là một giải pháp tốt ..

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
Chà ... điều đó đạt được mục tiêu của tôi về sự an toàn và không có cảnh báo. Nhưng ... yeesh. Tôi sẽ cảnh báo nếu đó là điều tôi phải làm. :)
Justin L.

1
Không đẹp?! Tùy thuộc vào khẩu vị. Nó cho phép sử dụng printf hoàn toàn di động với các con thú như uintptr_t và tương tự. Tuyệt quá!
Slava

@ user877329, bạn có thể tạo chuỗi định dạng đó dưới dạng std :: string và sau đó nối GetPrintfID <size_t> :: id ở nơi bạn cần
stijn

@stijn Nói cách khác: không có liên kết thời gian biên dịch khả dụng
user877329 Ngày

@ user877329 không (trừ khi sử dụng macro hoặc tôi thiếu thứ gì đó). Nhưng tại sao đó lại là một yêu cầu khó?
stijn

4

Các thư viện fmt cung cấp một xách tay (và an toàn) nhanh chóng thực hiện printfbao gồm các zsửa đổi đối với size_t:

#include "fmt/printf.h"

size_t a = 42;

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

Ngoài ra, nó hỗ trợ cú pháp chuỗi định dạng giống Python và nắm bắt thông tin loại để bạn không phải cung cấp nó theo cách thủ công:

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

Nó đã được thử nghiệm với các trình biên dịch lớn và cung cấp đầu ra nhất quán trên các nền tảng.

Tuyên bố từ chối trách nhiệm : Tôi là tác giả của thư viện này.


3

Loại hiệu quả bên dưới size_t phụ thuộc vào việc triển khai . C Standard định nghĩa nó là kiểu được trả về bởi toán tử sizeof; ngoài việc không được đánh dấu và là một loại tích phân, size_t có thể là bất cứ thứ gì mà kích thước có thể chứa giá trị lớn nhất mà sizeof () mong đợi được trả về.

Do đó, chuỗi định dạng được sử dụng cho size_t có thể khác nhau tùy thuộc vào máy chủ. Nó phải luôn có chữ "u", nhưng có thể là l hoặc d hoặc có thể là một cái gì đó khác ...

Một thủ thuật có thể là chuyển nó thành kiểu tích phân lớn nhất trên máy, đảm bảo không bị mất trong quá trình chuyển đổi và sau đó sử dụng chuỗi định dạng được liên kết với kiểu đã biết này.


Tôi sẽ rất tuyệt khi đúc size_tcác s của mình thành kiểu tích phân lớn nhất trên máy và sử dụng chuỗi định dạng được liên kết với kiểu này. Câu hỏi của tôi là: Có cách nào tôi có thể làm điều này trong khi duy trì mã sạch (chỉ cảnh báo đối với lỗi chuỗi định dạng printf hợp pháp, không có phôi xấu xí, v.v.) không? Tôi có thể viết một trình bao bọc để thay đổi chuỗi định dạng, nhưng sau đó GCC sẽ không thể đưa ra cảnh báo cho tôi khi tôi làm sai chuỗi định dạng của mình một cách hợp pháp.
Justin L.

Sử dụng macro CPP để kiểm tra kích thước của các loại; chọn một trong những phù hợp và chỉ định chuỗi định dạng đi với loại phù hợp.
Rõ ràng hơn

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

Sau đó trong mã:

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

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.