manipulators for filtering_stream s

Surprisingly I was not able to find on the net code that would give me a smart way to indent a stream. So I wrote a little filter based on the boost iostreams library. My first attempt was: boost::iostreams::filtering_ostream out; indent_filter ind(4); out.push(ind); out.push(std::cout); However trying to control it via e.g. ind.in() ind.out() turns out to be problematic interface since, 1) out.push() takes a copy of ind 2) the interface ind.in() is suboptimal. With my attached example it is possible to: #include "indent.hpp" ... boost::iostreams::filtering_ostream out; indent_filter::push(out,2); out.push(std::cout); ... And use it like so: out << "Hello Filter!\n" << indent_in << "this is\n" << "indented\n" << indent_out << "until here\n" ; Which will result in output: Hello Filter! this is indented until here The state is stored in the ios base class of the stream, by way of the pword mechanism. Consequently the filter stream even may be downcast to a basic_ostream, and the indenter will still work. E.g. with out defined as above: ... foo(out); ... void foo(std::ostream& out) { out << indent_in << "out is a downcast filter_stream\n" << indent_out << "end of indentation\n" ; } If you want to try it out, you just need to include the header indent.hpp. If someone thinks the snippet is usable either as an example for the iostreams library or the filter streams manipulator idea is worth generalizing to make it usable for other filters too, I would be glad to hear from you. -- _________________________________________ _ _ | Roland Schwarz |_)(_ | aka. speedsnail | \__) | mailto:roland.schwarz@chello.at ________| http://www.blackspace.at

On 02/29/08 09:32, Roland Schwarz wrote:
A long time ago, I had one and was intending *eventually* to add it to iostreams, but never got around to it. It's in: http://svn.boost.org/trac/boost/browser/sandbox-branches/cppljevans/boost/io... It was used, with g++'s demangler and boost's spirit, to produce more readable demangled names as demonstrated by: http://sourceforge.net/mailarchive/message.php?msg_id=fmqia0%24kak%241%40ger... OOPS. It doesn't appear the way it should there, but it did appear properly indented in my mail reader. In short, the stream has an operator++ and operator-- as well as operator+= and operator-= which increment the indentation and decrement the indentation. I've attached the spirit code which, given the demangled name from g++'s demangler, produces the indented output. The indentable stream is the str_tree_type_name_parser_print::my_out member variable.

Larry Evans wrote:
Thank you. Al though google revealed some postings about margin_ostream it failed to get me to the sources. I am not sure however you noticed the difference to my implementation: I am storing state _in_ the stream, i.e. in ios base member and thus allowing out << indent_in << "blah blah\n"; out << "still indented\n"; And a function void foo(ostream& os) that gets passed down my indent_filter'ed stream still knowns the indentation status. The margin_ostream implements indent operation only the stream object. Does is also have manipulators? Will margin_ostream also work when downcasted to a plain ostream? -- _________________________________________ _ _ | Roland Schwarz |_)(_ | aka. speedsnail | \__) | mailto:roland.schwarz@chello.at ________| http://www.blackspace.at

On 03/01/08 04:58, Roland Schwarz wrote:
The above http reference should show the sources. I must be misunderstanding you :(. I haven't used svn to download it recently, but I guess using a command similar to that shown here: http://svn.boost.org/trac/boost/wiki/BoostSubversion , except with the appropriate directory substituted for trunk, would work.
Ah! That sounds better than marg_ostream. It uses, IIRC, some sort or wrapper around an ostream to do its work. This looks better to me (than marg_ostream).
No. indent.hpp is beginning to sound better and better. One application I had for marg_ostream (actually for an earlier version back in mid-late 90's) was indenting fortran code with the additional notation in the margins showing the arrows reflecting the goto's. marg_ostream has a string member function containing the margin contents which, AFAICT, can be used for this type of notation. I think it's a minor feature but still could be useful. How could indent.hpp be adapted to do this or would there have to be some derivation from some class to provide this feature. For example, another use would be putting line numbers in the 1st few characters of the margin so that the output appears something like that shown in the svn browser here: http://svn.boost.org/trac/boost/browser/sandbox-branches/cppljevans/boost/io... I'll be trying out indent.hpp after I'm done with the proto review. Thanks for the contribution. -regards, Larry

On 03/01/08 07:52, Larry Evans wrote: [snip]
I'll be trying out indent.hpp after I'm done with the proto review.
I couldn't resist trying it now; however, I'm getting wrong output :( The attached test driver (indent_test.cpp) is the same as the one used for marg_ostream but modified to use indent.hpp as appropriate. The expected output is shown in test_mout.cpp.out. The actual output is shown in indent_test.cpp.out. How can I get the expected output with indent.hpp? -regards, Larry cd /home/evansl/prog_dev/boost-root.ln/boost_dev/libs/io/filters/tests/ make run g++-3.3 -g -c -I../../../.. -o test_mout.o test_mout.cpp g++-3.3 -v -o test_mout.exe test_mout.o ../../../../libs/io/filters/src/mout.o Reading specs from /usr/lib/gcc-lib/i486-linux/3.3.2/specs Configured with: ../src/configure -v --enable-languages=c,c++,java,f77,pascal,objc,ada,treelang --prefix=/usr --mandir=/usr/share/man --infodir=/usr/share/info --with-gxx-include-dir=/usr/include/c++/3.3 --enable-shared --with-system-zlib --enable-nls --without-included-gettext --enable-__cxa_atexit --enable-clocale=gnu --enable-debug --enable-java-gc=boehm --enable-java-awt=xlib --enable-objc-gc i486-linux Thread model: posix gcc version 3.3.2 (Debian) /usr/lib/gcc-lib/i486-linux/3.3.2/collect2 --eh-frame-hdr -m elf_i386 -dynamic-linker /lib/ld-linux.so.2 -o test_mout.exe /usr/lib/gcc-lib/i486-linux/3.3.2/../../../crt1.o /usr/lib/gcc-lib/i486-linux/3.3.2/../../../crti.o /usr/lib/gcc-lib/i486-linux/3.3.2/crtbegin.o -L/usr/lib/gcc-lib/i486-linux/3.3.2 -L/usr/lib/gcc-lib/i486-linux/3.3.2/../../.. test_mout.o ../../../../libs/io/filters/src/mout.o -lstdc++ -lm -lgcc_s -lgcc -lc -lgcc_s -lgcc /usr/lib/gcc-lib/i486-linux/3.3.2/crtend.o /usr/lib/gcc-lib/i486-linux/3.3.2/../../../crtn.o ./test_mout.exe line1 line1.1 line1.1.1 line1.2:unsigned=22 line1.3:float=3.1416 line2:a_test=test{my_str=a_test string,my_dbl=9.999} Compilation finished at Mon Dec 22 19:28:37 ...updating 3 targets... gcc.compile.c++ ../../bin.v2/sandbox/RowlandSchwarz/gcc-4.1/debug/link-static/indent_test.o gcc.link ../../bin.v2/sandbox/RowlandSchwarz/gcc-4.1/debug/link-static/indent_test testing.unit-test ../../bin.v2/sandbox/RowlandSchwarz/gcc-4.1/debug/link-static/indent_test.passed line1 line1.1 line1.1.1 line1.2:unsigned=22 line1.3:float=3.1416 line2:a_test=:my_str=a_test string:my_dbl=9.999 (test*)0=(test*)0 &a_test=*(test*)=:my_str=a_test string:my_dbl=9.999 ...updated 3 targets... Compilation finished at Sat Mar 1 09:14:24

Larry Evans wrote:
I couldn't resist trying it now; ;-)
however, I'm getting wrong output :(
Hmm, I did not test it, but from looking at your source: marg_ostream& mout(void) { return std::cout; } gives you back std::cout, while it should hand back ; boost::iostreams::filtering_ostream out The state is stored in out instance of course, not cout. -- _________________________________________ _ _ | Roland Schwarz |_)(_ | aka. speedsnail | \__) | mailto:roland.schwarz@chello.at ________| http://www.blackspace.at

On 03/01/08 09:40, Roland Schwarz wrote:
Right. So, should: <-- cut here -- #include "indent.hpp" typedef boost::iostreams::filtering_ostream marg_ostream; marg_ostream out; #include <iostream> marg_ostream& mout(void) { return out; } //#define DEBUG_PRINT void operator++(marg_ostream& sout) { #ifdef DEBUG_PRINT sout<<"operator++(marg_ostream&)\n"; #endif sout<<indent_in; } void operator--(marg_ostream& sout) { sout<<indent_out; #ifdef DEBUG_PRINT sout<<"operator--(marg_ostream&)\n"; #endif } ... int main(void) { ; indent_filter::push(out,2) ; out.push(std::cout) ; mout()<<"line1\n" ...
-- cut here -- work? With this change, I get compile errors like:
indent_test.cpp: In function 'marg_ostream& operator<<(marg_ostream&, const test*)': indent_test.cpp:60: error: no match for 'operator<<' in 'std::operator<< [with _Traits = std::char_traits<char>](((std::basic_ostream<char, std::char_traits<char>

The attached file works for me, and gives indented output. -- _________________________________________ _ _ | Roland Schwarz |_)(_ | aka. speedsnail | \__) | mailto:roland.schwarz@chello.at ________| http://www.blackspace.at line1 line1.1 line1.1.1 line1.2:unsigned=22 line1.3:float=3.1416 line2:a_test=:my_str=a_test string:my_dbl=9.999 (test*)0=(test*)0 &a_test=*(test*)=:my_str=a_test string:my_dbl=9.999

On 03/01/08 04:58, Roland Schwarz wrote:
[snip]
No.
Will margin_ostream also work when downcasted to a plain ostream?
No. However, I've looked further at indent.hpp and the storing of the pointer to the filter in the ios_base using ios_base::pword. What's the advantage of using ios_base::pword to store the pointer to something that's already available via basic_ios<...>::rd_buf and then filtering_streambuf<...>::component<T>(int): http://www.boost.org/doc/libs/1_37_0/libs/iostreams/doc/classes/filtering_st... This design is implemented in boost vault under the "Input - Output" directory in indent_scoped_ostreambuf.zip.

On 01/21/09 17:26, Larry Evans wrote: [snip]
I believe there is a problem with this rdbuf/component<T>(int) combination that's illustrated by scoped_buf_swapper.zip in the "Input - Output" directory of the vault. The ios_base::pword store of indent_filter works because the filter is found by looking up the unique key of that filter in the ios_base. With the rdbuf/component<T> method, the component<T>(0) will return the 0 if more than one filter has been pushed onto the streambuf.

Hi Roland,
This looks pretty cool. One question : do you intend to use this for more than logging? I'm asking because Boost Logging v3 will certainly include such a feature. Best, John -- http://John.Torjo.com -- C++ expert http://blog.torjo.com ... call me only if you want things done right

Hi John, John Torjo wrote:
This looks pretty cool. One question : do you intend to use this for more than logging?
I am not sure I understand your question. I am not using the indenter for logging at all. But I have to admit that I do not know what is special with logging with respect to indentation.
I'm asking because Boost Logging v3 will certainly include such a feature.
Hmm, what is "such"? Do you mean "indentation" or "manipulators for filtering streams"? -- _________________________________________ _ _ | Roland Schwarz |_)(_ | aka. speedsnail | \__) | mailto:roland.schwarz@chello.at ________| http://www.blackspace.at

Hi Roland,
I would assume you'd need indentation usually for logging. I don't see another scenario, but maybe I'm too shortsighted.
I meant "indentation". Best, John -- http://John.Torjo.com -- C++ expert http://blog.torjo.com ... call me only if you want things done right

On Sun, Mar 02, 2008 at 04:17:46AM +0200, John Torjo wrote:
I used with a Boost.Logging v1 wrapper: namespace { static int logIndentLevel = 0; void prepend_spaces(const boost::logging::logging_types::string &/*log_name*/, boost::logging::logging_types::string & msg) { msg = " " + msg; } } void logging::addLogIndention() { std::ostringstream str; ++logIndentLevel; str << "indentation" << logIndentLevel; using namespace boost::logging; // flush all old messages with previous modifier flush_log_cache(); add_modifier("*", &prepend_spaces, str.str(), DEFAULT_INDEX+5); } void logging::restoreLogIndention() { std::ostringstream str; str << "indentation" << logIndentLevel; --logIndentLevel; ASSERT(logIndentLevel>=0); using namespace boost::logging; // the modifier will be changed soon, sync! flush_log_cache(); del_modifier("*", str.str()); } Both addLogIndention() and restoreLogIndention() are also called in the constructor/destructor of a helper class to ensure a proper reset. Very simple ... Jens

Yes indeed :) Just wanted to point out that it'll be in v3 - simple again :) Best, John -- http://John.Torjo.com -- C++ expert http://blog.torjo.com ... call me only if you want things done right
participants (5)
-
Jens Seidel
-
John Torjo
-
Larry Evans
-
Markus Werle
-
Roland Schwarz