POSIXファイル記述子からC ++ FSTREAMを作成する方法は?
-
02-10-2019 - |
質問
基本的に、fdopen()のC ++バージョンを探しています。私はこれについて少し研究しましたが、それは簡単だと思われるものの1つですが、非常に複雑であることが判明しました。私はこの信念に何かが足りませんか(つまり、本当に簡単です)?そうでない場合、これを処理するためのどこかに良い図書館がありますか?
編集:私の例の解決策を別の答えに移動しました。
解決
エリック・マレンファントによって与えられた答えから:
AFAIK、標準のC ++でこれを行う方法はありません。プラットフォームに応じて、標準ライブラリの実装は、(標準以外の拡張機能として)入力としてファイル記述子を取得するFSTREAMコンストラクターを提供する場合があります。 (これは、libstdc ++、iirc)またはファイル*の場合です。
上記の観察と以下の私の研究に基づいて、2つのバリアントの作業コードがあります。 1つはlibstdc ++用、もう1つはMicrosoft Visual C ++用です。
libstdc ++
非標準があります __gnu_cxx::stdio_filebuf
継承するクラステンプレート std::basic_streambuf
次のコンストラクターがあります
stdio_filebuf (int __fd, std::ios_base::openmode __mode, size_t __size=static_cast< size_t >(BUFSIZ))
説明付き このコンストラクターは、ファイルストリームバッファーを開いたPOSIXファイル記述子と関連付けます。
Posixハンドル(1行目)を通過すると作成し、Basic_StreamBuf(行2)としてISTreamのコンストラクターに渡します。
#include <ext/stdio_filebuf.h>
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
int main()
{
ofstream ofs("test.txt");
ofs << "Writing to a basic_ofstream object..." << endl;
ofs.close();
int posix_handle = fileno(::fopen("test.txt", "r"));
__gnu_cxx::stdio_filebuf<char> filebuf(posix_handle, std::ios::in); // 1
istream is(&filebuf); // 2
string line;
getline(is, line);
cout << "line: " << line << std::endl;
return 0;
}
Microsoft Visual C ++
以前は標準以外のものでした バージョン ifstreamのコンストラクターのposixファイル記述子を取得しているが、両方から欠落している 現在 ドキュメントとコードから。 IFStreamのコンストラクターのファイルを取得する別の標準バージョン*があります*
explicit basic_ifstream(_Filet *_File)
: _Mybase(&_Filebuffer),
_Filebuffer(_File)
{ // construct with specified C stream
}
文書化されていません(古いドキュメントが存在する場所を見つけることさえできませんでした)。私たちはそれを(1行目)と呼び、パラメーターは呼び出しの結果です _fdopen posixファイルハンドルからcストリームファイル*を取得します。
#include <cstdio>
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
int main()
{
ofstream ofs("test.txt");
ofs << "Writing to a basic_ofstream object..." << endl;
ofs.close();
int posix_handle = ::_fileno(::fopen("test.txt", "r"));
ifstream ifs(::_fdopen(posix_handle, "r")); // 1
string line;
getline(ifs, line);
ifs.close();
cout << "line: " << line << endl;
return 0;
}
他のヒント
AFAIK、標準のC ++でこれを行う方法はありません。プラットフォームに応じて、標準ライブラリの実装は(標準以外の拡張機能として)ファイル記述子を取得するFSTREAMコンストラクターを提供する場合があります(これはLIBSTDC ++、IIRCの場合です)または FILE*
入力として。
別の選択肢は、aを使用することです boost :: ioStreams :: file_descriptor デバイス、aで包むことができます ブースト:: ioStreams :: stream STD ::ストリームインターフェイスが必要な場合。
コンパイラが標準以外であっても、ファイルベースのFSTREAMコンストラクターを提供する可能性は十分にあります。例えば:
FILE* f = fdopen(my_fd, "a");
std::fstream fstr(f);
fstr << "Greetings\n";
しかし、私の知る限り、これを行うポータブルな方法はありません。
この質問のオリジナルの(未定の)動機の一部は、安全に作成された一時ファイルを使用してプログラムまたはテストプログラムの2つの部分の間でデータを渡すことができるが、TMPNAM()がGCCで警告を投げることができるので、私は望んでいたので、代わりにMKSTEMP()を使用します。以下は、Eric Malenfantによって与えられた回答に基づいて書いたテストプログラムですが、fdopen()の代わりにmkStemp()を使用しています。これは、ブーストライブラリがインストールされている私のubuntuシステムで機能します。
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <string>
#include <iostream>
#include <boost/filesystem.hpp>
#include <boost/iostreams/device/file_descriptor.hpp>
#include <boost/iostreams/stream.hpp>
using boost::iostreams::stream;
using boost::iostreams::file_descriptor_sink;
using boost::filesystem::path;
using boost::filesystem::exists;
using boost::filesystem::status;
using boost::filesystem::remove;
int main(int argc, const char *argv[]) {
char tmpTemplate[13];
strncpy(tmpTemplate, "/tmp/XXXXXX", 13);
stream<file_descriptor_sink> tmp(mkstemp(tmpTemplate));
assert(tmp.is_open());
tmp << "Hello mkstemp!" << std::endl;
tmp.close();
path tmpPath(tmpTemplate);
if (exists(status(tmpPath))) {
std::cout << "Output is in " << tmpPath.file_string() << std::endl;
std::string cmd("cat ");
cmd += tmpPath.file_string();
system(cmd.c_str());
std::cout << "Removing " << tmpPath.file_string() << std::endl;
remove(tmpPath);
}
}
Piotr dobrogostが上記のLibstdc ++の上記で提案したソリューションを試してみましたが、痛みを伴う欠陥があることがわかりました。ISTREAMの適切な移動コンストラクターがないため、作成機能の作成から新しく構築されたISTREAMオブジェクトを取得することは非常に困難です。 。それに関するもう1つの問題は、ファイルオブジェクトをリークすることです(基礎となるPOSIXファイル記述子ではないと考えています)。これらの問題を回避する代替ソリューションは次のとおりです。
#include <fstream>
#include <string>
#include <ext/stdio_filebuf.h>
#include <type_traits>
bool OpenFileForSequentialInput(ifstream& ifs, const string& fname)
{
ifs.open(fname.c_str(), ios::in);
if (! ifs.is_open()) {
return false;
}
using FilebufType = __gnu_cxx::stdio_filebuf<std::ifstream::char_type>;
static_assert( std::is_base_of<ifstream::__filebuf_type, FilebufType>::value &&
(sizeof(FilebufType) == sizeof(ifstream::__filebuf_type)),
"The filebuf type appears to have extra data members, the cast might be unsafe");
const int fd = static_cast<FilebufType*>(ifs.rdbuf())->fd();
assert(fd >= 0);
if (0 != posix_fadvise(fd, 0, 0, POSIX_FADV_SEQUENTIAL)) {
ifs.close();
return false;
}
return true;
}
posix_fadvise()への呼び出しは、潜在的な使用を示しています。また、この例が使用していることにも注意してください static_assert と 使用 C ++ 11であり、それ以外はC ++ 03モードで正常に構築されるはずです。
実際には非常に簡単です。ニコライ・M・ジョスティスがリリースしました fdstream
彼の本と一緒に C ++標準ライブラリ - チュートリアルと参照. 。 184行の実装を見つけることができます ここ.
私の理解では、コードをポータブルに保つために、C ++ iOSTREAMオブジェクトモデルにファイルポインターまたはファイル記述子との関連がないことです。
とはいえ、私はいくつかの場所を参照していました MDS-Utils または、そのギャップを埋めるのを助けるためにブースト。