How to Construct a C++ Fstream from a Posix File Descriptor

How to construct a c++ fstream from a POSIX file descriptor?

From the answer given by Éric Malenfant:

AFAIK, there is no way to do this in
standard C++. Depending on your
platform, your implementation of the
standard library may offer (as a
nonstandard extension) a fstream
constructor taking a file descriptor
as input. (This is the case for
libstdc++, IIRC) or a FILE*.

Based on above observations and my research below there's working code in two variants; one for libstdc++ and another one for Microsoft Visual C++.


libstdc++

There's non-standard __gnu_cxx::stdio_filebuf class template which inherits std::basic_streambuf and has the following constructor

stdio_filebuf (int __fd, std::ios_base::openmode __mode, size_t __size=static_cast< size_t >(BUFSIZ)) 

with description This constructor associates a file stream buffer with an open POSIX file descriptor.

We create it passing POSIX handle (line 1) and then we pass it to istream's constructor as basic_streambuf (line 2):

#include <ext/stdio_filebuf.h>
#include <iostream>
#include <fstream>
#include <string>

using namespace std;

int main()
{
ofstream ofs("test.txt");
ofs << "Writing to a basic_ofstream object..." << endl;
ofs.close();

int posix_handle = fileno(::fopen("test.txt", "r"));

__gnu_cxx::stdio_filebuf<char> filebuf(posix_handle, std::ios::in); // 1
istream is(&filebuf); // 2

string line;
getline(is, line);
cout << "line: " << line << std::endl;
return 0;
}

Microsoft Visual C++

There used to be non-standard version of ifstream's constructor taking POSIX file descriptor but it's missing both from current docs and from code. There is another non-standard version of ifstream's constructor taking FILE*

explicit basic_ifstream(_Filet *_File)
: _Mybase(&_Filebuffer),
_Filebuffer(_File)
{ // construct with specified C stream
}

and it's not documented (I couldn't even find any old documentation where it would be present). We call it (line 1) with the parameter being the result of calling _fdopen to get C stream FILE* from POSIX file handle.

#include <cstdio>
#include <iostream>
#include <fstream>
#include <string>

using namespace std;

int main()
{
ofstream ofs("test.txt");
ofs << "Writing to a basic_ofstream object..." << endl;
ofs.close();

int posix_handle = ::_fileno(::fopen("test.txt", "r"));

ifstream ifs(::_fdopen(posix_handle, "r")); // 1

string line;
getline(ifs, line);
ifs.close();
cout << "line: " << line << endl;
return 0;
}

Creating fstream object from a FILE* pointer

You cannot do that just in standard C++, since iostreams and C I/O are entirely separate and unrelated. You could however write your own iostream that's backed by a C FILE stream. I believe that GCC comes with one such stream class as a library extension.

Alternatively, if all you want is an object-y way of wrapping a C FILE stream, you could use a unique pointer for that purpose.

Getting a FILE* from a std::fstream

The short answer is no.

The reason, is because the std::fstream is not required to use a FILE* as part of its implementation. So even if you manage to extract file descriptor from the std::fstream object and manually build a FILE object, then you will have other problems because you will now have two buffered objects writing to the same file descriptor.

The real question is why do you want to convert the std::fstream object into a FILE*?

Though I don't recommend it, you could try looking up funopen().

Unfortunately, this is not a POSIX API (it's a BSD extension) so its portability is in question. Which is also probably why I can't find anybody that has wrapped a std::stream with an object like this.

FILE *funopen(
const void *cookie,
int (*readfn )(void *, char *, int),
int (*writefn)(void *, const char *, int),
fpos_t (*seekfn) (void *, fpos_t, int),
int (*closefn)(void *)
);

This allows you to build a FILE object and specify some functions that will be used to do the actual work. If you write appropriate functions you can get them to read from the std::fstream object that actually has the file open.

How to get a FILE* stream from a file descriptor?

   FILE *fdopen(int fd, const char *mode);

See fdopen(3), but it should be on the same page as fopen(3):

The fdopen() function associates a stream with the existing
file descriptor, fd. The mode of the stream (one of the values
"r", "r+", "w", "w+", "a", "a+") must be compatible
with the mode of the file descriptor. The file position indicator
of the new stream is set to that belonging to fd, and the error and
end-of-file indicators are cleared. Modes "w" or "w+" do not
cause truncation of the file. The file descriptor is not dup’ed,
and will be closed when the stream created by fdopen() is
closed. The result of applying fdopen() to a shared memory object
is undefined.

How can I convert a file pointer ( FILE* fp ) to a file descriptor (int fd)?

The proper function is int fileno(FILE *stream). It can be found in <stdio.h>, and is a POSIX standard but not standard C.

Can I use a FILE* to initialize a C++ ostream object?

With GCC you can just pass the existing file descriptor to the constructor (ref):

FILE *f = ...;
std::ofstream os(fileno(f));

Constructor: ofstream::ofstream (int fd)

Make an ofstream for writing to a file that was already open, using file descriptor fd.

How to open custom I/O streams from within a C++ program?

It is not possible if you need your program to be portable. The C++ 11 Standard does not specify a unified way of doing that.

However, you can define your own output stream buffer which overrides the overflow() and xsputn() virtual functions and writes each character or sequence of characters to the stream with the specified descriptor using system-specific API.

Something along these lines:

class my_ostream_buf : public std::streambuf
{

public:

my_ostream_buf(int fd) : _fd(fd) { }

protected:

virtual int_type overflow (int_type c)
{
if (c != EOF)
{
char ch = c;
if (write(_fd, &ch, 1) != 1)
{
return EOF;
}
}

return c;
}

// This is not strictly necessary, but performance is better if you
// write a sequence of characters all at once rather than writing
// each individual character through a separate system call.
virtual std::streamsize xsputn(const char* s, std::streamsize num)
{
return write(_fd, s, num);
}

private:

int _fd = 0;

};

And this is how you would use it:

using namespace std;

int main()
{
int fd = ...; // Any file descriptor
my_ostream_buf buf(fd);

ostream os(&buf); // Take care of the lifetime of `buf` here, or create your
// own class that derives from ostream and encapsulates an
// object of type my_ostream_buf

os << "Hello" << endl;
}

Get FILE stream from File Handle

Use _open_osfhandle() to create a C-style file descriptor from a Win32 HANDLE, and then use _fdopen() to create a FILE* from the file descriptor.



Related Topics



Leave a reply



Submit