Problems with derived IO classes

I

Ian C

Hello all

Apologies if this is a dumb question, but this is driving me nuts, and
I'm really an old procedural programmer so not sure if it's me or the
compiler at fault. I can perhaps guess :)

I have written a derived streambuf class so I can log to a
system-dependent logging interface. I use this in a derived class like so:

class Syslog : public std::eek:stream
{
public:
Syslog(eSyslogLevel level)
: std::eek:stream(new SyslogBuffer(level))
{
}
};


If I use a variable to use this class, it compiles fine:

Syslog str(SYSLOG_INFO);

str << "Test Number " << 1 << std::endl;


However, if I use it as a temporary object like this:

Syslog(SYSLOG_INFO) << "Test Number " << 1 << std::endl;

the compiler complains that it can't find a match for operator<< for the
Syslog class and the character array.

Am I going mad, or just doing something dumb?

Cheers.


Ian C
 
R

red floyd

Ian said:
Hello all

Apologies if this is a dumb question, but this is driving me nuts, and
I'm really an old procedural programmer so not sure if it's me or the
compiler at fault. I can perhaps guess :)

I have written a derived streambuf class so I can log to a
system-dependent logging interface. I use this in a derived class like so:

class Syslog : public std::eek:stream
{
public:
Syslog(eSyslogLevel level)
: std::eek:stream(new SyslogBuffer(level))
{
}
};


If I use a variable to use this class, it compiles fine:

Syslog str(SYSLOG_INFO);

str << "Test Number " << 1 << std::endl;


However, if I use it as a temporary object like this:

Syslog(SYSLOG_INFO) << "Test Number " << 1 << std::endl;

the compiler complains that it can't find a match for operator<< for the
Syslog class and the character array.

I believe the difference is that in the second case, Syslog(SYSLOG_INFO)
is a temporary, and therefore binds to a const reference, and there is
no operator<< that takes a const ostream& as the LHS.

BTW, you do know you can change the streambuf in a regular ostream, so
you don't have to derive from std::eek:stream, right?
 
V

Victor Bazarov

Ian said:
Apologies if this is a dumb question, but this is driving me nuts, and
I'm really an old procedural programmer so not sure if it's me or the
compiler at fault. I can perhaps guess :)

I have written a derived streambuf class so I can log to a
system-dependent logging interface. I use this in a derived class
like so:
class Syslog : public std::eek:stream
{
public:
Syslog(eSyslogLevel level)
: std::eek:stream(new SyslogBuffer(level))
{
}
};


If I use a variable to use this class, it compiles fine:

Syslog str(SYSLOG_INFO);

str << "Test Number " << 1 << std::endl;


However, if I use it as a temporary object like this:

Syslog(SYSLOG_INFO) << "Test Number " << 1 << std::endl;

the compiler complains that it can't find a match for operator<< for
the Syslog class and the character array.

Am I going mad, or just doing something dumb?

The operator that it can find (and uses for the former case, with
the 'str' variable) is probably a non-member, and it has the first
argument a reference to non-const ostream. You cannot bind a non-
const reference to a temporary (in your case 'Syslog(SYSLOG_INFO)'
is the temporary).

There is a trick you can play to get where you want to be:

Syslog(SYSLOG_INFO) << std::flush << "Test Number" << 1 << std::endl;

What it does is calling a member function of 'ostream' which then
returns a non-const reference (to the same temporary, but it's OK
since the temporary survives until the end of the full expression),
and then it can re-use the same non-const reference to call the
non-member operator <<.

V
 
I

Ian C

red said:
Ian said:
>> [snip]
If I use a variable to use this class, it compiles fine:

Syslog str(SYSLOG_INFO);

str << "Test Number " << 1 << std::endl;


However, if I use it as a temporary object like this:

Syslog(SYSLOG_INFO) << "Test Number " << 1 << std::endl;

the compiler complains that it can't find a match for operator<< for
the Syslog class and the character array.

I believe the difference is that in the second case, Syslog(SYSLOG_INFO)
is a temporary, and therefore binds to a const reference, and there is
no operator<< that takes a const ostream& as the LHS.

Ah! Thanks for that -- you learn something new everyday!
BTW, you do know you can change the streambuf in a regular ostream, so
you don't have to derive from std::eek:stream, right?

I was making it so that the destruction of the streambuf flushed out
output to the logging interface, and the second method lust looked like
a neat way of implementing logging.

I have seen similar logging classes where different IO classes are
returned from member functions within a container and allocated at
start-up, but that could get rather messy as I'm in a multi-threaded
application, so the individual temporary object method seemed ideal.

Well, would have done had it been valid :)

Thanks for the help, red.


Ian C
 
I

Ian C

Victor said:
Ian said:
>> [snip]
If I use a variable to use this class, it compiles fine:

Syslog str(SYSLOG_INFO);

str << "Test Number " << 1 << std::endl;


However, if I use it as a temporary object like this:

Syslog(SYSLOG_INFO) << "Test Number " << 1 << std::endl;

the compiler complains that it can't find a match for operator<< for
the Syslog class and the character array.

Am I going mad, or just doing something dumb?

The operator that it can find (and uses for the former case, with
the 'str' variable) is probably a non-member, and it has the first
argument a reference to non-const ostream. You cannot bind a non-
const reference to a temporary (in your case 'Syslog(SYSLOG_INFO)'
is the temporary).

There is a trick you can play to get where you want to be:

Syslog(SYSLOG_INFO) << std::flush << "Test Number" << 1 << std::endl;

What it does is calling a member function of 'ostream' which then
returns a non-const reference (to the same temporary, but it's OK
since the temporary survives until the end of the full expression),
and then it can re-use the same non-const reference to call the
non-member operator <<.

Thanks to you too, Victor. Never thought of temporary objects being
const, but I suppose it makes some sense.

I think I'll have to have a rethink. I may just make the streambuf
complete the logging operation when it receives a newline (rather than
on destruction as it does at the minute), so that users can just
instantiate the appropriate logging objects when they start up and carry
on using it.

Thanks again Victor.


Ian C
 
P

Pete Becker

Ian said:
Thanks to you too, Victor. Never thought of temporary objects being
const, but I suppose it makes some sense.

Victor was very careful not to say that temporary objects are const.
They're not. Well, sort of. The rule is that you can't bind a temporary
object to a non-const reference. So this isn't valid:

struct T
{
void func();
}

void f(T&);

f(T()); // ill-formed: attempts to call f with temporary

Granted, that looks a lot like a const object. But you can call
non-const member functions on a temporary object:

T().func(); // okay

which is why the trick that Victor showed works. The inserter for char*
is not a member function, so you can't call it on the temporary stream
object. The inserter for io manipulators is a member function, so
calling it on the temporary is okay. And once the stream object has been
laundered through the inserter for manipulators, you're no longer trying
to bind a temporary to anything, so the rest of the manipulators, both
member and non-member, are okay.

--

-- Pete
Roundhouse Consulting, Ltd. (www.versatilecoding.com)
Author of "The Standard C++ Library Extensions: a Tutorial and
Reference." (www.petebecker.com/tr1book)
 
I

Ian C

Pete said:
Victor was very careful not to say that temporary objects are const.
They're not. Well, sort of. The rule is that you can't bind a temporary
object to a non-const reference. So this isn't valid:

struct T
{
void func();
}

void f(T&);

f(T()); // ill-formed: attempts to call f with temporary

Granted, that looks a lot like a const object. But you can call
non-const member functions on a temporary object:

T().func(); // okay

which is why the trick that Victor showed works. The inserter for char*
is not a member function, so you can't call it on the temporary stream
object. The inserter for io manipulators is a member function, so
calling it on the temporary is okay. And once the stream object has been
laundered through the inserter for manipulators, you're no longer trying
to bind a temporary to anything, so the rest of the manipulators, both
member and non-member, are okay.

Apologies to Victor and thanks for the clarification Pete -- I realised
shortly afterwards they couldn't be const just because they're temporary
as I'd already tried something like:

Constructor().give_me_the_stream() << "Hello";

while messing around...

It does ring a definite bell now, and I can remember being told a good
numbers of years ago (when I last had a brush with C++) about not using
temporary objects for interfaces with a non-const reference parameter.

I just didn't realise that you could circumvent it by calling a
non-const member that returned the same object, which is where my
confusion came in. Assuming I have actually finally got it straight
this time :)

Thanks for the help.


Ian C
 
P

Pete Becker

Ian said:
It does ring a definite bell now, and I can remember being told a good
numbers of years ago (when I last had a brush with C++) about not using
temporary objects for interfaces with a non-const reference parameter.

I just didn't realise that you could circumvent it by calling a
non-const member that returned the same object, which is where my
confusion came in. Assuming I have actually finally got it straight
this time :)

Yup, you've got it straight. That's a clever trick, one that I haven't
seen before.

--

-- Pete
Roundhouse Consulting, Ltd. (www.versatilecoding.com)
Author of "The Standard C++ Library Extensions: a Tutorial and
Reference." (www.petebecker.com/tr1book)
 

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

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,483
Members
44,901
Latest member
Noble71S45

Latest Threads

Top