Re: Ohh, stream buffer(?) - aka indentation
{ Please don't quote the clc++m banner or any other extraneous material,
and please format your article to fit in 70 columns or so. -mod }
"White Wolf" <wolof@freemail.hu> wrote in message
news:frnsuv$und$1@news.al.sw.ericsson.se...
Hi All,
I'm back (sort of). I am thinking about creating a special stream-thing
for
program execution tracing. Can be good for demonstration and to
understand
why/how an algorithm brings a certain result. (I have made the Rexx PARSER
VAR command work in C++, with the standard algorithm, and sometimes it
brings surprising results.)
One of the many things I would like to support in this tracing stream-like
thing is indentation levels. So if I enter a function (or print the
content
of a container) I would get the inside 2 spaces indented, and so on, if I
call functions from this one or my container has another container as a
member etc. So the indentation level grows as I go "inside" and shrinks
as
I go outside.
Since I want my stream to work on top of any existing ostream derivative,
the indentation support should be a "wrapper" of some kind. And here
comes
the dilemma: wrap what? Wrap the ostream (pointer)? Or wrap the stream
buffer? Is this kind of indentation formatting (so it should go to the
ostream) or something else?
I know many of you guys have working experience in fiddling with standard
streams, so I hope you can point me into the right direction.
Hi,
I'm not sure whether this is the sort of thing you're after, but it's the
best I could come up with off the top of my head. The basic way it works is:
* ostream_indent (my wrapper) holds a reference to a std::ostream, to which
it forwards all the real work
* It stores a bool (m_indentNecessary) indicating whether we're at the start
of a new line. If we are, the requisite number of spaces (stored in
m_indent) will be output and m_indentNecessary will be set to false.
* If we output a newline ('\n') anywhere, m_indentNecessary is set to true.
(I've tried to cover as many obvious cases as I could, like single chars,
string literals and std::strings, but there are doubtless others...)
* ostream_indent is passed by *value* to functions - the copy refers to the
same std::ostream, but has its m_indent value incremented by the specified
IndentAmount. The indentation reverts to whatever it was before when the
function exits, since then a different ostream_indent object (i.e. the one
we were using outside the function) will be being used instead.
Caveat: There may be much better ways of achieving this - I'm curious to see
what other people suggest :-)
Regards,
Stu
~~~~~~
#include <iostream>
#include <string>
template <int IndentAmount>
class ostream_indent
{
private:
std::ostream& m_os;
bool m_indentNecessary;
unsigned int m_indent;
public:
ostream_indent(std::ostream& os)
: m_os(os),
m_indent(0),
m_indentNecessary(true)
{}
ostream_indent(const ostream_indent& rhs)
: m_os(rhs.m_os),
m_indent(rhs.m_indent + IndentAmount),
m_indentNecessary(rhs.m_indentNecessary)
{}
private:
template <int N, typename T>
struct OutputterBase
{
ostream_indent<N>& output(ostream_indent<N>& os_i, const T& t)
{
std::ostream& os = os_i.m_os;
if(os_i.m_indentNecessary)
{
for(unsigned int i=0; i<os_i.m_indent; ++i)
{
os << ' ';
}
os_i.m_indentNecessary = false;
}
os << t;
return os_i;
}
ostream_indent<N>& output_nullterminated_string(ostream_indent<N>& os_i, const char *p)
{
const char *q = p;
while(*q != '\0')
{
os_i << *q;
++q;
}
return os_i;
}
};
template <int N, typename T>
struct Outputter : public OutputterBase<N,T>
{
ostream_indent<N>& operator()(ostream_indent<N>& os_i, const T& t)
{
return output(os_i, t);
}
};
template <int N>
struct Outputter<N,char> : public OutputterBase<N,char>
{
ostream_indent<N>& operator()(ostream_indent<N>& os_i, const char& c)
{
if(c == '\n')
{
os_i.m_indentNecessary = true;
os_i.m_os << '\n';
return os_i;
}
else return output(os_i, c);
}
};
template <int N>
struct Outputter<N,const char *> : public OutputterBase<N,const char*>
{
ostream_indent<N>& operator()(ostream_indent<N>& os_i, const char *p)
{
return output_nullterminated_string(os_i, p);
}
};
template <int N,int K>
struct Outputter<N,const char[K]> : public OutputterBase<N,const char[K]>
{
ostream_indent<N>& operator()(ostream_indent<N>& os_i, const char *p)
{
return output_nullterminated_string(os_i, p);
}
};
template <int N>
struct Outputter<N,std::string> : public OutputterBase<N,std::string>
{
ostream_indent<N>& operator()(ostream_indent<N>& os_i, const std::string& s)
{
return output_nullterminated_string(os_i, s.c_str());
}
};
public:
template <int N, typename T>
friend ostream_indent<N>& operator<<(ostream_indent<N>& os_i, const T& t)
{
return Outputter<N,T>()(os_i, t);
}
};
void g(ostream_indent<2> os_i)
{
os_i << "This is a slightly\ntrickier test\nif you ask me\n";
}
void f(ostream_indent<2> os_i)
{
os_i << "Blah";
os_i << "Woof";
os_i << '\n';
os_i << std::string("Wibble\n");
os_i << "Fluff\n";
g(os_i);
}
int main()
{
ostream_indent<2> os_i(std::cout);
f(os_i);
return 0;
}
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]