粘性自定义流操纵器

pac*_*c42 12 c++

如何实现我自己的自定义流操纵器以使其粘滞.例如,我想将整数转换为二进制,以便:

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

会回来:

decimal of 4: 4
decimal of 4: 4
binary of 4: 100
binary of 4: 100
decimal of 4: 4
decimal of 4: 4
Run Code Online (Sandbox Code Playgroud)

Die*_*ühl 17

做一切事情有点牵扯.为了使其易于理解,我将从基本的东西开始:为用户定义的类型使用自定义格式标记.整数的自定义格式将如下所示.

iostream类派生[间接]从std::ios_base其中提供了两个存储用于数据:std::ios_base::iword()std::ios_base::pword()用于intS和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';
}
Run Code Online (Sandbox Code Playgroud)

现在,intlike 4不是用户定义类型,并且已经为这些类型定义了输出运算符.幸运的是,您可以使用facet自定义整数的格式,更具体地说是使用std::num_put<char>.现在,为此,您需要执行以下步骤:

  1. 从中派生类std::num_put<char>并覆盖do_put()要为其提供特殊行为的成员.
  2. std::locale使用新创建的构面创建对象.
  3. 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';
}
Run Code Online (Sandbox Code Playgroud)

有点丑陋的是,imbue()自定义std::locale需要使用custom操纵器.为了摆脱这种情况,我们可以确保自定义facet安装在used中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;
}
Run Code Online (Sandbox Code Playgroud)

现在剩下的就是覆盖不同的do_put()成员以适应unsigned各种类型并使用long long但是这仍然是一个练习.