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