web-dev-qa-db-ja.com

POSIXファイル記述子からc ++ fstreamを構築する方法は?

基本的にfdopen()のC++バージョンを探しています。私はこれについて少し調査しましたが、簡単なはずのように思えますが、非常に複雑であることが判明しています。この信念に何かが欠けていますか(つまり、本当に簡単です)?そうでない場合、これを処理するための良いライブラリがどこかにありますか?

編集:私のソリューション例を別の答えに移動しました。

86
BD at Rivenhill

エリック・マレンファントの答えから:

私の知る限り、標準C++でこれを行う方法はありません。プラットフォームに応じて、標準ライブラリの実装は、入力としてファイル記述子を使用するfstreamコンストラクターを(非標準の拡張機能として)提供する場合があります。 (これはlibstdc ++、IIRCの場合)またはFILE *です。

上記の所見と以下の私の調査に基づいて、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と​​してistreamのコンストラクターに渡します(2行目)。

#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++

以前はPOSIXファイル記述子を取得するifstreamのコンストラクタの非標準 version がありましたが、 current docsとコードの両方から欠落しています。 FILE *を使用するifstreamのコンストラクターの別の非標準バージョンがあります

explicit basic_ifstream(_Filet *_File)
    : _Mybase(&_Filebuffer),
        _Filebuffer(_File)
    {   // construct with specified C stream
    }

そして、それは文書化されていません(それが存在するであろう古い文書さえ見つけられませんでした)。 POSIXファイルハンドルからCストリームFILE *を取得するために _ fdopen を呼び出した結果であるパラメーターで呼び出します(1行目)。

#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;
}
66
Piotr Dobrogost

私の知る限り、標準C++でこれを行う方法はありません。プラットフォームに応じて、標準ライブラリの実装は、(非標準の拡張機能として)ファイル記述子(これはlibstdc ++、IIRCの場合)またはFILE*入力として。

別の代替方法は、 boost :: iostreams :: file_descriptor デバイスを使用することです。これは、stdが必要な場合は boost :: iostreams :: stream でラップできます::それへのストリームインターフェース。

38
Éric Malenfant

この質問の元の(述べられていない)動機の一部は、安全に作成された一時ファイルを使用してプログラム間またはテストプログラムの2つの部分間でデータを渡す機能を持つことですが、tmpnam()はgccで警告をスローします代わりにmkstemp()を使用します。以下は、ÉricMalenfantの回答に基づいて作成したテストプログラムですが、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);
  }
}
8
BD at Rivenhill

コンパイラは、たとえ非標準であっても、FILEベースのfstreamコンストラクターを提供する可能性があります。例えば:

FILE* f = fdopen(my_fd, "a");
std::fstream fstr(f);
fstr << "Greetings\n";

しかし、私の知る限り、これを実行するための移植可能な方法はありません。

8
Darryl

Piotr Dobrogostによってlibstdc ++に対して上記で提案されたソリューションを試してみましたが、痛みを伴う欠陥があることがわかりました。 。もう1つの問題は、FILEオブジェクトがリークすることです(基礎となる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()の呼び出しは、潜在的な用途を示しています。また、この例では、C++ 11であるstatic_assertおよびsingを使用している点に注意してください。

4
YitzikC

実際には非常に簡単です。 Nicolai M. Josuttisは彼の本と一緒にfdstreamをリリースしました C++標準ライブラリ-チュートリアルとリファレンス 。 184行の実装を見つけることができます こちら

4
Mark