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