Zasadniczo szukam wersji C ++ fdopen (). Zrobiłem trochę badań na ten temat i jest to jedna z tych rzeczy, które wydają się być łatwe, ale okazują się bardzo skomplikowane. Czy czegoś mi brakuje w tym przekonaniu (tj. To naprawdę jest łatwe)? Jeśli nie, czy jest gdzieś dobra biblioteka, która może się tym zająć?
EDYCJA: Przeniosłem moje przykładowe rozwiązanie do osobnej odpowiedzi.
c++
posix
fstream
file-descriptor
BD w Rivenhill
źródło
źródło
mmap
z plikiem i ujawnić jego zawartość jako tablicę bajtów.Odpowiedzi:
Z odpowiedzi udzielonej przez Érica Malenfanta:
Bazując na powyższych obserwacjach i moich badaniach poniżej, kod działa w dwóch wariantach; jeden dla libstdc ++, a drugi dla Microsoft Visual C ++.
libstdc ++
Istnieje niestandardowy
__gnu_cxx::stdio_filebuf
szablon klasy, który dziedziczystd::basic_streambuf
i ma następujący konstruktorstdio_filebuf (int __fd, std::ios_base::openmode __mode, size_t __size=static_cast< size_t >(BUFSIZ))
z opisem Ten konstruktor wiąże bufor strumienia plików z otwartym deskryptorem pliku POSIX.
Tworzymy go, przekazując uchwyt POSIX (linia 1), a następnie przekazujemy go do konstruktora istream jako basic_streambuf (linia 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 ++
Kiedyś istniała niestandardowa wersja konstruktora ifstream pobierającego deskryptor pliku POSIX, ale brakuje go zarówno w bieżącej dokumentacji, jak iw kodzie. Istnieje inna niestandardowa wersja konstruktora ifstream pobierająca PLIK *
explicit basic_ifstream(_Filet *_File) : _Mybase(&_Filebuffer), _Filebuffer(_File) { // construct with specified C stream }
i nie jest udokumentowane (nie mogłem nawet znaleźć żadnej starej dokumentacji, w której byłaby obecna). Nazywamy to (wiersz 1), a parametr jest wynikiem wywołania _fdopen w celu pobrania strumienia C PLIK * z uchwytu pliku POSIX.
#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; }
źródło
std::cout
implementację to dobry pomysł. Zastanawiam się, jaka jest różnica międzystdio_filebuf
istdio_sync_filebuf
?AFAIK, nie ma takiej możliwości w standardowym C ++. W zależności od platformy Twoja implementacja biblioteki standardowej może oferować (jako niestandardowe rozszerzenie) konstruktor fstream pobierający deskryptor pliku (tak jest w przypadku libstdc ++, IIRC) lub
FILE*
jako dane wejściowe.Inną alternatywą byłoby użycie urządzenia boost :: iostreams :: file_descriptor , które można opakować w boost :: iostreams :: stream, jeśli chcesz mieć do niego interfejs std :: stream.
źródło
Istnieje duża szansa, że Twój kompilator oferuje konstruktor fstream oparty na PLIKU, nawet jeśli jest niestandardowy. Na przykład:
FILE* f = fdopen(my_fd, "a"); std::fstream fstr(f); fstr << "Greetings\n";
Ale o ile wiem, nie ma na to przenośnego sposobu.
źródło
Częścią pierwotnej (nieokreślonej) motywacji tego pytania jest możliwość przekazywania danych między programami lub między dwoma częściami programu testowego przy użyciu bezpiecznie utworzonego pliku tymczasowego, ale tmpnam () wyświetla ostrzeżenie w gcc, więc chciałem zamiast tego użyć mkstemp (). Oto program testowy, który napisałem na podstawie odpowiedzi udzielonej przez Érica Malenfanta, ale używając mkstemp () zamiast fdopen (); to działa na moim systemie Ubuntu z zainstalowanymi bibliotekami Boost:
#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); } }
źródło
W rzeczywistości jest to całkiem proste. Nicolai M. Josuttis wydał
fdstream
w połączeniu ze swoją książką The C ++ Standard Library - A Tutorial and Reference . Implementację linii 184 można znaleźć tutaj .źródło
Wypróbowałem rozwiązanie zaproponowane powyżej dla libstdc ++ autorstwa Piotra Dobrogosta i stwierdziłem, że ma ono bolesną wadę: ze względu na brak odpowiedniego konstruktora ruchu dla istream bardzo trudno jest usunąć nowo skonstruowany obiekt istream z funkcji tworzącej . Innym problemem jest to, że przecieka obiekt FILE (nawet jeśli nie jest to podstawowy deskryptor pliku POSIX). Oto alternatywne rozwiązanie, które pozwala uniknąć tych problemów:
#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; }
Wywołanie posix_fadvise () demonstruje potencjalne zastosowanie. Zauważ również, że przykład używa static_assert i używa C ++ 11, poza tym, że powinien dobrze budować w trybie C ++ 03.
źródło
Rozumiem, że nie ma powiązania ze wskaźnikami FILE lub deskryptorami plików w modelu obiektowym C ++ iostream w celu zachowania przenośności kodu.
To powiedziawszy, widziałem kilka miejsc odnoszących się do mds-utils lub boost, aby pomóc wypełnić tę lukę.
źródło