做整个事情有点复杂。为了使其易于理解,我将从基本内容开始:对用户定义的类型使用自定义格式标志。整数的自定义格式将在下面进行。
IOStream 类 [间接地] 派生,std::ios_base
从中提供两个数据存储:std::ios_base::iword()
分别std::ios_base::pword()
用于int
s 和void*
。维护存储的已分配内存并std::ios_base::pword()
非易事,幸运的是,对于这个相对简单的用例,不需要。要使用这些都返回const
对相应类型的非引用的函数,您通常在程序中使用一次分配索引std::ios_base::xalloc()
,并在需要访问自定义格式标志时使用它。当您使用iword()
或pword()
最初访问一个值时,它将被初始化为零。总而言之,这里有一个小程序演示了这一点:
#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';
}
现在,int
like4
不是用户定义的类型,并且已经为这些定义了输出运算符。幸运的是,您可以使用构面自定义整数格式化的方式,更具体地说,使用std::num_put<char>
. 现在,要这样做,您需要执行多个步骤:
- 派生一个类
std::num_put<char>
并覆盖do_put()
您要为其提供专门行为的成员。
std::locale
使用新创建的构面创建一个对象。
std::ios_base::imbue()
与新的流std::locale
。
为了让用户感觉更好,您可能希望在使用操纵器时想出一个std::locale
具有合适构面的新的。std::num_put<char>
但是,在这样做之前,让我们从创建一个合适的方面开始:
#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';
}
有点难看的是,必须imbue()
习惯std::locale
使用custom
机械手。为了摆脱这种情况,我们可以确保在 used 中安装了自定义 facet std::locale
,如果没有,则在设置标志时安装它:
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
各种类型和 with正常工作,long long
但这留作练习。