[英]Sticky custom stream manipulator
如何实现我自己的自定义流操纵器以使其粘滞。 例如,我想将整数转换为二进制,以便:
cout << "decimal of 4: " << 4
<< "\ndecimal of 4: " << 4
<< binary << "\nbinary of 4: " << 4
<< "\nbinary of 4: " << 4
<< nobinary << "\ndecimal of 4: " << 4
<< "\ndecimal of 4: " << 4 << endl;
会回来:
decimal of 4: 4
decimal of 4: 4
binary of 4: 100
binary of 4: 100
decimal of 4: 4
decimal of 4: 4
做一切事情有点牵扯。 为了使其易于理解,我将从基本的东西开始:为用户定义的类型使用自定义格式标记。 整数的自定义格式将如下所示。
IOStream类[间接]从std::ios_base
派生,它为数据提供了两个存储: std::ios_base::iword()
和std::ios_base::pword()
用于int
和void*
。 维护用std::ios_base::pword()
存储的已分配内存是非常重要的,幸运的是,这个相对简单的用例不需要。 要使用这两个函数都将非const
引用返回给相应的类型,通常在程序中使用std::ios_base::xalloc()
一次分配索引,并在需要访问自定义格式标记时使用它。 当您使用iword()
或pword()
访问值时, iword()
初始化为零。 把事情放在一起,这是一个小程序,展示了这一点:
#include <iostream>
static int const index = std::ios_base::xalloc();
std::ostream& custom(std::ostream& stream) {
stream.iword(index) = 1;
return stream;
}
std::ostream& nocustom(std::ostream& stream) {
stream.iword(index) = 0;
return stream;
}
struct mytype {};
std::ostream& operator<< (std::ostream& out, mytype const&) {
return out << "custom-flag=" << out.iword(index);
}
int main()
{
std::cout << mytype() << '\n';
std::cout << custom;
std::cout << mytype() << '\n';
std::cout << nocustom;
std::cout << mytype() << '\n';
}
现在,像4
这样的int
不是用户定义类型,并且已经为这些类型定义了输出运算符。 幸运的是,您可以使用facet自定义整数的格式,更具体地说,使用std::num_put<char>
。 现在,为此,您需要执行以下步骤:
std::num_put<char>
派生一个类,并覆盖要为其提供特殊行为的do_put()
成员。 std::locale
对象。 std::ios_base::imbue()
带有新std::locale
的流。 为了使用户更好,您可能希望在使用操纵器时使用合适的std::num_put<char>
facet来std::num_put<char>
一个新的std::locale
。 但是,在这样做之前,让我们从创建一个合适的方面开始:
#include <bitset>
#include <iostream>
#include <limits>
#include <locale>
static int const index = std::ios_base::xalloc();
class num_put
: public std::num_put<char>
{
protected:
iter_type do_put(iter_type to,
std::ios_base& fmt,
char_type fill,
long v) const
{
if (!fmt.iword(index)) {
return std::num_put<char>::do_put(to, fmt, fill, v);
}
else {
std::bitset<std::numeric_limits<long>::digits> bits(v);
size_t i(bits.size());
while (1u < i && !bits[i - 1]) {
--i;
}
for (; 0u < i; --i, ++to) {
*to = bits[i - 1]? '1': '0';
}
return to;
}
}
#if 0
// These might need to be added, too:
iter_type do_put(iter_type, std::ios_base&, char_type,
long long) const;
iter_type do_put(iter_type, std::ios_base&, char_type,
unsigned long) const;
iter_type do_put(iter_type, std::ios_base&, char_type,
unsigned long long) const;
#endif
};
std::ostream& custom(std::ostream& stream) {
stream.iword(index) = 1;
return stream;
}
std::ostream& nocustom(std::ostream& stream) {
stream.iword(index) = 0;
return stream;
}
int main()
{
std::locale loc(std::locale(), new num_put);
std::cout.imbue(loc);
std::cout << 13 << '\n';
std::cout << custom;
std::cout << 13 << '\n';
std::cout << nocustom;
std::cout << 13 << '\n';
}
有点丑陋的是,有必要将自定义std::locale
imbue()
以使用custom
操纵器。 为了解决这个问题,我们可以确保在使用过的std::locale
安装了自定义facet,如果不是,只需在设置标志时安装它:
std::ostream& custom(std::ostream& stream) {
if (!stream.iword(index)
&& 0 == dynamic_cast<num_put const*>(
&std::use_facet<std::num_put<char> >(stream.getloc()))) {
stream.imbue(std::locale(stream.getloc(), new num_put));
}
stream.iword(index) = 1;
return stream;
}
现在剩下的就是覆盖不同的do_put()
成员以使用各种unsigned
类型和long long
来正常工作,但这仍然是一个练习。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.