Long dead thread: Re: The patch for PR libstdc++/14097.
Brad Spencer
spencer@infointeractive.com
Wed Apr 28 17:22:00 GMT 2004
On Wed, Feb 11, 2004 at 08:01:38PM -0600, Benjamin Kosnik wrote:
>
> >Then, for reasons of symmetry, we should expose the FILE* when we wrap the
> >other layer. Like this:
>
> I don't have a problem adding this kind of thing to the extension
> filebufs. Either, both. Submit a patch, ChangeLog entry, the whole deal.
>
> I do have a problem with adding fd/FILE accessors to std::basic_filebuf,
> but this isn't what you are asking (at this point in the thread.)
I know this is a long stale thread, but from what I remember of the
"long, torrid history" :) was that "if you wanted to do something
weird, write your own filebuf". Well, as a reference for the next time
this comes up, perhaps, I did just that long ago.
I have a logging system based on iostreams, and I do two special
things. First, I have "class fdfilebuf : public std::filebuf" which
hacks in an "int fd()" that I need for some fork() and other
purposes. This is clearly an unsupported extension since it's in user
code :)
Second, I have "class logfilebuf : public fdfilebuf" that overrides
overflow() and sync() to call syslog() (and then chains to the base
class to write to the file, too). I consider this to be an extreme
customization, and it only takes a couple of dozen or so lines of user
code.
I agree with what you were saying on this, and I think it users can
get to the details they need with the appropriate caveats without
hackery inside libstdc++. In fact, from my (incomplete) reading of
Langer and Kreft's "Standard C++ IOStreams and Locales", this is the
intended approach for extending IOStreams functionality. There's a
whole section (3.3) of the text that discusses the general topic.
For reference, I've attached a simplified concept implementation of
how you can do this sort of thing in real applications. The sample
makes some compromises, but it admits as much. This works with
gcc-3.4.0, and a form of it has worked since the gcc-2.95.x days.
It is important to note that I've combined two solutions into this
single sample. The syslog() functionality does not depend on being
able to get the UNIX file descriptor or vice versa.
--
------------------------------------------------------------------
Brad Spencer - spencer@infointeractive.com - "It's quite nice..."
Systems Architect | InfoInterActive Corp. | A Canadian AOL Company
-------------- next part --------------
//*****************************************************************************
#include <iostream>
#include <fstream>
#include <syslog.h>
// This class exposes the UNIX file descriptor that is beneath the IO
// Streams filebuf.
class fdfilebuf : public std::filebuf
{
typedef std::filebuf base;
public:
fdfilebuf()
: base()
{}
virtual ~fdfilebuf()
{}
public:
// Our purpose in life is to return the UNIX file descriptor. This is an
// extension that is made possible by some knowledge of the underlying
// IO Streams implementation and is not supported as an interface in that
// implementation.
int fd()
{
// _M_file is a protected member of our base class, and it knows the real
// file descriptor.
return _M_file.fd();
}
};
//-----------------------------------------------------------------------------
// This is a streambuf that tees the output to syslog
class logfilebuf : public fdfilebuf
{
typedef fdfilebuf base;
public:
logfilebuf()
: base()
{}
virtual ~logfilebuf()
{}
public:
// Both overflow and sync can happen during a log emit. Overflow is
// called in the middle of a log line that's too big for the internal
// buffer of the filebuf.
virtual int_type overflow(int_type c = traits_type::eof())
{
prv_doSyslog();
return base::overflow(c);
}
// This will essentially correspond to 'endl'
virtual int sync()
{
prv_doSyslog();
return base::sync();
}
private:
// Actually emit the internal buffer to syslog
void prv_doSyslog()
{
// See Langer and Kreft p.513; pptr() == 0 on first call
if(pptr() == NULL) {
return;
}
// We need to look at the current buffer and figure out what we want to
// print
static char buff[1024];
const off_type n = pptr() - pbase();
// Don't log ""
if(n == 0) {
return;
}
// Syslog needs a null-terminated string :(, but it also can't handle
// more than 1024 characters. To remain safe, we copy up to that many
// into a temporary buffer and then syslog that.
const unsigned int p =
std::min(static_cast<size_t>(n), sizeof(buff) - 1);
memcpy(buff, pbase(), p);
buff[p] = '\0';
::syslog(LOG_INFO, "%s", buff);
}
};
//-----------------------------------------------------------------------------
int
main()
{
// Start syslog
openlog("test", LOG_CONS, LOG_LOCAL7);
// Here's a file iostream that we will open
std::ofstream realFile;
// Make a new logfilebuf so we can tee the realFile ostream to syslog.
logfilebuf * const fb = new logfilebuf;
// Swap out the old filebuf one with the new one and toss the old one. We
// have to check to see if the current one is allocated with new. This is
// somewhat bizarre and you should look at Langer and Kreft pp. 495
if(realFile.std::basic_ostream<char>::rdbuf() == realFile.rdbuf()) {
// The buffer is the basic_ofstream's internal basic_filebuf, which is a
// member object. Don't delete that; ignore the return value.
realFile.std::basic_ostream<char>::rdbuf(fb);
} else {
// It's _not_ the basic_ofstream's internal basic_filebuf member
// object. We surmise that it has been allocated with new, so we delete
// it.
delete realFile.std::basic_ostream<char>::rdbuf(fb);
}
// We've passed ownership of the filebuf along at this point, one way or
// another.
// We have to close any open file and re-open the new one
if(fb->is_open() == true) {
if(fb->close() == NULL) {
// Handle this error somehow
std::cerr << "Error closing already open filebuf" << std::endl;
return 1;
}
}
// Open the file with the filebuf itself
if(fb->open("/tmp/file", std::ios_base::out | std::ios_base::app) == NULL) {
// Handle this error somehow
std::cerr << "Error opening filebuf" << std::endl;
return 2;
}
// If we _know_ that we put our own custom fdfilebuf into a stream, we can
// downcast it, or we could have remembered the fdfilebuf pointer externally
// somewhere. This should yield the same value as fb->fd().
const int descriptor =
static_cast<fdfilebuf *>(realFile.std::basic_ostream<char>::rdbuf())->fd();
// Use the file and it will go to syslog as well
realFile << "This is a test message. My associated UNIX file descriptor is "
<< descriptor << " == " << fb->fd() << '.' << std::endl;
return 0;
}
/*
Here's what it does:
$ ./sample
$ cat /tmp/file
This is a test message. My associated UNIX file descriptor is 3 == 3.
$ tail -1 /var/log/messages
Apr 28 14:04:50 bubbles test: This is a test message. My associated UNIX file descriptor is 3 == 3.
*/
//*****************************************************************************
More information about the Libstdc++
mailing list