0

I would like to have some facility which makes a std::ostream (or derived) automatically ident on encountering special characters (or special objects). Let's assume that the special characters are < and >. In this case the following input test0<test1<test2, test3<test4> > > should produce the following output:

test0<
    test1<
        test2,
        test3<
            test4
        >
    >
>

How would one go to implement this?

Frank Puck
  • 467
  • 1
  • 11
  • should it detect those characters by itself or indent should be forced by call of some function? Is it enough to change behavior when single character is output on stream? – Marek R Jan 31 '22 at 16:52
  • @MarekR The only restriction is that normal formatting of std::ostream should continue working. – Frank Puck Jan 31 '22 at 16:56
  • 1
    [boost iostreams](https://www.boost.org/doc/libs/1_78_0/libs/iostreams/doc/guide/filtering_streams.html) might help – Alan Birtles Jan 31 '22 at 17:41
  • Does this help? https://stackoverflow.com/a/1397588/14065 – Martin York Jan 31 '22 at 17:47

2 Answers2

1

boost::iostreams makes this fairly easy, you can define filters then chain them together with an output stream to transform the input to the desired output:

#include <iostream>
#include <boost/iostreams/filtering_stream.hpp>

namespace io = boost::iostreams;

struct QuoteOutputFilter {
  typedef char                   char_type;
  typedef io::output_filter_tag  category;

  int indent = 0;

  template<typename Sink>
  bool newLine(Sink& snk)
  {
    std::string str = "\n" + std::string(indent * 4, ' ');
    return io::write(snk, str.c_str(), str.size());
  }

  template<typename Sink>
  bool put(Sink& snk, char c)
  {
    switch (c)
    {
      case '<':
        io::put(snk, c);
        indent += 1;
        return newLine(snk);
      case ',':
        io::put(snk, c);
        return newLine(snk);
      case '>':
        indent -= 1;
        newLine(snk);
        return io::put(snk, c);
      default:
        return io::put(snk, c);
    }
  }
};

int main()
{
  io::filtering_ostream out;
  out.push(QuoteOutputFilter());
  out.push(std::cout);

  out << "test0<test1<test2, test3<test4> > >";
}
Alan Birtles
  • 32,622
  • 4
  • 31
  • 60
0

std::ostream? The IOStream design is rather complicated (and a tad slow), so I can see why you'd think this. The name ostream sounds like a good hint, but it really doesn't do much. It really works by binding std::ostreambuf and operator<< overloads together. The derived ostream classes provide concrete streambufs.

What you'd want is likely a streambuf filter. You grab the underlying streambuf, and use that as an output, but you insert some extra spaces when you see a >

MSalters
  • 173,980
  • 10
  • 155
  • 350