File descriptor hack - best that can be done?

Chris Rankin cj.rankin@ntlworld.com
Sun Jul 28 13:39:00 GMT 2002


Hi,

I have been investigating how feasible it might be to port a C++
application to g++. The original application had this crucial line:

fcntl(m_statFile.rdbuf()->fd(), F_SETFD, FD_CLOEXEC);

Now this is not supported by libstdc++ - I've read the mailing
lists. So my task has been to see how this rather important
descriptor-level access has been implemented instead. I have written a
test application which seems to do what I want, but *it's UGLY*!!!

The two significant flaws are:
a) The application ends up with two file buffers.
b) The rdbuf() function ALWAYS return the built-in buffer, despite the
   fact that the stream is no longer using it. I can see this becoming
   a rather nasty "gotcha" in future.

It also occurred to me that one possible solution might be:

template<typename _CharT, typename _Traits>
class basic_ofstream : public basic_ostream<_CharT,_Traits>
{
public:

#ifdef __GNU_SOURCE
  typedef __gnu_cxx::stdio_filebuf<char_type, traits_type>  __filebuf_type;
#else
  typedef basic_filebuf<char_type, traits_type>   __filebuf_type;
#endif

};

This might actually make everyone happy; I for one could live with it.

Cheers,
Chris

----------------------------------------------------------------------
#include <iostream>
#include <fstream>
#include <string>
#include <stdexcept>

#include <ext/stdio_filebuf.h>

#include <fcntl.h>

using namespace std;


class Foo
{
private:
  /*
   * File stream object ...
   */
  ofstream                  m_file;

  /*
   * Replacement file buffer, because the default
   * one does not contain enough functionality.
   *
   * NOTE: We must create it on the heap because we
   *       can't call the constructor until we've
   *       created our file descriptor ...
   */
  ofstream::__filebuf_type* m_buffer;

public:
  Foo();
  ~Foo();

  void write(const string &msg);
};


Foo::Foo()
{
  int fd = ::open("fooey.dat",
                  O_CREAT | O_WRONLY | O_APPEND,
                  S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
  if (fd == -1)
  {
    throw runtime_error("Error creating description");
  }

  if ( fcntl(fd, F_SETFD, FD_CLOEXEC) == -1 ) 
  {
    throw runtime_error("Error setting descriptor flag");
  }

  /*
   * What is the address of the original file buffer?
   */
  cout << "rdbuf() returns " << static_cast<void*>(m_file.rdbuf()) << endl;

  /*
   * Now create a more useful file buffer and plug
   * it into the stream object ...
   */
  m_buffer = new __gnu_cxx::stdio_filebuf<ofstream::char_type>
                    (fd, ios_base::out | ios_base::app, true, BUFSIZ);
  m_file.basic_ios<ofstream::char_type>::rdbuf(m_buffer);

  /*
   * Just for interest, which file buffer does the stream report using?
   */
  cout << "rdbuf() returns " << static_cast<void*>(m_file.rdbuf()) << endl;
  cout << "new buffer is at " << static_cast<void*>(m_buffer) << endl;
}


Foo::~Foo()
{
  delete m_buffer;
}


void
Foo::write(const string &msg)
{
  m_file << msg << endl;
}


int
main()
{
  try
  {
    Foo foo;
    foo.write("Text message");
  }
  catch (const exception &e)
  {
    cerr << "ERROR: " << e.what() << endl;
  }

  return 0;
}



More information about the Libstdc++ mailing list