Como construir um c + + fstream a partir de um descritor de arquivo POSIX?

Estou basicamente procurando por uma versão C ++ de fdopen (). Eu fiz um pouco de pesquisa sobre isso e é uma daquelas coisas que parece que deve ser fácil, mas acaba por ser muito complicado. Estou faltando alguma coisa nessa crença (ou seja, é realmente fácil)? Se não, há uma boa biblioteca em algum lugar para lidar com isso?

EDIT: Mudei minha solução de exemplo para uma resposta separada.

Da resposta dada por Éric Malenfant:

AFAIK, não há como fazer isso no C ++ padrão. Dependendo da sua plataforma, sua implementação da biblioteca padrão pode oferecer (como uma extensão não padronizada) um construtor fstream, tomando um descritor de arquivo como input. (Este é o caso de libstdc ++, IIRC) ou um FILE *.

Com base nas observações acima e na minha pesquisa abaixo, há código de trabalho em duas variantes; um para libstdc ++ e outro para o Microsoft Visual C ++.


libstdc ++

Existe um modelo de class __gnu_cxx::stdio_filebuf padrão que herda std::basic_streambuf e possui o seguinte construtor

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

com descrição Esse construtor associa um buffer de stream de arquivo a um descritor de arquivo POSIX aberto.

Nós criamos passando o identificador POSIX (linha 1) e depois passamos para o construtor do istream como basic_streambuf (linha 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 ++

Costumava haver uma versão não padronizada do construtor ifstream, tomando o descritor de arquivo POSIX, mas faltando os documentos atuais e o código. Existe outra versão não padronizada do construtor ifstream levando FILE *

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

e não está documentado (não consegui encontrar nenhuma documentação antiga em que estaria presente). Nós o chamamos (linha 1) com o parâmetro sendo o resultado da chamada de _fdopen para obter o stream C FILE * do manipulador de arquivo 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, não há como fazer isso no C ++ padrão. Dependendo da sua plataforma, sua implementação da biblioteca padrão pode oferecer (como uma extensão não padronizada) um construtor fstream, tendo um descritor de arquivo (Este é o caso de libstdc ++, IIRC) ou um FILE* como uma input.

Outra alternativa seria usar um dispositivo boost :: iostreams :: file_descriptor , que você poderia include em um boost :: iostreams :: stream se você quer ter uma interface std :: stream para ele.

Há uma boa chance de seu compilador oferecer um construtor de stream baseado em FILE, mesmo que não seja padrão. Por exemplo:

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

Mas, tanto quanto sei, não há maneira portátil de fazer isso.

Parte da motivação original (não declarada) desta questão é ter a habilidade de passar dados entre programas ou entre duas partes de um programa de teste usando um arquivo temporário criado com segurança, mas tmpnam () lança um aviso no gcc, então eu queria para usar mkstemp () em vez disso. Aqui está um programa de teste que eu escrevi baseado na resposta dada por Éric Malenfant mas usando mkstemp () ao invés de fdopen (); isso funciona no meu sistema Ubuntu com bibliotecas 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); } } 

Eu tentei a solução proposta acima para libstdc ++ por Piotr Dobrogost e descobri que ela tinha uma falha dolorosa: devido à falta de um construtor de movimento adequado para istream, é muito difícil obter o object istream recém-construído fora da function de criação . Outro problema é que ele vaza um object FILE (mesmo que não seja o descritor de arquivo posix subjacente). Aqui está uma solução alternativa que evita esses 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; } 

A chamada para posix_fadvise () demonstra um uso potencial. Observe também que o exemplo usa static_assert e usa quais são C ++ 11, além de que ele deve ser construído bem no modo C ++ 03.

Na verdade é bem fácil. Nicolai M. Josuttis lançou fdstream em conjunto com seu livro The C ++ Standard Library – Um tutorial e referência . Você pode implementar a linha 184 aqui .

Meu entendimento é que não há nenhuma associação com pointers FILE ou descritores de arquivo no modelo de object C ++ iostream para manter o código portátil.

Dito isso, vi vários lugares se referirem aos mds-utils ou boost para ajudar a preencher essa lacuna.