[英]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
上插入适当的强制转换,以消除虚假的编译器警告,同时仍然保持良好的警告。
有任何想法吗?
printf
格式说明符%zu
在 C++ 系统上可以正常工作; 没有必要让它变得更复杂。
大多数编译器都有自己的size_t
和ptrdiff_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_t
和uintptr_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));
既然您使用的是 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_t
的z
修饰符:
#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.