Topic: Manipulator problem with ofstream subclass


Author: Christopher Eltschka <celtschk@physik.tu-muenchen.de>
Date: 1999/11/18
Raw View
Heath Rice wrote:
>
> Hi all,
>
> I am finally starting to work on a "trace" output stream class that I've
> had in the back of my head for a year or so now.  It derives from
> ofstream and adds things like tracing levels, file wrap (or historic
> copies), etc.  Anyway, I was trying to add manipulators that were
> specific to my new stream class (call it TraceStream or TS for short).
> When I added the line
>
> TS& operator<<(TS& (*_f)(TS&));
>
> in my TL class definition, all sorts of funky things started to happen.
> All of the other operator<< from ostream (for int, char *, etc) stopped
> being visible.  The compiler spit out things like
>
> Error: The operation "TraceStream << int" is illegal.
>
> and
>
> Error: Trying to take the address of the overloaded function endl.
>
> in the places where I was trying to use instances of my new class.

Obviously your compiler implements operator<< as members.
Your operator<< hides the base class versions.
You can avoid this by using a global (maybe friend) operator<<.

BTW, you'll find other surprises even then:
Code like

TS& foo(TS&);

int main()
{
  TS somestream;
  TS << "foo" << foo;
}

will not work. That's because the return type of
ostream::operator<<(char const*) is _not_ TS&, but ostream&,
and there's no operator<< taking ostream and TS&(*)(TS&).

In short: Adding specific operator<< to derived streams doesn't
work well at all.

A possible solution would be to make your TS detectable through
the ostream interface (f.ex. through xalloc/iword), and make
your TS manipulators just ostream manipulators which check if
they are really working on a TS, and only then do their work
(possibly through static_cast<TS&>(the_stream) or similar).

Not ideal, but I don't know any better solution.

>
> I played around a little and it happens anytime I add any new
> operator<<, no matter what type the operator takes, at the TS level.  I am
> able to add other operators (like []); just not <<.

The point is that ostream has no operator[] which you might hide.
But try to add void rdbuf(double*), and then try to get the
underlying streambuf...

[...]


[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: Dietmar Kuehl <dietmar.kuehl@claas-solutions.de>
Date: 1999/11/19
Raw View
Hi,
In article <80pp3s$t2c$1@nntp1.atl.mindspring.net>,
  hrice@netcom.com (Heath Rice) wrote:
> I am finally starting to work on a "trace" output stream class that I've
> had in the back of my head for a year or so now.  It derives from
> ofstream and adds things like tracing levels, file wrap (or historic
> copies), etc.

This approach will almost certainly fail to deliver what you want: You
should never derive from 'std::basic_istream' or 'std::basic_ostream'
for a different purpose than convenient construction of a stream
initialized with a special stream buffer and convenient access to
methods specific to such a stream buffer. Depending on what you want to
do you would use various different appraoches. To add formatting flags,
eg. for user defined types, you would use 'std::ios_base::xalloc()' and
family ('pword()', 'iword()', 'register_callback()',...). To create
modified I/O behavior you would derive a new stream buffer (from the
class 'std::basic_streambuf'). To change the behavior of formatting for
(at least some) built-in types you would derive from the corresponding
numeric facet and install a 'std::locale' object with the new facet
being installed.

In any case, you would derive from the stream classes only for
convenience.

>  Anyway, I was trying to add manipulators that were
> specific to my new stream class (call it TraceStream or TS for short).

There is no such thing as manipulator which are specific to some
derived stream class. The reason is simply that the corresponding type
is lost as soon as you have used one of the normal I/O operators on the
stream because these all these I/O operators return a reference to
'std::basic_istream<...>' or 'std::basic_ostream<...>'.

Thus, you have to arrange for your manipulators to work on all streams,
potentially using the stuff they find to evaluate their suitability for
the stream on which they are applied. For example, a 'dynamic_cast' can
be used to determine whether the stream really uses a specific type as
stream buffer and then to call a specific function on this stream
buffer.

> When I added the line
>
> TS& operator<<(TS& (*_f)(TS&));
>
> in my TL class definition, all sorts of funky things started to
happen.

Sure. You are not supposed to derive from the stream classes in the
first place. In particular, you should not try to overload the output
operator because it will hide the versions in the base class. The same
thing happens for all function names overloaded in a derived class. You
might try to fiddle with a 'using std::basic_ostream<...>::operator<<;'
or something like this but this will come back on you anyway: Even
assuming this works, you would then have problem with this:

  TS &mymanipulator(TS& s) { return s; }
  void f(TS& s) {
    s << 17 << mymanipulator;
  }

because the static type used in the overload resolutions resulting from
the expression 's << 17' is 'std::basic_ostream<...>&' and not 'TS&'
although the dynamic type is, of course, still 'TS&'.

> Any ideas?  I can give more detail if I need to.  I didn't want to bore
> everyone if my screwup was so obvious that it didn't require pages of
> description.

The way to go is probably to have a filtering stream buffer (ie. one
which uses some other stream buffer to do the actual work) which
maintains a trace level and does not do any processing depending on the
combination of the current trace level and the trace messages being
written. Also, the constructor of this class can handle the renaming
of the file names if this is desired.

Setting the trace level using corresponding manipulators would eg. use
code like this:

  class tracebuf: public std::basic_streambuf<char> {
  public:
    void setTraceLevel(int l);
    // ...
  };

  std::basic_ostream<char>& debug(std::basic_ostream<char>& out) {
    if (tracebuf* buf = dynamic_cast<tracebuf*>(out.rdbuf())
      buf->setTraceLevel(5);
    return out;
  }

For easy creation of a corresponding 'std::ostream' you would then
derive from 'ostream' initializing the base object. The derive class
would do nothing else than maintaining the appropriate stream buffer.
--
<mailto:dietmar.kuehl@claas-solutions.de>
homepage: <http://www.informatik.uni-konstanz.de/~kuehl>


Sent via Deja.com http://www.deja.com/
Before you buy.


      [ Send an empty e-mail to c++-help@netlab.cs.rpi.edu for info ]
      [ about comp.lang.c++.moderated. First time posters: do this! ]

[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: hrice@netcom.com (Heath Rice)
Date: 1999/11/15
Raw View
Hi all,

I am finally starting to work on a "trace" output stream class that I've
had in the back of my head for a year or so now.  It derives from
ofstream and adds things like tracing levels, file wrap (or historic
copies), etc.  Anyway, I was trying to add manipulators that were
specific to my new stream class (call it TraceStream or TS for short).
When I added the line

TS& operator<<(TS& (*_f)(TS&));

in my TL class definition, all sorts of funky things started to happen.
All of the other operator<< from ostream (for int, char *, etc) stopped
being visible.  The compiler spit out things like

Error: The operation "TraceStream << int" is illegal.

and

Error: Trying to take the address of the overloaded function endl.

in the places where I was trying to use instances of my new class.

I played around a little and it happens anytime I add any new
operator<<, no matter what type the operator takes, at the TS level.  I am
able to add other operators (like []); just not <<.

Any ideas?  I can give more detail if I need to.  I didn't want to bore
everyone if my screwup was so obvious that it didn't require pages of
description.

Thanks,
Heath


[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: Michiel Salters <salters@lucent.com>
Date: 1999/11/16
Raw View
Heath Rice wrote:

> Hi all,

> I am finally starting to work on a "trace" output stream class that I've
> had in the back of my head for a year or so now.  It derives from
> ofstream and adds things like tracing levels, file wrap (or historic
> copies), etc.  Anyway, I was trying to add manipulators that were
> specific to my new stream class (call it TraceStream or TS for short).
> When I added the line

> TS& operator<<(TS& (*_f)(TS&));

> in my TL class definition, all sorts of funky things started to happen.
> All of the other operator<< from ostream (for int, char *, etc) stopped
> being visible.  The compiler spit out things like

> Error: The operation "TraceStream << int" is illegal.

> and

> Error: Trying to take the address of the overloaded function endl.

> in the places where I was trying to use instances of my new class.

> I played around a little and it happens anytime I add any new
> operator<<, no matter what type the operator takes, at the TS level.  I am
> able to add other operators (like []); just not <<.

> Any ideas?  I can give more detail if I need to.  I didn't want to bore
> everyone if my screwup was so obvious that it didn't require pages of
> description.
> Thanks,
> Heath

(Simplified explanation).

I've got enough to "guess" what happened; you wanted to add
operator<<(TS& (*_f)(TS&)) to the "overload set" for
operator<<(...). The overload set is created from all
operator<<()'s at the same 'distance' (needing the
same number of conversions etc.). You place one
operator<< closer, and suddenly all other operators
are second-rank, do not longer participate in overload
resolution.

(Official explanation)
Your program contains no suitable operator<<, according
to the standard.

(Trivial Solution)
Add forwarding functions.

(Non-Trivial Solution)
In general, operator <<() int returns an ostream&, not
an TS&, so defining your operator<< on TS& is not
a good design solution, I think. Could you redesign
it?

Michiel Salters


[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: "J.Barfurth" <techview@bfk-net.de>
Date: 1999/11/16
Raw View

Heath Rice <hrice@netcom.com> schrieb in im Newsbeitrag:
80pp3s$t2c$1@nntp1.atl.mindspring.net...
>
> Hi all,

> TS& operator<<(TS& (*_f)(TS&));
>
> in my TL class definition, all sorts of funky things started to happen.
> All of the other operator<< from ostream (for int, char *, etc) stopped
> being visible.  The compiler spit out things like
Compare your problem to

struct A
{
    int foo(int);
};
struct B : A
{
    void foo();
};
int main()
{
    return B().foo(42);    // wont compile
}
Can you resolve this ?

> Any ideas?  I can give more detail if I need to.  I didn't want to bore
> everyone if my screwup was so obvious that it didn't require pages of
> description.

How about using 'using'.

> Thanks,
> Heath

HTH
-- J   rg Barfurth




[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: hrice@netcom.com (Heath Rice)
Date: 1999/11/16
Raw View
Thanks for the answers.  I would have lost a bet on this one.  I thought
that, since I made my new operator<< have a completely different type
than any of the ones in ostream, there wouldn't be a collision.  I learn
something everyday. ;)

Using 'using' looks perfect, but it doesn't appear that my compiler
supports it.  It doesn't seem to know it as a keyword.  I'm using the
Sun's CC compiler (v4.2).


Heath

 In article <80rf70$hpc$1@news.hamburg.pop.de>,
J.Barfurth <jbarfurth@vossnet.de> wrote:
>
>
>Heath Rice <hrice@netcom.com> schrieb in im Newsbeitrag:
>80pp3s$t2c$1@nntp1.atl.mindspring.net...
>>
>> Hi all,
>
>> TS& operator<<(TS& (*_f)(TS&));
>>
>> in my TL class definition, all sorts of funky things started to happen.
>> All of the other operator<< from ostream (for int, char *, etc) stopped
>> being visible.  The compiler spit out things like
>Compare your problem to
>
>struct A
>{
>    int foo(int);
>};
>struct B : A
>{
>    void foo();
>};
>int main()
>{
>    return B().foo(42);    // wont compile
>}
>Can you resolve this ?
>
>> Any ideas?  I can give more detail if I need to.  I didn't want to bore
>> everyone if my screwup was so obvious that it didn't require pages of
>> description.
>
>How about using 'using'.
>
>> Thanks,
>> Heath
>
>HTH
>-- J   rg Barfurth


[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]