Re: A std::ostringstream wrapper for on-the-fly streaming - is this
dodgy?
On 3 Ago, 22:27, Stuart Golodetz
<sgolod...@NdOiSaPlA.pMiPpLeExA.ScEom> wrote:
Francesco wrote:
On Aug 3, 3:29 pm, Stuart Golodetz
<sgolod...@NdOiSaPlA.pMiPpLeExA.ScEom> wrote:
Just wondering if anyone can see a problem with the code below? The
intent is to allow you to construct strings "on-the-fly" via streaming,
e.g. f(OSSWrapper() << "Wibble " << 42). The code works, but I'm
concerned that it might not be the best practice in the world -
particularly the need to use a const reference to bind to the temporary,
and the ensuing need to make m_os mutable. Seems a bit icky to me! Is
there a better way?
Cheers,
Stu
#include <iostream>
#include <sstream>
#include <string>
class OSSWrapper
{
private:
mutable std::ostringstream m_os;
public:
operator std::string() const
{
return m_os.str();
}
template <typename T>
friend const OSSWrapper& operator<<(const OSSWrapper& ossw, const T& rhs)
{
ossw.m_os << rhs;
return ossw;
}
};
int main()
{
std::string s = OSSWrapper() << "Blah " << 23 << ' ' << 9.0;
std::cout << s << std::endl;
return 0;
}
Hi Stuart - and hi everybody, as this is my very first post here.
I'm no C++ expert, so I can only give my two cents.
The code you posted didn't compile on my machine (WinXP and
Code::Blocks 8.02) - it returned some kind of error telling
std::ios_base is private and cannot be accessed.
Mm, that's odd :-) Can you post the exact error if you get a chance
please? (It compiles on MSVC++8 and Comeau, so I'm curious as to what's
going on...)
Of course I can, it happens all the time with your exact original
code, here is the error:
---------------------------------
D:\Documenti\C++ Projects\osstream wrapper\main.cpp: In copy
constructor `std::basic_ios<char, std::char_traits<char> >::basic_ios
(const std::basic_ios<char, std::char_traits<char> >&)':
C:/Programmi/CodeBlocks/MinGW/bin/../lib/gcc/mingw32/3.4.5/../../../../
include/c++/3.4.5/bits/ios_base.h:781: error: `std::ios_base::ios_base
(const std::ios_base&)' is private
D:\Documenti\C++ Projects\osstream wrapper\main.cpp:27: error: within
this context
D:\Documenti\C++ Projects\osstream wrapper\main.cpp: In copy
constructor `std::basic_stringbuf<char, std::char_traits<char>,
std::allocator<char> >::basic_stringbuf(const
std::basic_stringbuf<char, std::char_traits<char>,
std::allocator<char> >&)':
C:/Programmi/CodeBlocks/MinGW/bin/../lib/gcc/mingw32/3.4.5/../../../../
include/c++/3.4.5/streambuf:769: error: `std::basic_streambuf<_CharT,
_Traits>::basic_streambuf(const std::basic_streambuf<_CharT,
_Traits>&) [with _CharT = char, _Traits = std::char_traits<char>]' is
private
---------------------------------
The compiler I'm using is GNU GCC (I forgot to mention it along with
my OS and my IDE, before).
The line it is chocking on is the first one in "main()", in your
original code - but I assume you don't have to be told this, by now I
know that you are way more experienced than me and I suppose you are
able to evince this from the report I posted above ;-)
Converting the friend function to a normal const member function did
actually let it compile without any warning/error, and the program ran
as expected - and honestly I can see no reason for setting a member
function as a friend of its own class, but recall that I'm an
hobbyist.
It's not a member function - you can put a non-member friend inside the
class like that. The reason it's a friend in this case is that it
accesses m_os, which is private.
I didn't know such a non-member function could be defined (further to
be declared) inside of the class itself. Good to know, I learned a new
thing - among a couple more, read below.
My compiler chokes on it, anyway, I had to redefine it in this way, in
order for it to compile:
---------------------------------
template <class T>
const OSSWrapper& operator<<(const T& rhs) const
{
m_os << rhs;
return *this;
}
---------------------------------
Once more, an unneeded clarification, I suppose ;-)
I would post my code but I'm new here and I don't want to make the
mistake of doing others' work in their place, right here in my first
post. Please excuse me if I'm wrong.
:-) If you're referring to FAQ 5.3, that's more to do with not doing
people's homework problems for them actually. I never did C++ homework
(and these days I don't do homework period - although I sometimes work
from home).
Eheheehh, yes, you got me on this, luckily you got me straight and you
did not get offended by my words.
You have to know that I've taught myself C++ with a small help from a
C professional and by reading (and practicing) Stroustrup's 3rd
edition of TC++PL (in Italian). I'm sort of an "ivory tower" coder, I
have no grasp on "real world" issues and "homework" assignments -
that's why I didn't use the word "homework" in first place ;-)
As for my knowledge and my experience, after fixing that function
there would be no problem with your class - apart that the template
definition will have to be included in every compile unit it is used,
unless your compiler lets you use the export keyword.
That's ok. My compiler doesn't support export (not surprisingly), but I
can just #include the template so it's not a huge problem.
Reading again my words I've noticed how obvious (and silly) my
statement was. I think even the dumbest C++ coder knows that, but take
in account my background and the mood I felt posting my first words in
such a mythic NG ;-)
Another issue I've met making a similar class is with std::endl, which
cannot be passed to your operator<<() function. About this, in my
code, I use to create a local "endl" constant setting it to a newline
character, just for my very ease - recall I'm an DIY-coder, again - I
know that some similar classes can be implemented allowing standard
manipulators, but that's just out of the scope of my skills, and maybe
also out of the scope of the original questions.
That's an excellent point - thanks! Might need to devote a bit more
thought to this...
Much appreciated,
Stu
Have good coding, hope this helped you.
Cheers,
Francesco
On 3 Ago, 22:34, Stuart Golodetz
<sgolod...@NdOiSaPlA.pMiPpLeExA.ScEom> wrote:
Ok, adding the following to OSSWrapper seems to sort out the issue with
std::endl:
friend const OSSWrapper& operator<<(const OSSWrapper& ossw,
std::ostream& (*f)(std::ostream&))
{
ossw.m_os << f;
return ossw;
}
Thanks for pointing it out!
Stu
You're welcome!
In fact, I've just stolen your solution... in my programs I use to do
a lot of logging, for that reason I've built a logger class, and now
I've fixed the std::endl issue with your solution - which [cool!]
handles all manipulators:
---------------------------------
#include <iostream>
#include <iomanip>
struct logger {
std::ostream* stream;
logger() : stream(0) {};
template<class T> logger& operator<<(T t) {
if (stream) (*stream) << t;
return *this;
}
logger& operator<<(std::ostream& (*f)(std::ostream&)) {
if (stream) (*stream) << f;
return *this;
}
};
int main()
{
logger log;
log.stream = &std::cout;
log << "iomanip test1 " << std::setbase(16) << 16 << std::endl;
log << "iomanip test2 " << std::setbase(8) << 8 << std::endl;
return 0;
}
---------------------------------
Here above I'm using a struct and a pointer to an ostream because I
use such objects in the implementation of my (other) classes, so that
I can simply avoid passing an ostream pointer to those classes and
avoid all logging, when debugging is over - ready to be turned on when
the next bug appears ;-)
All the best, thank you for solving the manipulators issue for me ;-)
Francesco