パイプとかファイルディスクリプタとか、そーゆー低レイヤーのはなし | |
---|---|
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です。 - 以下の手順により達成可能です。
- basic_filebufを継承したクラス(basic_filebufFd)を宣言します。
- basic_filebufFdを、元のbasic_filebuf(rdbuf)の右辺値参照により実体化(rdbufFd)します。
- rdbufFd上で必要な処理を行います。
- 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
# 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;
}