Writing a Manipulator for a Custom Stream Class

I wrote my own stream class that prints indented text and has manipulators that can change the level of indentation. All indentation is done in the user stream buffer used by the stream class. The buffer works (i.e. the text is indented at the output), but I can not get my manipulators to work. I read in many places how ostream (which is distributed by my class) overloads the operator <for example:

ostream& ostream::operator << ( ostream& (*op)(ostream&)) { // call the function passed as parameter with this stream as the argument return (*op)(*this); } 

This means that it can take a function as a parameter. So why are my indent or deindent stream functions not recognized? I'm sure I need to do some overloading of the <<operator, but do I need to do this? Below is the code of 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; } 

Thanks!

+1
source share
1 answer

Your manipulator should be declared as a function that takes only one argument of type ostream& . However, if you make it a member function, you know that the implicit this argument is passed to the function.

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

 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; } 

Alternatively, if you really want your function to be a member, you can 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 will 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; } 
+4
source

Source: https://habr.com/ru/post/985204/


All Articles