Topic: ANSI ostringstream breaks idiom
Author: Christopher Eltschka <celtschk@physik.tu-muenchen.de>
Date: 1997/09/29 Raw View
J. Kanze wrote:
[...]
> The temporary is not the problem here. In the above line, what you are
> passing is the return value of the last operator<<. All operator<<'s
> return an ostream&. This is, by definition, an lvalue, and so can bind
> to your reference. But it is NOT an ostringstream -- it is an ostream.
> (For type checking purposes, of course. Its dynamic type is still
> ostringstream, and any virtual functions, like the destructor, will
> still act on the ostringstream.
>
> Although somewhat a pain, in this case, the only solution I've found is
> to declare the function to take an ostream&, and dynamic_cast it back up
> to the desired stream type in the function. Of course, this means that
> any errors will only be caught at runtime (in the dynamic_cast), and not
> at compile time.
What about the following solution:
#include <iostream.h>
#include <strstream.h>
template<class OSTREAM> class ostream_wrapper
{
OSTREAM& os;
public:
ostream_wrapper(OSTREAM& ost): os(ost) {}
operator OSTREAM&() { return os; }
ostream_wrapper& lvalue() { return *this; }
};
template<class OSTREAM, class T> inline
ostream_wrapper<OSTREAM>& operator<<(ostream_wrapper<OSTREAM>& o, const
T& t)
{
ostream& oo(o);
oo << t;
return o;
}
template<class OSTREAM> inline
ostream_wrapper<OSTREAM>& operator<<(ostream_wrapper<OSTREAM>& o,
ostream&(*f)(ostream&))
{
ostream& oo(o);
oo << f;
return o;
}
template<class OSTREAM> ostream_wrapper<OSTREAM> wrapos(OSTREAM& o)
{
return o;
}
void f(ostrstream& ost)
{
cout << ost.str() << endl;
ost.freeze(0);
}
int main()
{
ostrstream s;
f(wrapos(s).lvalue() << "Testing the wrapper..." << ends);
return 0;
}
This should be compile-time typesafe, and should have less overhead than
dynamic_cast (a good optimizer might even reduce it to zero).
---
[ 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 ]
[ FAQ: http://reality.sgi.com/employees/austern_mti/std-c++/faq.html ]
[ Policy: http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu ]
Author: kanze@gabi-soft.fr (J. Kanze)
Date: 1997/09/25 Raw View
dent@highway1.com.au (Andy Dent) writes:
|> The following code shows two issues:
|> 1) the new ANSI libs break the idiom oss << os.rdbuf() where
|> ostream os;
|> ostringstream oss;
In what way? Obviously, since ostringstream didn't exist before, it
can't break anything concerning it. But there is still an
ostream::operator<<( streambuf* ), and rdbuf still returns a streambuf*.
There is no reason why "oss << os.rdbuf()" should not work. It should
copy the input stream of the streambuf to the ostringstream.
Perhaps this is your problem: what is the input stream of a streambuf
connected to an ostream. It all depends. What is the type of
streambuf, and what has been done with it since creation. Thus, for
example, when opening a file on a filebuf, you must specify input
(ios::in) and/or output (ios::out). If the ostream is in fact an
ofstream, which you have created without specifying flags, the default
flags are (ios::out | ios::trunc) only -- input will result in an
immediate end of file.
Normally, of course, if you are going to read from a stream, as above,
you will not create it using ofstream. You will create a filebuf
manually, open it with the modes you want, and attach it to the ostream
or istream as needed. (In this case, you would probably pass just the
streambuf* to the function, rather than the ostream.)
|> 2) I can't pass a temporary ostringstream into an ostringstream& parameter
|> but it will accept it for an ostream& parameter. I have no idea why this
|> occurs. I didn't think there was anything lexically different between
|> declaring a no-parameters variable on one line and using the same
|> constructor inline to declare an anonymous variable.
You cannot pass a temporary to a non-const reference, independant of the
type. This is a change, but it is an old change (pre 1991, at least,
since it is already in the ARM).
The exact rule is that a non-const reference can only bind to an
lvalue. You can also call member functions, including non-const member
functions, on a non lvalue. So you get the curious behavior:
ostream& operator<<( ostream& o , MyType const& obj ) ;
MyType x ;
ostringstream() << x ; // Illegal, since the temp cannot bind
// to the first argument of operator<<.
ostringstream() << "" << x ;// Legal, since:
// 1. the first operator<< is a member
// functions, and
// 2. it returns a reference, which is,
// by definition, an lvalue.
Really intuitive, isn't it?
|> /*
|> * test program for two suspected streams bugs
|> * or a hell of a lot of confusion induced by the ANSI libs
|> * Contact Andy Dent mailto:dent@highway1.com.au
|>
|> Written to run under CodeWarrior Pro and Visual C++ v5
|> */
|>
|> #include <iostream>
|>
|> #ifndef MSIPL_IOSTREAM_H
|> #ifdef __MWERKS__
|> #define OLD_ANSI_LIBS
|> #endif
|> #endif
|>
|> #ifdef OLD_ANSI_LIBS
|> #include <strstream>
|> typedef ostrstream ostringstream;
|> #endif
|>
|> #ifdef _MSC_VER
|> #include <sstream>
|> using namespace std;
|> #endif
|>
|> void testBufCopy(ostream&);
|> void testPassStream(ostringstream&);
|>
|> void
|> testBufCopy(ostream& os)
|> {
|> ostringstream oss;
|> #ifdef OLD_ANSI_LIBS
|> // Obsolete libs (incorrectly) didn't define an operator<< that took a
|> pointer to a buffer
|> oss << *(os.rdbuf()) << " and a bit on the end" << ends; // old version works
|> #else
|> // MSL version - also fails with Visual C++ which leads me to conclude
|> // that the problem is generic to the new ANSI libraries
|> oss << os.rdbuf() << " and an MSL bit on the end"; // the rdbuf not copied!
If the input stream of the streambuf is not copied, it is an error. But
see above. Where did the streambuf come from, and what is its input
stream?
|> // as we have only an output sequence, the gptr() is null, BUT this
|> idiom used to work
|> // with ostrstream getting a buffer from an ostream
|> #endif
|>
|> cout << "Dumping the buf contents after copy:\n" << oss.str();
|> }
|>
|>
|> void
|> testPassStream(ostringstream& oss)
|> {
|> #ifdef OLD_ANSI_LIBS
|> oss << ends; // the MSL version of str() returns a terminated string,
|> but not the old version
|> #endif
|> cout << "\n\nDumping the buf contents as passed:\n" << oss.str();
|> }
|>
|>
|> void main(void)
|> {
|> cout << "Andy's test of iostreams!\n\n";
|>
|> // why on earth can I pass a temporary ostringstream to an ostream&
|> // but get compiler errors if passing to an ostringstream& ???
|> testBufCopy(ostringstream() << "This should appear on the screen " << 42);
The temporary is not the problem here. In the above line, what you are
passing is the return value of the last operator<<. All operator<<'s
return an ostream&. This is, by definition, an lvalue, and so can bind
to your reference. But it is NOT an ostringstream -- it is an ostream.
(For type checking purposes, of course. Its dynamic type is still
ostringstream, and any virtual functions, like the destructor, will
still act on the ostringstream.
Although somewhat a pain, in this case, the only solution I've found is
to declare the function to take an ostream&, and dynamic_cast it back up
to the desired stream type in the function. Of course, this means that
any errors will only be caught at runtime (in the dynamic_cast), and not
at compile time.
An alternative would be to use "cout << oss.rdbuf()" in the function,
rather than oss.str(); this does not require the cast, but of course, as
mentioned above, it silently fails if the actual streambuf is output
only. (This is what I actually did, but in my case, the output was to a
non-essential log, so silently failing didn't really harm much, and my
compiler didn't support dynamic_cast -- an old style cast followed by
calling ostrstream::str() would probably have resulted in a core dump if
the stream wasn't of the correct type. In general, I think that this is
one of the rare cases where the use of dynamic_cast is the preferred
solution.)
|> // the following form works for all compilers
|> ostringstream X;
|> X << "This should appear on the screen " << 42;
|> testPassStream(X);
It works because you are passing an actual ostringstream, declared as
such (and not an ostream& which happens to refer to an ostringstream).
|> /* generator of compiler error - haven't a CLUE why it fails and the
|> above works */
|> testPassStream(ostringstream() << "This should appear on the screen "
|> << 42);
See above. Here, you are passing the return value of an operator<<.
This is an ostream&. As it happens, the reference does refer to an
actual ostringstream, but there is no way for the compiler to know this,
at least in general.
|> Visual C++ error
|> D:\test\teststreams\teststreams.cpp(64) : error C2664: 'testPassStream' :
|> cannot convert parameter 1 from 'class std::basic_ostream<char,struct
|> std::char_traits<char>>' to 'class std::basic_ostringstream<char,struct
|> std::char_traits<char>,class std:
|>
|>
|> MSL Error : cannot convert
|> 'basic_ostream<char, char_traits<char>>' to
|> 'basic_ostringstream<char, char_traits<char>>'
|> teststreams.cp line 34 testPassStream(ostringstream() << "This should
|> appear on the screen" << 42);
|>
|>
|> Obsolete error:
|> Error : cannot convert
|> 'ostream' to
|> 'ostrstream'
|> teststreams.cp line 41 testPassStream(ostringstream() << "This should
|> appear on the screen " << 42);
|>
|>
|> */
|> }
I'm sorry that I don't have a typesafe solution for passing the
stringstream; I could have used it myself.
For the problem outputting the rdbuf, I'd suggest using a bidirectional
stringstream instead of an ostringstream. Or just using the str()
function to read the characters.
--
James Kanze +33 (0)1 39 23 84 71 mailto: kanze@gabi-soft.fr
GABI Software, 22 rue Jacques-Lemercier, 78000 Versailles, France
I'm looking for a job -- Je recherche du travail
---
[ 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 ]
[ FAQ: http://reality.sgi.com/employees/austern_mti/std-c++/faq.html ]
[ Policy: http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu ]
Author: dent@highway1.com.au (Andy Dent)
Date: 1997/09/23 Raw View
The following code shows two issues:
1) the new ANSI libs break the idiom oss << os.rdbuf() where
ostream os;
ostringstream oss;
2) I can't pass a temporary ostringstream into an ostringstream& parameter
but it will accept it for an ostream& parameter. I have no idea why this
occurs. I didn't think there was anything lexically different between
declaring a no-parameters variable on one line and using the same
constructor inline to declare an anonymous variable.
/*
* test program for two suspected streams bugs
* or a hell of a lot of confusion induced by the ANSI libs
* Contact Andy Dent mailto:dent@highway1.com.au
Written to run under CodeWarrior Pro and Visual C++ v5
*/
#include <iostream>
#ifndef MSIPL_IOSTREAM_H
#ifdef __MWERKS__
#define OLD_ANSI_LIBS
#endif
#endif
#ifdef OLD_ANSI_LIBS
#include <strstream>
typedef ostrstream ostringstream;
#endif
#ifdef _MSC_VER
#include <sstream>
using namespace std;
#endif
void testBufCopy(ostream&);
void testPassStream(ostringstream&);
void
testBufCopy(ostream& os)
{
ostringstream oss;
#ifdef OLD_ANSI_LIBS
// Obsolete libs (incorrectly) didn't define an operator<< that took a
pointer to a buffer
oss << *(os.rdbuf()) << " and a bit on the end" << ends; // old version works
#else
// MSL version - also fails with Visual C++ which leads me to conclude
// that the problem is generic to the new ANSI libraries
oss << os.rdbuf() << " and an MSL bit on the end"; // the rdbuf not copied!
// as we have only an output sequence, the gptr() is null, BUT this
idiom used to work
// with ostrstream getting a buffer from an ostream
#endif
cout << "Dumping the buf contents after copy:\n" << oss.str();
}
void
testPassStream(ostringstream& oss)
{
#ifdef OLD_ANSI_LIBS
oss << ends; // the MSL version of str() returns a terminated string,
but not the old version
#endif
cout << "\n\nDumping the buf contents as passed:\n" << oss.str();
}
void main(void)
{
cout << "Andy's test of iostreams!\n\n";
// why on earth can I pass a temporary ostringstream to an ostream&
// but get compiler errors if passing to an ostringstream& ???
testBufCopy(ostringstream() << "This should appear on the screen " << 42);
// the following form works for all compilers
ostringstream X;
X << "This should appear on the screen " << 42;
testPassStream(X);
/* generator of compiler error - haven't a CLUE why it fails and the above works
testPassStream(ostringstream() << "This should appear on the screen " << 42);
Visual C++ error
D:\test\teststreams\teststreams.cpp(64) : error C2664: 'testPassStream' :
cannot convert parameter 1 from 'class std::basic_ostream<char,struct
std::char_traits<char>>' to 'class std::basic_ostringstream<char,struct
std::char_traits<char>,class std:
MSL Error : cannot convert
'basic_ostream<char, char_traits<char>>' to
'basic_ostringstream<char, char_traits<char>>'
teststreams.cp line 34 testPassStream(ostringstream() << "This should
appear on the screen" << 42);
Obsolete error:
Error : cannot convert
'ostream' to
'ostrstream'
teststreams.cp line 41 testPassStream(ostringstream() << "This should
appear on the screen " << 42);
*/
}
--
Andy Dent, Software Designer, A.D. Software, Western Australia
OOFILE - "the cross-platform OODBMS that speaks c++"
ftp://ftp.highway1.com.au/pub/adsoftware/
http://www.highway1.com.au/adsoftware/
---
[ 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 ]
[ FAQ: http://reality.sgi.com/employees/austern_mti/std-c++/faq.html ]
[ Policy: http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu ]