在C++中清除printf size_t的代码(或者:C++中C99的%z的最近等价物)

Jus*_* L. 94 c++ printf size-t

我有一些C++代码打印出size_t:

size_t a;
printf("%lu", a);
Run Code Online (Sandbox Code Playgroud)

我希望在32位和64位架构上编译时不会发出警告.

如果这是C99,我可以使用printf("%z", a);.但是AFAICT %z在任何标准C++方言中都不存在.所以相反,我必须这样做

printf("%lu", (unsigned long) a);
Run Code Online (Sandbox Code Playgroud)

这真的很难看.

如果没有size_t内置于该语言中的打印功能,我想知道是否可以编写一个printf包装器或类似的东西,这样可以在size_ts 上插入适当的强制转换,以便在保持好的编译器警告的同时消除虚假的编译器警告.

有任何想法吗?


编辑为了澄清为什么我使用printf:我有一个相对较大的代码库,我正在清理.它使用printf包装器来执行诸如"编写警告,将其记录到文件以及可能退出错误的代码"之类的操作.我可能能够用cout包装器来运行足够的C++ - foo,但是我不想改变程序中的每个warn()调用,只是为了摆脱一些编译器警告.

Wil*_*ill 67

printf格式说明%zu将正常工作在C++系统; 没有必要让它变得更复杂.

  • @ChrisMarkle快速测试显示它在MinGW中不起作用.MS站点也没有列出它(http://msdn.microsoft.com/en-us/library/tcxf1dw6%28v=vs.100%29.aspx).我想答案是否定的. (9认同)

dal*_*lle 60

大多数编译器都有自己的说明符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
Run Code Online (Sandbox Code Playgroud)

用法:

size_t a;
printf(JL_SIZE_T_SPECIFIER, a);
printf("The size of a is " JL_SIZE_T_SPECIFIER " bytes", a);
Run Code Online (Sandbox Code Playgroud)

  • 这并不容易.是否支持`%z`取决于运行时,而不是编译器.因此,如果将GCC/mingw与msvcrt混合(并且不使用mingw的增强printf),则使用`__GNUC__`会有点问题. (5认同)

Okt*_*ist 17

C++ 11

C++ 11导入C99所以std::printf应该支持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);
Run Code Online (Sandbox Code Playgroud)

如果你真的想要安全,请uintmax_t使用PRIuMAX:

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


mei*_*rsd 16

在Windows上和printf的Visual Studio实现

 %Iu
Run Code Online (Sandbox Code Playgroud)

适合我.见 msdn


War*_*ung 10

既然你正在使用C++,为什么不使用IOStreams?这应该在没有警告的情况下进行编译并做出正确的类型感知事情,只要你没有使用没有定义operator <<for 的脑死亡的C++实现size_t.

当必须完成实际输出时printf(),您仍然可以将其与IOStream组合以获得类型安全行为:

size_t foo = bar;
ostringstream os;
os << foo;
printf("%s", os.str().c_str());
Run Code Online (Sandbox Code Playgroud)

它不是超级高效的,但上面你的情况是处理文件I/O,所以这是你的瓶颈,而不是这个字符串格式化代码.


vit*_*aut 9

fmt提供了一种快速可移植(且安全)的实现,printf其中包含以下z修饰符size_t

#include "fmt/printf.h"

size_t a = 42;

int main() {
  fmt::printf("%zu", a);
}
Run Code Online (Sandbox Code Playgroud)

除此之外,它还支持类似 Python 的格式字符串语法并捕获类型信息,这样您就不必手动提供它:

fmt::print("{}", a);
Run Code Online (Sandbox Code Playgroud)

它已经过主要编译器的测试,并提供跨平台的一致输出。

免责声明:我是这个库的作者。


sti*_*ijn 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 ) );
Run Code Online (Sandbox Code Playgroud)

  • 嗯......这确实达到了我的安全目标,没有任何警告.但是...... yeesh.如果那是我必须做的事情,我会接受警告.:) (2认同)