简体   繁体   English

在 C++ 中将代码清理为 printf size_t(或:在 C++ 中与 C99 的 %z 最接近的等价物)

[英]Clean code to printf size_t in C++ (or: Nearest equivalent of C99's %z in C++)

I have some C++ code that prints a size_t :我有一些打印size_t C++ 代码:

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

I'd like this to compile without warnings on both 32- and 64-bit architectures.我希望在 32 位和 64 位体系结构上编译时没有警告。

If this were C99, I could use printf("%z", a);如果这是 C99,我可以使用printf("%z", a); . . But AFAICT %z doesn't exist in any standard C++ dialect.但是 AFAICT %z在任何标准 C++ 方言中都不存在。 So instead, I have to do所以相反,我必须做

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

which is really ugly.这真的很丑。

If there's no facility for printing size_t s built into the language, I wonder if it's possible to write a printf wrapper or somesuch such that will insert the appropriate casts on size_t s so as to eliminate spurious compiler warnings while still maintaining the good ones.如果语言中没有打印size_t的功能,我想知道是否有可能编写 printf 包装器或类似的东西,以便在size_t上插入适当的强制转换,以消除虚假的编译器警告,同时仍然保持良好的警告。

Any ideas?有任何想法吗?


Edit To clarify why I'm using printf: I have a relatively large code base that I'm cleaning up. 编辑为了澄清我为什么使用 printf:我有一个相对较大的代码库,我正在清理。 It uses printf wrappers to do things like "write a warning, log it to a file, and possibly exit the code with an error". 它使用 printf 包装器来执行诸如“编写警告,将其记录到文件中,并可能以错误退出代码”之类的操作。 I might be able to muster up enough C++-foo to do this with a cout wrapper, but I'd rather not change every warn() call in the program just to get rid of some compiler warnings. 我可能能够使用 cout 包装器收集足够的 C++-foo 来执行此操作,但我宁愿不更改程序中的每个 warn() 调用只是为了摆脱一些编译器警告。

The printf format specifier %zu will work fine on C++ systems; printf格式说明符%zu在 C++ 系统上可以正常工作; there is no need to make it more complicated.没有必要让它变得更复杂。

Most compilers have their own specifier for size_t and ptrdiff_t arguments, Visual C++ for instance use %Iu and %Id respectively, I think that gcc will allow you to use %zu and %zd.大多数编译器都有自己的size_tptrdiff_t参数说明符,例如 Visual C++ 分别使用 %Iu 和 %Id,我认为 gcc 将允许您使用 %zu 和 %zd。

You could create a 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

Usage:用法:

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

C++11 C++11

C++11 imports C99 so std::printf should support the C99 %zu format specifier. C++11 导入 C99,所以std::printf应该支持 C99 %zu格式说明符。

C++98 C++98

On most platforms, size_t and uintptr_t are equivalent, in which case you can use the PRIuPTR macro defined in <cinttypes> :在大多数平台上, size_tuintptr_t是等效的,在这种情况下,您可以使用<cinttypes>定义的PRIuPTR宏:

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

If you really want to be safe, cast to uintmax_t and use PRIuMAX :如果您真的想要安全,请转换为uintmax_t并使用PRIuMAX

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

On windows and the Visual Studio implementation of printf在 windows 上和 printf 的 Visual Studio 实现

 %Iu

works for me.为我工作。 see msdnmsdn

Since you're using C++, why not use IOStreams?既然您使用的是 C++,为什么不使用 IOStreams? That should compile without warnings and do the right type-aware thing, as long as you're not using a brain-dead C++ implementation that doesn't define an operator << for size_t .只要您没有使用没有为size_t定义operator <<的脑死 C++ 实现,就应该在没有警告的情况下编译并执行正确的类型感知operator <<

When the actual output has to be done with printf() , you can still combine it with IOStreams to get type-safe behavior:当必须使用printf()完成实际输出时,您仍然可以将其与 IOStreams 结合以获得类型安全的行为:

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

It's not super-efficient, but your case above deals with file I/O, so that's your bottleneck, not this string formatting code.它不是超级高效,但你上面的情况处理文件 I/O,所以这是你的瓶颈,而不是这个字符串格式化代码。

here's a possible solution, but it's not quite a pretty one..这是一个可能的解决方案,但它不是一个很好的解决方案..

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

The fmt library provides a fast portable (and safe) implementation of printf including the z modifier for size_t : fmt 库提供了printf的快速可移植(和安全)实现,包括size_tz修饰符:

#include "fmt/printf.h"

size_t a = 42;

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

In addition to that it supports Python-like format string syntax and captures type information so that you don't have to provide it manually:除此之外,它还支持类似 Python 的格式字符串语法并捕获类型信息,因此您不必手动提供它:

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

It has been tested with major compilers and provides consistent output across platforms.它已经过主要编译器的测试,并提供跨平台一致的输出。

Disclaimer : I'm the author of this library.免责声明:我是这个库的作者。

The effective type underlying size_t is implementation dependent .底层size_t的有效类型取决于实现 C Standard defines it as the type returned by the sizeof operator; C 标准将其定义为 sizeof 运算符返回的类型; aside from being unsigned and a sort of integral type, the size_t can be pretty much anything which size can accommodate the biggest value expected to be returned by sizeof().除了无符号和一种整数类型之外,size_t 几乎可以是任何大小可以容纳预期由 sizeof() 返回的最大值的任何东西。

Consequently the format string to be used for a size_t may vary depending on the server.因此,用于 size_t 的格式字符串可能因服务器而异。 It should always have the "u", but may be l or d or maybe something else...它应该总是有“u”,但可能是 l 或 d 或者其他东西......

A trick could be to cast it to the biggest integral type on the machine, ensuring no loss in the conversion, and then using the format string associated with this known type.一个技巧可能是将它转换为机器上最大的整数类型,确保转换不会丢失,然后使用与此已知类型关联的格式字符串。

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

Later in code:后面的代码:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM