为自定义流类编写操纵器 [英] Writing a manipulator for a custom stream class

查看:139
本文介绍了为自定义流类编写操纵器的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我编写了一个自定义流类,它输出缩进的文本,并具有可以更改缩进级别的操纵器。所有缩进的工作都在一个自定义流缓冲区类中实现,它由流类使用。缓冲区是工作(即文本缩进在输出),但我不能让我的操纵器工作。我在很多地方阅读ostream(我的类扩展)重载操作符<<像这样:

I've written a custom stream class that outputs indented text and that has manipulators that can change the indent level. All of the indenting work is implemented in a custom stream buffer class, which is used by the stream class. The buffer is working (i.e. text is indented in the output), but I can't get my manipulators to work. I was reading in a lot of places how ostream (which my class extends) overloads the operator<< like this:

ostream& ostream::operator << ( ostream& (*op)(ostream&))

{
    // call the function passed as parameter with this stream as the argument
    return (*op)(*this);
}

这意味着它可以将函数作为参数。那么为什么我的缩进或deindent流函数不被识别?我肯定我必须做一些重载的操作符<<,但我不应该不需要?请参阅下面的代码:

Which means it can take in a function as a parameter. So why aren't my "indent" or "deindent" stream functions being recognized? I'm sure I have to do some overloading of operator<<, but shouldn't I not need to? See below for my code:

#include <iostream>
#include <streambuf>
#include <locale>
#include <cstdio>

using namespace std;

class indentbuf: public streambuf {

public:

    indentbuf(streambuf* sbuf): m_sbuf(sbuf), m_indent(4), m_need(true) {}

    int indent() const { return m_indent; }
    void indent() { m_indent+=4; }
    void deindent() { if(m_indent >= 4) m_indent-= 4; }

protected:

    virtual int_type overflow(int_type c) {

        if (traits_type::eq_int_type(c, traits_type::eof()))

            return m_sbuf->sputc(c);

        if (m_need)
        {
            fill_n(ostreambuf_iterator<char>(m_sbuf), m_indent, ' ');
            m_need = false;
        }

        if (traits_type::eq_int_type(m_sbuf->sputc(c), traits_type::eof()))

            return traits_type::eof();

        if (traits_type::eq_int_type(c, traits_type::to_char_type('\n')))

            m_need = true;

        return traits_type::not_eof(c);
    }

    streambuf* m_sbuf;
    int m_indent;
    bool m_need;
};

class IndentStream : public ostream {
public:
    IndentStream(ostream &os) : ib(os.rdbuf()), ostream(&ib){};

    ostream& indent(ostream& stream) {
        ib.indent();
        return stream;
    }

   ostream& deindent(ostream& stream) {
        ib.deindent();
        return stream;
    }

private:
    indentbuf ib;
};

int main()
{
    IndentStream is(cout);
    is << "31 hexadecimal: " << hex << 31 << endl;
    is << "31 hexadecimal: " << hex << 31 << endl;
    is << "31 hexadecimal: " << hex << 31 << deindent << endl;
    return 0;
}

谢谢!

推荐答案

您的操纵器应该声明为一个只接受类型 ostream& code>。然而,如果你使它成为一个成员函数,你知道有一个隐式的这个参数传递给函数。

Your manipulator should be declared as a function which accepts just one argument of type ostream&. However, if you make it a member function, you know there is an implicit this argument being passed to the function as well.

因此,您应该将操作器声明为一个免费的非成员函数,使它 friend 类,以便它可以访问其私有成员 ib

Thus, you should rather declare your manipulator as a free, non-member function, making it friend of your class so that it can access its private member ib:

class IndentStream : public ostream {
public:
    IndentStream(ostream &os) : ib(os.rdbuf()), ostream(&ib){};

    ostream& indent(ostream& stream) {
        ib.indent();
        return stream;
    }

    friend ostream& deindent(ostream& stream);
//  ^^^^^^

private:
    indentbuf ib;
};

ostream& deindent(ostream& stream)
{
    IndentStream* pIndentStream = dynamic_cast<IndentStream*>(&stream);
    if (pIndentStream != nullptr)
    {
        pIndentStream->ib.deindent();
    }

    return stream;
}

int main()
{
    IndentStream is(cout);
    is << "31 hexadecimal: " << hex << 31 << endl;
    is << "31 hexadecimal: " << hex << 31 << deindent << endl;
    is << "31 hexadecimal: " << hex << 31 << endl;
    return 0;
}

或者,如果你真的想让你的函数成为一个成员,它 static:

Alternatively, if you really want your function to be a member, you could make it static:

class IndentStream : public ostream {
public:
    IndentStream(ostream &os) : ib(os.rdbuf()), ostream(&ib){};

    ostream& indent(ostream& stream) {
        ib.indent();
        return stream;
    }

    static ostream& deindent(ostream& stream)
    {
        IndentStream* pIndentStream = dynamic_cast<IndentStream*>(&stream);
        if (pIndentStream != nullptr)
        {
            pIndentStream->ib.deindent();
        }

        return stream;
    }

private:
    indentbuf ib;
};

但是,这将迫使您使用限定名称来引用它:

However, this would force you to use a qualified name to refer to it:

int main()
{
    IndentStream is(cout);
    is << "31 hexadecimal: " << hex << 31 << endl;
    is << "31 hexadecimal: " << hex << 31 << IndentStream::deindent << endl;
    //                                       ^^^^^^^^^^^^^^
    is << "31 hexadecimal: " << hex << 31 << endl;
    return 0;
}

这篇关于为自定义流类编写操纵器的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆