C++ Logo


Advanced search

Re: [std-proposals] Construct an iostream from a file descriptor

From: Arthur O'Dwyer <arthur.j.odwyer_at_[hidden]>
Date: Fri, 2 Sep 2022 12:15:30 -0400
On Fri, Sep 2, 2022 at 6:32 AM Peter C++ via Std-Proposals <
std-proposals_at_[hidden]> wrote:

> I think it is doable and desirable. i even was of the impression it would
> already work,but at least not as of what is in the std.
> issues:
> - [...] should adoption also work from a FILE*?
> - can there be destruction without close?
On 2 Sep 2022, at 11:37, Paul Fee via Std-Proposals <
> std-proposals_at_[hidden]> wrote:
> POSIX defines fdopen(), which takes ownership of a low level file
> descriptor and returns a FILE* with which a user can perform buffered I/O.
> There doesn't appear to be a similar feature in C++, though non-standard
> workarounds exist, such as __gnu_cxx::stdio_filebuf.
> https://stackoverflow.com/questions/2746168/how-to-construct-a-c-fstream-from-a-posix-file-descriptor
> Are there issues that would block standardisation of such a facility?
> Perhaps it's because fdopen() is part of POSIX rather than ISO C, hence
> file descriptors themselves may not be universally portable.
> Paul: The feature "turn a file descriptor into a FILE" already exists in
both C and C++, *on POSIX platforms*; the POSIX-standard facility is called
File descriptors are a POSIX-standard thing. File descriptors don't exist,
per se, on non-POSIX platforms. So the situation re "turn a file descriptor
into a FILE" is already in the best possible state it could ever be in.

However, then there's the next step: "turn a FILE into an ifstream,
ofstream, or fstream." These are things that exist in every standard C++
implementation, because the C++ standard defines both `FILE` (cstdio) and
iostreams. It would be quite reasonable to propose a way to convert a
`FILE` into an `{if,of,f}stream`, and/or vice versa.

However, you'll have to figure out what is the right API for this. POSIX
uses plain old `int` for all kinds of file descriptors, regardless of
readability/writeability. C and C++ use `FILE*` for all kinds of files,
regardless of readability/writeability. But the C++ iostreams library
uses *different
types* — std::istream for readable files, std::ostream for writeable files.
What happens if I do
    FILE *fp = fopen("hello.txt", "r");
    std::ofstream out = std::ofstream(std::from_file, fp);
Does this throw an exception? Is it library UB? A proposal would have to
specify this kind of thing.
Another thing you'll need to figure out — and probably by actually trying
to implement this in a real library or two, not just on paper — is whether
it's even possible to create a plain `std::ofstream` from a `FILE`, or
whether you really need to create a whole new derived class, e.g.
`std::ofilestream` (by analogy to `ostringstream`). And then you'll have to
decide whether an `ofilestream` IS-AN `ofstream` or merely an `ostream`. See

Completely off the top of my head, I'd expect the "right" design would be
new classes analogous to <sstream>:
    #include <filestream>
    class ifilestream : public istream {};
    class ofilestream : public ostream {};
    class iofilestream : public iostream {};
And by the way, I'm sure you could write these classes today in standard
C++, if you really wanted to. Nico Josuttis's "fdstream" implementation is
only 200 lines long for the whole thing; replacing the `int fd` and `write`
with `FILE *fp` and `fwrite` probably wouldn't be very hard.

Peter asks: Can there be destruction without (explicit) close? — Yes, the
semantics of `fstream` are that if you destroy an `fstream` without
explicitly `.close()`'ing it first, then the destructor will close it for
you (and swallow any filesystem errors that might occur).

Paul also wrote:
> Looking at std::format [C++20], it outputs strings. std::print [C++23],
outputs to FILE*. Does this suggest iostream gets sidelined and FILE*
becomes more preferable?

No, at least not any more than it has been sidelined in real code for 20+
std::print has overloads taking `std::ostream&` alongside overloads taking
`FILE*`, so you will be able to write *either* of these:
    std::println(stdout, "hello world!");
    std::println(std::cout, "hello world!");
just as today you can write either of these:
    puts("hello world!");
    std::cout << "hello world!\n";


Received on 2022-09-02 16:15:43