繁体   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++)

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

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

我希望在 32 位和 64 位体系结构上编译时没有警告。

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

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

这真的很丑。

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

有任何想法吗?


To clarify why I'm using printf: I have a relatively large code base that I'm cleaning up. 为了澄清我为什么使用 printf:我有一个相对较大的代码库,我正在清理。 它使用 printf 包装器来执行诸如“编写警告,将其记录到文件中,并可能以错误退出代码”之类的操作。 我可能能够使用 cout 包装器收集足够的 C++-foo 来执行此操作,但我宁愿不更改程序中的每个 warn() 调用只是为了摆脱一些编译器警告。

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

大多数编译器都有自己的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);

C++11

C++11 导入 C99,所以std::printf应该支持 C99 %zu格式说明符。

C++98

在大多数平台上, size_tuintptr_t是等效的,在这种情况下,您可以使用<cinttypes>定义的PRIuPTR宏:

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

在 windows 上和 printf 的 Visual Studio 实现

 %Iu

为我工作。 msdn

既然您使用的是 C++,为什么不使用 IOStreams? 只要您没有使用没有为size_t定义operator <<的脑死 C++ 实现,就应该在没有警告的情况下编译并执行正确的类型感知operator <<

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

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

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

这是一个可能的解决方案,但它不是一个很好的解决方案..

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

fmt 库提供了printf的快速可移植(和安全)实现,包括size_tz修饰符:

#include "fmt/printf.h"

size_t a = 42;

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

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

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

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

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

底层size_t的有效类型取决于实现 C 标准将其定义为 sizeof 运算符返回的类型; 除了无符号和一种整数类型之外,size_t 几乎可以是任何大小可以容纳预期由 sizeof() 返回的最大值的任何东西。

因此,用于 size_t 的格式字符串可能因服务器而异。 它应该总是有“u”,但可能是 l 或 d 或者其他东西......

一个技巧可能是将它转换为机器上最大的整数类型,确保转换不会丢失,然后使用与此已知类型关联的格式字符串。

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

暂无
暂无

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

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