C++自定义流操纵器,用于更改流上的下一个项目

Tri*_*ick 57 c++ stream

在C++中,要以十六进制打印数字,请执行以下操作:

int num = 10;
std::cout << std::hex << num; // => 'a'
Run Code Online (Sandbox Code Playgroud)

我知道我可以创建一个只需要向流添加内容的操纵器:

std::ostream& windows_feed(std::ostream& out)
{
    out << "\r\n";
    return out;
}

std::cout << "Hello" << windows_feed; // => "Hello\r\n"
Run Code Online (Sandbox Code Playgroud)

但是,如何创建一个像"十六进制"一样修改项目中的项目的操纵器?举个简单的例子,我如何在这里创建plusone操纵器?:

int num2 = 1;
std::cout << "1 + 1 = " << plusone << num2; // => "1 + 1 = 2"

// note that the value stored in num2 does not change, just its display above.
std::cout << num2; // => "1"
Run Code Online (Sandbox Code Playgroud)

Joh*_*itb 72

首先,您必须将一些状态存储到每个流中.您可以使用函数iword和传递给它的索引来执行此操作,由下式给出xalloc:

inline int geti() { 
    static int i = ios_base::xalloc();
    return i;
}

ostream& add_one(ostream& os) { os.iword(geti()) = 1; return os; } 
ostream& add_none(ostream& os) { os.iword(geti()) = 0; return os; }
Run Code Online (Sandbox Code Playgroud)

有了这些,您就可以检索所有流中的某些状态.现在,您只需要挂钩相应的输出操作.数字输出由构面完成,因为它可能依赖于区域设置.所以你可以做到

struct my_num_put : num_put<char> {
    iter_type 
    do_put(iter_type s, ios_base& f, char_type fill, long v) const { 
        return num_put<char>::do_put(s, f, fill, v + f.iword(geti())); 
    } 

    iter_type 
    do_put(iter_type s, ios_base& f, char_type fill, unsigned long v) const { 
        return num_put<char>::do_put(s, f, fill, v + f.iword(geti())); 
    } 
}; 
Run Code Online (Sandbox Code Playgroud)

现在,你可以测试一下这些东西.

int main() {
    // outputs: 11121011
    cout.imbue(locale(locale(),new my_num_put));
    cout << add_one << 10 << 11 
         << add_none << 10 << 11;
}
Run Code Online (Sandbox Code Playgroud)

如果您希望只增加下一个数字,只需0在每次调用后再将该单词设置为do_put.

  • +1.这是彻底的,通过书本(并且您可以看到,相当复杂)的解决方案.但我想知道创建一个带有一个参数并返回结果的函数plusone()是否更简单(并且可能更清晰)? (2认同)
  • 很好的阐述! (2认同)

180*_*ION 13

我完全赞同Neil Butterworth的这一点,但是在你使用的特定情况下,你可以完成这个完全可怕的黑客攻击.不要在任何生产代码中执行此操作.它有很多错误.一方面它只适用于上面的单行,它不会改变底层流的状态.

class plusone_stream : public std::ostream
{
  public:
    std::ostream operator<<(int i)
    {
      _out << i+1;
      return *this;
    }
};

std::ostream& plusone(std::ostream& out)
{
    return plusone_stream(out);
}
Run Code Online (Sandbox Code Playgroud)