LoginSignup
0
1

More than 1 year has passed since last update.

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

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

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


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

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

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


#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の方が資料的価値が高そうということに気づきました。


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

20220110

pybind11のウェブサイトを見ていたら、protectedメンバはusingでpublicに書き換えられることが判明。というわけでこれだけで良かった…C++はやはりワカラナイ…

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;
}
0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1