Using temporary std::stream object

S

sven.suursoho

Hello,

In main(), the first output API is what I try to achieve. Unfortunately
it fails, printing first string as pointer instead of human readable
message. Tried to initialize str(""), set new buffer etc, but nothing
worked.

Ideas? Also might use another internal construct, only API is needed
and requirement to reuse existing std::eek:stream inserters.

App itself:
#include <iostream>
#include <sstream>


class printer
{
public:

void print (const std::string &s) {
std::cout << s << std::endl;
}
};


class stream: public std::eek:stringstream
{
public:

stream (printer &p): std::eek:stringstream(), printer_(p) {
// gets stored correctly
*this << "*** ";
}

~stream () {
printer_.print(str());
}

std::eek:stream &
operator* () {
return *this;
}


private:

printer &printer_;
};


int
main ()
{
printer p;

// want to achieve this but:
// prints address of string instead of "first"
stream(p) << "first" << " fails";

// works because first inserted object is not printed
stream(p) << std::flush << "ok 1";

// works because stream object is used through 'operator*'
// can be any other method returning this or *this
*stream(p) << "ok 2";

return 0;
}
 
K

Kanenas

I've found some solutions, but my understanding of what is happening
is limited. As I see it, g++ won't consider the temporary stream
objects as viable arguments to functions taking 'stream&', just
'stream' or 'const stream&'. Thus the only insertion operators
considered are the member insertion operators (since the binary
variety all take non-const refs). Why are the member insertion
operators valid? I don't know. As a result,
'ostream::eek:perator<<(void const*)' gets picked as the function used in
'stream(p) << "first"'.

You don't have this issue in other uses of '<<'.
'ostream::eek:perator<<(void const*)' returns an ostream&, so
'operator<<(ostream&, const char *)' gets called for ' << " fails"'.
Similarly, '*stream(p) << "ok 2"' works because 'stream::eek:perator*()'
returns an ostream&. '*this << "*** "' works because '*this' is a
'stream&'. &c.

What to do? I tried a couple alterations of your current
implementation. They all started with the idea of adding an
operator<< and most ended with const abuse. The simplest was to never
use a temporary 'stream', but this may not work for your application.

The shortest was to add:

std::eek:stream& operator<<(const stream& streme, const char* str) {
return (* const_cast<stream&>(streme)) << str;
}

I also tried:

std::eek:stream& operator<<(stream streme, const char* str) {
return (*streme) << str;
}

but it required more code than the first example above, and messy code
at that. For example, inserting into a local 'stream' object would
create a temporary 'stream', both of which would print. I created a
method to cancel a 'stream', but it needed to be called on a 'const
stream&', so I used a mutable member of 'stream'... It was
conceptually hideous. If you are only going to use temporary streams,
it might work for you; in this case, the only other thing you need to
add is a copy constructor.

You may be able to find a way of writing a stream::eek:perator<<(const
char*). Everytime I tried, g++ would complain that, according to ISO
C++, 'stream::eek:perator<<(const char*)' and 'operator<<(ostream&, const
char*)' were ambiguous. If 'stream' didn't inherit from
'ostringstream', this would definitely work, but you'd have to define
the various '<<(stream&, ARG2)' and '<<(ARG)' methods.

I tried to think of a way to use a factory method (called
stream::make) rather than a constructor to create streams (such an
approash shouldn't abuse constness), but couldn't do it without
dynamically created objects. Throw in smart pointers and it would
work with minimal changes to the API (use stream::make rather than
stream::stream), but seems heavy handed.

I hope something here will work or set you off in the direction of a
workable solution.
 
S

sven.suursoho

Messed with code myself also and went quite similar direction as you
did: create new stream inserter that will return std::eek:stream, on which
following inserters will continue to work :)

Resolved ambiguity by making inserter member operator, that calls
external inserter and returns result stream.

Final solution:
#include <iostream>
#include <sstream>


class printer
{
public:

void
print (const std::string &s) {
std::cout << s << std::endl;
}
};


class stream: public std::eek:stringstream
{
public:

stream (printer &p): printer_(p) {}


~stream () {
printer_.print(str());
}


template <typename T>
std::eek:stream &
operator<< (const T &t) {
return static_cast<std::eek:stream &>(*this) << t;
}


private:

printer &printer_;
};



int
main ()
{
printer p;
stream(p) << "now" << " works " << "well";

return 0;
}
 
R

Richard Herring

Kanenas said:
I've found some solutions, but my understanding of what is happening
is limited. As I see it, g++ won't consider the temporary stream
objects as viable arguments to functions taking 'stream&', just
'stream' or 'const stream&'.

It's not just g++. No conformant compiler will, because you'd be binding
a temporary to a non-const reference, which is forbidden.
Thus the only insertion operators
considered are the member insertion operators (since the binary
variety all take non-const refs). Why are the member insertion
operators valid? I don't know.

Because calling a non-const member function of a temporary doesn't bind
it to a reference.
As a result,
'ostream::eek:perator<<(void const*)' gets picked as the function used in
'stream(p) << "first"'.

You don't have this issue in other uses of '<<'.
'ostream::eek:perator<<(void const*)' returns an ostream&, so
'operator<<(ostream&, const char *)' gets called for ' << " fails"'.
Similarly, '*stream(p) << "ok 2"' works because 'stream::eek:perator*()'
returns an ostream&. '*this << "*** "' works because '*this' is a
'stream&'. &c.

What to do? I tried a couple alterations of your current
implementation. They all started with the idea of adding an
operator<< and most ended with const abuse. The simplest was to never
use a temporary 'stream', but this may not work for your application.

How about giving your stream class a function (or operator()) that
returns a reference to itself?

Then you can call it using e.g.

stream(p)() << "hello";
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,770
Messages
2,569,583
Members
45,072
Latest member
trafficcone

Latest Threads

Top