私は基本的にC ++バージョンのfdopen()を探しています。私はこれについて少し調査しましたが、それは簡単なはずのように見えますが、非常に複雑であることがわかりました。私はこの信念に何か欠けていますか(つまり、本当に簡単です)?そうでない場合、これを処理するための良いライブラリがどこかにありますか?
編集:サンプルソリューションを別の回答に移動しました。
mmap
はファイルに対して行うことができ、その内容をバイト配列として公開しました。
私は基本的にC ++バージョンのfdopen()を探しています。私はこれについて少し調査しましたが、それは簡単なはずのように見えますが、非常に複雑であることがわかりました。私はこの信念に何か欠けていますか(つまり、本当に簡単です)?そうでない場合、これを処理するための良いライブラリがどこかにありますか?
編集:サンプルソリューションを別の回答に移動しました。
mmap
はファイルに対して行うことができ、その内容をバイト配列として公開しました。
回答:
エリック・マレンファントの答えから:
私の知る限り、これを標準のC ++で行う方法はありません。プラットフォームによっては、標準ライブラリの実装が、ファイル記述子を入力として受け取るfstreamコンストラクターを(非標準の拡張として)提供する場合があります。(これはlibstdc ++、IIRCの場合です)またはFILE *。
上記の観察と以下の私の研究に基づいて、2つのバリアントの作業コードがあります。1つはlibstdc ++用で、もう1つはMicrosoft Visual C ++用です。
次のコンストラクタ__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;
}
以前は標準ではないバージョンの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;
}
std::cout
実装を調べることは良い考えです。私は違い何思ったんだけどstdio_filebuf
とはstdio_sync_filebuf
?
私の知る限り、これを標準のC ++で行う方法はありません。プラットフォームに応じて、標準ライブラリの実装は、ファイル記述子(これはlibstdc ++、IIRCの場合)またはa FILE*
を受け取るfstreamコンストラクターを(非標準の拡張として)提供する場合があります。
もう1つの代替方法は、boost :: iostreams :: file_descriptorデバイスを使用することです。std :: streamインターフェースが必要な場合は、boost :: iostreams :: streamでラップできます。
標準ではありませんが、コンパイラがFILEベースのfstreamコンストラクタを提供する可能性は十分にあります。例えば:
FILE* f = fdopen(my_fd, "a");
std::fstream fstr(f);
fstr << "Greetings\n";
しかし、私の知る限り、これを行うためのポータブルな方法はありません。
この質問の元の(明言されていない)動機の一部は、安全に作成された一時ファイルを使用してプログラム間またはテストプログラムの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);
}
}
それは実際には非常に簡単です。Nicolai M. Josuttisはfdstream
、彼の著書The C ++ Standard Library-A Tutorial and Referenceとともにリリースされました。ここに 184行の実装があります。
私は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_assertとusing C ++ 11 を使用していますが、C ++ 03モードでは問題なくビルドできることに注意してください。