fstreamのファイルディスクリプタを取得する

| |パイプとかファイルディスクリプタとか、そーゆー低レイヤーのはなし|
|:--|:--|
|boost::processの子プロセスの入力を閉じる|https://qiita.com/cielavenir/items/9219162170cf2dd8b144|
|サブプロセス処理をパイプを使わずに行う|https://qiita.com/cielavenir/items/0e69848b705fafec86e1|
|fstreamのファイルディスクリプタを取得する<br>ムーブコンストラクタしかないクラスのprotectedメンバを呼び出す|https://qiita.com/cielavenir/items/fe892c564e6b12983776|

----

2022年01月10日の追記分が最新の方法です。

----

- `_M_file.fd()`を呼び出せば達成できますが、このメンバはprotectedです。
- 以下の手順により達成可能です。
    1. basic_filebufを継承したクラス(basic_filebufFd)を宣言します。
    1. basic_filebufFdを、元のbasic_filebuf(rdbuf)の右辺値参照により実体化(rdbufFd)します。
    1. rdbufFd上で必要な処理を行います。
    1. rdbufに、rdbufFdをbasic_filebufにキャストしたものをムーブします。
- 結果として、ムーブコンストラクタしかないクラスのprotectedメンバを呼び出すサンプルにもなってしまっています--;;;

----

- 最近の記事についてずっと言えることですが、移植性がありません。
   - libstdc++ https://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/include/std/fstream は対応しています。
   - libc++ https://github.com/llvm/llvm-project/blob/master/libcxx/include/fstream は対応していません。
   - MSVC STL https://github.com/microsoft/STL/blob/master/stl/inc/fstream は対応していません。
       - これらは`FILE*`がprivate宣言されているためどうしようもないと思われます。

※libstdc++であれば、g++/clang++ともに意図した動作をします。

いやーもう https://github.com/gcc-mirror/gcc/commit/a0bda314a53cb42fbbc2012c0241409b3e8be5d3 に感謝やなorz

----

```cpp
#include <fstream>
#include <stdio.h>

template<typename _CharT>
class basic_filebufFd : public std::basic_filebuf<_CharT>
{
public:
    basic_filebufFd(std::basic_filebuf<_CharT>&& other):std::basic_filebuf<_CharT>(std::move(other)){
    }
    int fd()
    {
        return this->_M_file.fd();
    }
};
int _GetFstreamFd(std::ios &f){
    if(std::fstream* fin = dynamic_cast<std::fstream*>(&f)){
        if(!fin->is_open()){
            return -1;
        }
        std::basic_filebuf<std::fstream::char_type> &rdbuf = *fin->rdbuf();
        basic_filebufFd<std::fstream::char_type> rdbufFd(std::move(rdbuf));
        int fd = rdbufFd.fd();
        rdbuf = std::move( *(std::remove_reference<decltype(rdbuf)>::type*)&rdbufFd );
        return fd;
    }
    if(std::ifstream* fin = dynamic_cast<std::ifstream*>(&f)){
        if(!fin->is_open()){
            return -1;
        }
        std::basic_filebuf<std::ifstream::char_type> &rdbuf = *fin->rdbuf();
        basic_filebufFd<std::ifstream::char_type> rdbufFd(std::move(rdbuf));
        int fd = rdbufFd.fd();
        rdbuf = std::move( *(std::remove_reference<decltype(rdbuf)>::type*)&rdbufFd );
        return fd;
    }
    if(std::ofstream* fin = dynamic_cast<std::ofstream*>(&f)){
        if(!fin->is_open()){
            return -1;
        }
        std::basic_filebuf<std::ofstream::char_type> &rdbuf = *fin->rdbuf();
        basic_filebufFd<std::ofstream::char_type> rdbufFd(std::move(rdbuf));
        int fd = rdbufFd.fd();
        rdbuf = std::move( *(std::remove_reference<decltype(rdbuf)>::type*)&rdbufFd );
        return fd;
    }
    return -1;
}

int main(){
    std::fstream f("hello.txt",std::ios::in | std::ios::out | std::ios::trunc);
    f.write("hello\n",6);
    f.seekp(0,std::ios::beg);
    f.flush();
    std::istream &in = f;
    if(1){
        int fd=_GetFstreamFd(f);
        if(fd<0){
            puts("failed to get fd");
            return 1;
        }
        std::string fname = std::string("/dev/fd/")+std::to_string(fd);
        puts("fd read");
        system(std::string("cat "+fname).c_str());
    }
    puts("rdbuf read");
    std::string line;
    std::getline(f,line);
    puts(line.c_str());
}
```

## 20200527

今更ながら、`typeof(rdbuf)`よりも`std::remove_reference<decltype(rdbuf)>::type`の方が資料的価値が高そうということに気づきました。

----

- <font color="white">これはまじな話なんですが、boost::processもcielavenir/mini7zも導入が却下されたため、放心状態が続いていますorz　まだ審議中だけど、俺的には(boost::processやcielavenir/mini7zを使った版と比べて)こっちのほうがよほどクソコードなんですよね…</font>

----

## 20220110

pybind11のウェブサイトを見ていたら、[protectedメンバはusingでpublicに書き換えられることが判明](https://pybind11.readthedocs.io/en/stable/advanced/classes.html#binding-protected-member-functions)。というわけでこれだけで良かった…C++はやはりワカラナイ…

```cpp
template<typename _CharT>
class basic_filebuf_Mfile : public std::basic_filebuf<_CharT>
{
public:
    using std::basic_filebuf<_CharT>::_M_file;
};
int _GetFstreamFd(std::ios &f){
    if(std::fstream* fin = dynamic_cast<std::fstream*>(&f)){
        if(!fin->is_open()){
            return -1;
        }
        return ((basic_filebuf_Mfile<std::fstream::char_type>*)fin->rdbuf())->_M_file.fd();
    }
    if(std::ifstream* fin = dynamic_cast<std::ifstream*>(&f)){
        if(!fin->is_open()){
            return -1;
        }
        return ((basic_filebuf_Mfile<std::ifstream::char_type>*)fin->rdbuf())->_M_file.fd();
    }
    if(std::ofstream* fin = dynamic_cast<std::ofstream*>(&f)){
        if(!fin->is_open()){
            return -1;
        }
        return ((basic_filebuf_Mfile<std::ofstream::char_type>*)fin->rdbuf())->_M_file.fd();
    }
    return -1;
}
```