---

title: ファイルディスクリプタを C++のストリームに変換して利用する
description: GCC専用ではあるが、open(2)で開いたファイルもストリームとして扱う方法が実はある
date: 2021-09-20
image: cplusplus.png
tweet_url: https://twitter.com/shimariso/status/1439875388180602887
---

C++の入門記事でよくある

```c++
std::cout << "Hello, World!" << std::endl;
```

みたいな I/Oストリームを使用した記述は C から C++への移民にとっては「なんじゃこりゃ！！」と言いたくなるようなものだが、Cだと常にバッファ境界のことを念頭に置きながら書かなければならなかった I/O処理の記述が C++だとだいたいの場合簡潔にできるので（メモリの豊富な実行環境がターゲットなら）これを使わない手はない。

とはいえ、Cで書かれている部分との兼ね合いで生のファイルディスクリプタしか手に入らない場合もある。(GCCを使用しており移植性を考えなくて良い状況なら)下記のように [__gnu_cxx::stdio_filebuf](https://gcc.gnu.org/onlinedocs/gcc-11.2.0/libstdc++/api/a09131.html) を利用すればファイルディスクリプタを I/Oストリームで覆って C++の作法で利用することができる。また、生のファイルディスクリプタ以外に [fopen(3)](https://linuxjm.osdn.jp/html/LDP_man-pages/man3/fopen.3.html)した FILE* を取るコンストラクタもあるようだ。（試していない）

stdio_filebufのデストラクタで [close(2)](https://linuxjm.osdn.jp/html/LDP_man-pages/man2/close.2.html) が呼ばれるようなので、クローズせずに返却しなければならないファイルディスクリプタに対しては使わないこと。

```c++
#include <fcntl.h>
#include <unistd.h>

#include <iostream>
#include <fstream>
#include <ext/stdio_filebuf.h> // for __gnu_cxx::stdio_filebuf

int main()
{
    int fd = open("/etc/hostname", O_RDONLY);
    if (fd < 0) throw std::runtime_error("open(/etc/hostname) failed");

    std::string hostname;

    {
        __gnu_cxx::stdio_filebuf<char> filebuf(fd, std::ios::in);
        std::istream f(&filebuf);

        f >> hostname;
        // filebuf's scope ends here
    }

    std::cout << hostname << std::endl;

    // check whether fd has been closed
    if (close(fd) && errno == EBADF) {
        std::cout << "fd already closed(probably by stdio_filebuf's destructor)" << std::endl;
    }

    return 0;
}
```
