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


93

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

編集:サンプルソリューションを別の回答に移動しました。


@Kazark-別の回答に移動しました。ありがとうございます。
BDがRivenhillで2013

WindowsとLinux mmapはファイルに対して行うことができ、その内容をバイト配列として公開しました。
TruthAdjuster

回答:


72

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

私の知る限り、これを標準の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(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ファイル記述子を取得していましたが、現在のドキュメントとコードの両方から欠落しています。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;
}

2
今完全性のために受け入れられた答え。他の人は、別の答えに移動されたboostを使用した私のソリューションに興味があるかもしれません。
RivenhillのBD、2013

1
Linuxの場合:gccでios_init.ccを見る場合(私が持っているソースはバージョン4.1.1です)std :: coutは、ファイル記述子の周りにstdio_sync_filebuf <char>を初期化することによって初期化され、次にstdio_sync_filebuf <の周りのostreamで初期化されますchar>。ただし、これが安定しているとは言えません。
Sparky

@Sparky std::cout実装を調べることは良い考えです。私は違い何思ったんだけどstdio_filebufとはstdio_sync_filebuf
Piotr Dobrogost 16

MSVCのPOSIX fdsはエミュレーションです。ファイル操作用のWindows APIは、さまざまな点でPOSIXのものとは異なります-異なる関数名とパラメーターのデータ型。Windowsは、さまざまなWindows APIオブジェクトを識別するためにいわゆる「ハンドル」を内部的に使用し、Windows APIタイプHANDLEはvoid *として定義されています。最低でも、64ビットプラットフォームの「int」(32ビット)には適合しません。したがって、Windowsの場合、Windows APIファイルHANDLEを介して作業できるストリームを探すことに興味があるかもしれません。
ivan.ukr

40

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

もう1つの代替方法は、boost :: iostreams :: file_descriptorデバイスを使用することです。std :: streamインターフェースが必要な場合は、boost :: iostreams :: streamでラップできます。


4
これが唯一の移植可能なソリューションであることを考えると、これがなぜ受け入れられた、または最高評価の答えではないのか、私にはわかりません。
Maarten

8

標準ではありませんが、コンパイラがFILEベースのfstreamコンストラクタを提供する可能性は十分にあります。例えば:

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

しかし、私の知る限り、これを行うためのポータブルな方法はありません。


2
g ++は(正しく)c ++ 11モードではこれを許可しないことに注意してください
Mark K Cowan '18

8

この質問の元の(明言されていない)動機の一部は、安全に作成された一時ファイルを使用してプログラム間またはテストプログラムの2つの部分間でデータを渡すことができるようにすることですが、tmpnam()がgccで警告をスローするため、代わりにmkstemp()を使用します。これは、ÉricMalenfantの回答に基づいて、fdopen()ではなくmkstemp()を使用して作成したテストプログラムです。これは、Boostライブラリがインストールされている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);
  }
}


4

私はPiotr Dobrogostによってlibstdc ++のために上記で提案された解決策を試してみましたが、それには痛い欠陥があることがわかりました:istreamの適切なmoveコンストラクターがないため、新しく作成されたistreamオブジェクトを作成関数から取り出すのは非常に困難です。もう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()の呼び出しは、潜在的な使用法を示しています。また、この例ではstatic_assertusing C ++ 11 を使用していますが、C ++ 03モードでは問題なくビルドできることに注意してください。


適切なバージョンの移動コンストラクタとはどういう意味ですか?どのバージョンのgccを使用しましたか?多分このバージョンにはまだムーブコンストラクターが実装されていませんでした– ifsteamのムーブコンストラクターが暗黙的に削除されていますか?を参照してください
Piotr Dobrogost

1
これは、基礎となる実装の詳細に依存するハックです。誰もこれを製品コードで使用しないことを望みます。
davmac

-4

私の理解では、コードの移植性を維持するために、C ++ iostreamオブジェクトモデルではFILEポインターまたはファイル記述子との関連付けはありません。

とは言っても、mds-utilsまたはboostを参照して、そのギャップを埋めるのに役立つ場所がいくつかありました。


9
FILE *は標準Cであり、したがってC ++なので、C ++ストリームをCストリームと連携させると移植性がどのように
損なわれる
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.