¿Cómo construir un c ++ fstream desde un descriptor de archivo POSIX?

Básicamente estoy buscando una versión C ++ de fdopen (). Hice un poco de investigación sobre esto y es una de esas cosas que parece que debería ser fácil, pero resulta ser muy complicado. ¿Me estoy perdiendo algo en esta creencia (es decir, realmente es fácil)? Si no, ¿hay una buena biblioteca en algún lado para manejar esto?

EDITAR: Moví mi solución de ejemplo a una respuesta separada.

De la respuesta dada por Éric Malenfant:

AFAIK, no hay forma de hacer esto en C ++ estándar. Dependiendo de su plataforma, su implementación de la biblioteca estándar puede ofrecer (como una extensión no estándar) un constructor fstream que tome un descriptor de archivo como entrada. (Este es el caso de libstdc ++, IIRC) o un ARCHIVO *.

En base a las observaciones anteriores y mi investigación a continuación, hay código de trabajo en dos variantes; uno para libstdc ++ y otro para Microsoft Visual C ++.


libstdc ++

Hay una plantilla de clase __gnu_cxx::stdio_filebuf no estándar que hereda std::basic_streambuf y tiene el siguiente constructor

 stdio_filebuf (int __fd, std::ios_base::openmode __mode, size_t __size=static_cast< size_t >(BUFSIZ)) 

con descripción Este constructor asocia un buffer de flujo de archivos con un descriptor de archivo POSIX abierto.

Lo creamos pasando el identificador POSIX (línea 1) y luego lo pasamos al constructor de istream como basic_streambuf (línea 2):

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

Solía ​​haber una versión no estándar del constructor de ifstream que toma el descriptor de archivo POSIX, pero falta tanto de los documentos actuales como del código. Hay otra versión no estándar del constructor de ifstream que toma FILE *

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

y no está documentado (ni siquiera pude encontrar documentación antigua donde estaría presente). Lo llamamos (línea 1) con el parámetro que es el resultado de llamar a _fdopen para obtener C stream FILE * del manejador de archivo POSIX.

 #include  #include  #include  #include  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; } 

AFAIK, no hay forma de hacer esto en C ++ estándar. Dependiendo de su plataforma, su implementación de la biblioteca estándar puede ofrecer (como una extensión no estándar) un constructor fstream que tome un descriptor de archivo (Este es el caso de libstdc ++, IIRC) o un FILE* como entrada.

Otra alternativa sería utilizar un dispositivo boost :: iostreams :: file_descriptor , que podría incluir en un boost :: iostreams :: stream si desea tener una interfaz std :: stream para él.

Existe una buena posibilidad de que el comstackdor ofrezca un constructor de fstream basado en FILE, aunque no sea estándar. Por ejemplo:

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

Pero hasta donde sé, no hay una forma portátil de hacerlo.

Parte de la motivación original (no explícita) de esta pregunta es tener la capacidad de pasar datos entre progtwigs o entre dos partes de un progtwig de prueba utilizando un archivo temporal creado de manera segura, pero tmpnam () arroja una advertencia en gcc, por lo que quería para usar mkstemp () en su lugar. Aquí hay un progtwig de prueba que escribí basado en la respuesta dada por Éric Malenfant pero usando mkstemp () en lugar de fdopen (); esto funciona en mi sistema Ubuntu con las bibliotecas de Boost instaladas:

 #include  #include  #include  #include  #include  #include  #include  #include  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 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); } } 

Probé la solución propuesta anteriormente para libstdc ++ por Piotr Dobrogost, y descubrí que tenía un defecto doloroso: debido a la falta de un constructor de movimiento apropiado para istream, es muy difícil sacar el objeto istream recién construido de la función creadora. . Otro problema con esto es que filtra un objeto FILE (aunque no es el descriptor subyacente del archivo POSIX). Aquí hay una solución alternativa que evita estos problemas:

 #include  #include  #include  #include  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; static_assert( std::is_base_of::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(ifs.rdbuf())->fd(); assert(fd >= 0); if (0 != posix_fadvise(fd, 0, 0, POSIX_FADV_SEQUENTIAL)) { ifs.close(); return false; } return true; } 

La llamada a posix_fadvise () demuestra un uso potencial. También tenga en cuenta que el ejemplo utiliza static_assert y con el cual son C ++ 11, aparte de que debe construir bien en el modo C ++ 03.

En realidad es bastante fácil. Nicolai M. Josuttis ha lanzado fdstream junto con su libro The C ++ Standard Library – A Tutorial and Reference . Puede la implementación de la línea 184 aquí .

Según entiendo, no existe una asociación con los punteros FILE o los descriptores de archivos en el modelo de objetos C ++ iostream para mantener el código portátil.

Dicho esto, vi que varios lugares se refieren a mds-utils o boost para ayudar a cerrar esa brecha.