
BD in Rivenhill
Ich suche im Grunde nach einer C++-Version von fdopen(). Ich habe ein wenig darüber recherchiert und es ist eines dieser Dinge, die so aussehen, als sollten sie einfach sein, sich aber als sehr kompliziert herausstellen. Übersehe ich etwas in diesem Glauben (dh es ist wirklich einfach)? Wenn nicht, gibt es irgendwo eine gute Bibliothek, um damit umzugehen?
BEARBEITEN: Meine Beispiellösung wurde in eine separate Antwort verschoben.

Piotr Dobrogost
Aus der Antwort von Éric Malenfant:
AFAIK, es gibt keine Möglichkeit, dies in Standard-C++ zu tun. Abhängig von Ihrer Plattform kann Ihre Implementierung der Standardbibliothek (als nicht standardmäßige Erweiterung) einen fstream-Konstruktor anbieten, der einen Dateideskriptor als Eingabe verwendet. (Dies ist der Fall bei libstdc++, IIRC) oder einer FILE*.
Basierend auf den obigen Beobachtungen und meinen Recherchen unten gibt es funktionierenden Code in zwei Varianten; eine für libstdc++ und eine für Microsoft Visual C++.
libstdc++
Es gibt Nicht-Standard __gnu_cxx::stdio_filebuf
Klassenvorlage, die erbt std::basic_streambuf
und hat den folgenden Konstruktor
stdio_filebuf (int __fd, std::ios_base::openmode __mode, size_t __size=static_cast< size_t >(BUFSIZ))
mit Beschreibung Dieser Konstruktor ordnet einen Dateistrompuffer einem offenen POSIX-Dateideskriptor zu.
Wir erstellen es, indem wir das POSIX-Handle (Zeile 1) übergeben, und übergeben es dann als basic_streambuf (Zeile 2) an den Konstruktor von 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++
Früher gab es Nicht-Standard Ausführung des Konstruktors von ifstream, der den POSIX-Dateideskriptor verwendet, aber es fehlen beide Strom docs und aus dem Code. Es gibt eine andere nicht standardmäßige Version des Konstruktors von ifstream, die FILE * verwendet
explicit basic_ifstream(_Filet *_File)
: _Mybase(&_Filebuffer),
_Filebuffer(_File)
{ // construct with specified C stream
}
und es ist nicht dokumentiert (ich konnte nicht einmal eine alte Dokumentation finden, in der es vorhanden wäre). Wir rufen es auf (Zeile 1), wobei der Parameter das Ergebnis des Aufrufs ist _fdöffnen um C-Stream FILE* vom POSIX-Dateihandle zu erhalten.
#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;
}

Eric Malenfant
AFAIK, es gibt keine Möglichkeit, dies in Standard-C++ zu tun. Abhängig von Ihrer Plattform kann Ihre Implementierung der Standardbibliothek (als nicht standardmäßige Erweiterung) einen fstream-Konstruktor anbieten, der einen Dateideskriptor verwendet (dies ist der Fall für libstdc++, IIRC) oder a FILE*
als Eingang.
Eine andere Alternative wäre die Verwendung von a boost::iostreams::file_descriptor Gerät, das Sie in ein wickeln könnten boost::iostreams::stream wenn Sie eine std::stream-Schnittstelle dazu haben möchten.
Es besteht eine gute Chance, dass Ihr Compiler einen FILE-basierten fstream-Konstruktor anbietet, obwohl er nicht dem Standard entspricht. Zum Beispiel:
FILE* f = fdopen(my_fd, "a");
std::fstream fstr(f);
fstr << "Greetings\n";
Aber soweit ich weiß, gibt es keine tragbare Möglichkeit, dies zu tun.
Ein Teil der ursprünglichen (nicht angegebenen) Motivation dieser Frage besteht darin, Daten entweder zwischen Programmen oder zwischen zwei Teilen eines Testprogramms mithilfe einer sicher erstellten temporären Datei übertragen zu können, aber tmpnam() gibt eine Warnung in gcc aus, also wollte ich um stattdessen mkstemp() zu verwenden. Hier ist ein Testprogramm, das ich basierend auf der Antwort von Éric Malenfant geschrieben habe, aber mkstemp() anstelle von fdopen() verwende; Dies funktioniert auf meinem Ubuntu-System mit installierten Boost-Bibliotheken:
#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);
}
}

Kennzeichen
Es ist eigentlich ganz einfach. Nicolai M. Josuttis wurde entlassen fdstream
in Verbindung mit seinem Buch Die C++-Standardbibliothek – ein Tutorial und eine Referenz. Sie finden die 184-Zeilen-Implementierung Hier.

Isac Casapu
Ich habe die oben vorgeschlagene Lösung für libstdc++ von Piotr Dobrogost ausprobiert und festgestellt, dass sie einen schmerzhaften Fehler hatte: Aufgrund des Fehlens eines geeigneten Bewegungskonstruktors für istream ist es sehr schwierig, das neu konstruierte istream-Objekt aus der Erstellungsfunktion herauszuholen . Ein weiteres Problem dabei ist, dass ein FILE-Objekt verloren geht (obwohl nicht der zugrunde liegende Posix-Dateideskriptor). Hier ist eine alternative Lösung, die diese Probleme vermeidet:
#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;
}
Der Aufruf von posix_fadvise() demonstriert eine mögliche Verwendung. Beachten Sie auch, dass das Beispiel verwendet static_assert und verwenden die C++ 11 sind, ansonsten sollte es im C++ 03-Modus gut gebaut werden.

Sockel
Mein Verständnis ist, dass es keine Zuordnung zu FILE-Zeigern oder Dateideskriptoren im C++-Iostream-Objektmodell gibt, um Code portabel zu halten.
Das heißt, ich habe mehrere Orte gesehen, auf die verwiesen wird mds-utils oder steigern, um diese Lücke zu schließen.
9902300cookie-checkWie konstruiere ich einen C++-Fstream aus einem POSIX-Dateideskriptor?yes
Windows und Linux können das
mmap
in die Datei und stellte ihren Inhalt als Byte-Array bereit.– Eigenfeld
21. April 2020 um 21:57 Uhr