Re: A std::ostringstream wrapper for on-the-fly streaming - is this
dodgy?
On Aug 5, 11:22 am, James Kanze <james.ka...@gmail.com> wrote:
On Aug 5, 12:46 am, Francesco <entul...@gmail.com> wrote:
On Aug 4, 6:36 pm, Stuart Golodetz
[snip]
I've done a test with my logger class.
Which wraps an ostream, I'd guess.
Among the following 32 STL manipulators:
-------
boolalpha, dec, endl, ends, fixed, flush, hex, internal, left,
noboolalpha, noshowbase, noshowpoint, noshowpos, noskipws, nounitbuf,
nouppercase, oct, resetiosflags, right, scientific, setbase, setfill,
setiosflags, setprecision, setw, showbase, showpoint, showpos, skipws,
unitbuf, uppercase, ws
-------
Only the last one (std::ws) doesn't get handled by the "operator<<
(std::ostream& (*)(std::ostream&))" function - actually, it accepted
also those manipulators that make no sense with ostreams, that would
be "std::skipws" and "std::noskipws", but that seems OK, according toht=
tp://www.cplusplus.com/reference/iostream/manipulators/
On the other hand, the "operator<<(std::ios& (*f)(std::ios&))"
function did not accept "std::ws" either.
That's normal, since std::ws (instantiated for char) is:
std::istream& ws( std::istream& ) ;
The ws manipulator only works on istream's, since it actually
reads characters. The other manipulators manipulate fields in
std::ios_base, fields which are present in both istream and
ostream, even if some of them are only used in one of these.
(In fact, most of them are only used in ostream. But skipws is
only used in istream.)
I'm surprised that most of these manipulators worked with
"operator<<( std::ostream& (*)( std::ostream& )". According to
the standard, they have a signature of:
std::ios_base& manipulator( std::ios_base& )
and shouldn't match that function. Are you sure they aren't
being handled by "operator<<( std::ios& (*)( std::ios& ) )"?
The "operator<<( std::ostream& (*)( std::ostream& ) )" is there
for the manipulators which only work with ostreams: endl, ends
and flush; all of the others should use the ios version.
I'm going to do further testing anyway - for the moment I
didn't check if the manipulators actually affected the stream,
I've only checked if they were accepted by the << operator.
I've used setw extensively with such wrappers, as well as
instances of my own manipulators. (Other than setw and endl, I
can't imagine actually using one of the standard manipulators
directly.)
I've been able to compile the "operator<<(T& (*manip)(T&))"
function but it wasn't able to accept any manipulator at all.
The manipulator functions are templates (they work on any
basic_ios or basic_ostream/basic_istream). In order for the
above to be called, template argument deduction has to be able
to figure out the type of T. Passing it a function template
doesn't allow this.
Currently, there seems to be no need for any function other
than the "operator<<(T t)" template along with
"operator<<(std::ostream& (*) (std::ostream&))", at least for
my very own needs.
I'm pretty sure you're missing something. You really do need
the "operator<<( std::ios& (*)( std::ios& ) )" as well.
Man, I'm missing almost all of it. I'm only reporting what my GNU GCC
compiler lets me compile and run.
Here is my code, recall I'm an hobbyist, my code is junky and unsafe
by definition, just experimental stuff:
-------
#include <iostream>
#include <iomanip>
#include <sstream>
struct o_logger {
std::ostream* stream;
o_logger() : stream(0) {};
template<class T> o_logger& operator<<(const T& t) {
if (stream) (*stream) << t;
return *this;
}
o_logger& operator<<(std::ostream& (*f)(std::ostream&)) {
if (stream) (*stream) << f;
return *this;
}
};
struct i_logger {
std::istream* stream;
i_logger() : stream(0) {};
template<class T> i_logger& operator>>(T& t) {
if (stream) (*stream) >> t;
return *this;
}
i_logger& operator>>(std::istream& (*f)(std::istream&)) {
if (stream) (*stream) >> f;
return *this;
}
};
struct io_logger : public i_logger, public o_logger {};
template<class T> void o_tester(T*);
template<class T> void i_tester(T*);
int main()
{
o_logger ol_cout;
ol_cout.stream = &std::cout;
io_logger iol_sstream;
std::stringstream ss;
iol_sstream.i_logger::stream = &ss;
iol_sstream.o_logger::stream = &ss;
int n = 42;
iol_sstream << "[" << std::hex << n << " HEX]";
iol_sstream << " == [" << std::oct << n << " OCT]";
ol_cout << ss.str();
char c = '#';
n = 11235813;
iol_sstream >> c >> std::hex >> n;
ol_cout << " == " << c << n << " DEC]" << std::endl;
std::cout << "o_tester(&iol_sstream)" << std::endl;
o_tester(&iol_sstream);
std::cout << "i_tester(&iol_sstream)" << std::endl;
i_tester(&iol_sstream);
std::cout << "o_tester(&std::cout)" << std::endl;
o_tester(&std::cout);
std::cout << "i_tester(&std::cin)" << std::endl;
std::cout << "Here I must enter a non-ws char and press ENTER to
continue," << std::endl;
std::cout << "just after the '*t >> std::ws;' statement. Why this
happens" << std::endl;
std::cout << "only with 't == std::cin' and not with 't ==
i_logger'?" << std::endl;
i_tester(&std::cin);
return 0;
}
template<class T> void o_tester(T* t) {
*t << std::boolalpha;
*t << std::dec;
*t << std::endl;
*t << std::ends;
*t << std::fixed;
*t << std::flush;
*t << std::hex;
*t << std::internal;
*t << std::left;
*t << std::noboolalpha;
*t << std::noshowbase;
*t << std::noshowpoint;
*t << std::noshowpos;
*t << std::nounitbuf;
*t << std::nouppercase;
*t << std::oct;
*t << std::right;
*t << std::scientific;
*t << std::showbase;
*t << std::showpoint;
*t << std::showpos;
*t << std::unitbuf;
*t << std::uppercase;
*t << std::resetiosflags(std::ios_base::showbase);
*t << std::setbase(10);
*t << std::setfill(' ');
*t << std::setiosflags(std::ios_base::showbase);
*t << std::setprecision(10);
*t << std::setw(10);
*t << std::skipws;
*t << std::noskipws;
/* the following manipulator is not accepted
by std::ostream nor by the o_logger functions */
// *t << std::ws;
}
template<class T> void i_tester(T* t) {
*t >> std::boolalpha;
*t >> std::dec;
*t >> std::fixed;
*t >> std::hex;
*t >> std::internal;
*t >> std::left;
*t >> std::noboolalpha;
*t >> std::noshowbase;
*t >> std::noshowpoint;
*t >> std::noshowpos;
*t >> std::nounitbuf;
*t >> std::nouppercase;
*t >> std::oct;
*t >> std::right;
*t >> std::scientific;
*t >> std::showbase;
*t >> std::showpoint;
*t >> std::showpos;
*t >> std::unitbuf;
*t >> std::uppercase;
*t >> std::skipws;
*t >> std::noskipws;
*t >> std::ws;
/* the following manipulators are not accepted
by std::istream nor by the i_logger functions */
// *t >> std::endl;
// *t >> std::ends;
// *t >> std::flush;
/* the following manipulators are not accepted
only by the i_logger functions */
// *t >> std::resetiosflags(std::ios_base::showbase);
// *t >> std::setbase(10);
// *t >> std::setfill(' ');
// *t >> std::setiosflags(std::ios_base::showbase);
// *t >> std::setprecision(10);
// *t >> std::setw(10);
}
------
The code above compiles without any warning. Please do your tests
uncommenting the manipulators that don't compile on my version of GCC
(which is already know to be buggy, referring to the errors I reported
in my second post in this topic).
About the "( std::ios& (*)( std::ios& ) )" version, my compiler won't
let me pass any manipulator to it:
-------
#include <iostream>
#include <iomanip>
struct o_ios_only {
std::ostream* stream;
o_ios_only() : stream(0) {};
o_ios_only& operator<<(std::ios& (*f)(std::ios&)) {
if (stream) (*stream) << f;
return *this;
}
};
int main()
{
o_ios_only ot_cout;
ot_cout.stream = &std::cout;
/* no manipulator accepted */
// ot_cout << std::boolalpha;
// ot_cout << std::dec;
// ot_cout << std::fixed;
// ot_cout << std::hex;
// ot_cout << std::internal;
// ot_cout << std::left;
// ot_cout << std::noboolalpha;
// ot_cout << std::noshowbase;
// ot_cout << std::noshowpoint;
// ot_cout << std::noshowpos;
// ot_cout << std::nounitbuf;
// ot_cout << std::nouppercase;
// ot_cout << std::oct;
// ot_cout << std::right;
// ot_cout << std::scientific;
// ot_cout << std::showbase;
// ot_cout << std::showpoint;
// ot_cout << std::showpos;
// ot_cout << std::unitbuf;
// ot_cout << std::uppercase;
// ot_cout << std::skipws;
// ot_cout << std::noskipws;
// ot_cout << std::endl;
// ot_cout << std::ends;
// ot_cout << std::flush;
// ot_cout << std::resetiosflags(std::ios_base::showbase);
// ot_cout << std::setbase(10);
// ot_cout << std::setfill(' ');
// ot_cout << std::setiosflags(std::ios_base::showbase);
// ot_cout << std::setprecision(10);
// ot_cout << std::setw(10);
// ot_cout << std::ws;
return 0;
}
-------
I'm going to post another program (about the "T& (*)(T&)" template) as
a reply to the message you posted just after this one I'm replying to.
In a moment,
Francesco
--
James Kanze (GABI Software) email:james.ka...@gmail.com
Conseils en informatique orient=E9e objet/
Beratung in objektorientierter Datenverarbeitung
9 place S=E9mard, 78210 St.-Cyr-l'=C9cole, France, +33 (0)1 30 23 00 34