POSIX 파일 설명자에서 C ++ fstream을 구성하는 방법은 무엇입니까?


93

기본적으로 fdopen ()의 C ++ 버전을 찾고 있습니다. 나는 이것에 대해 약간의 조사를했고 그것이 쉬울 것 같지만 매우 복잡한 것으로 밝혀진 것 중 하나입니다. 나는이 믿음에서 무언가를 놓치고 있는가 (즉, 정말 쉽다)? 그렇지 않다면 어딘가에 이것을 처리 할 수있는 좋은 라이브러리가 있습니까?

편집 : 예제 솔루션을 별도의 답변으로 옮겼습니다.


@Kazark-이제 별도의 답변으로 이동했습니다.
BD at Rivenhill 2013

Windows와 Linux는 mmap파일을 처리하고 그 내용을 바이트 배열로 노출 할 수 있습니다 .
truthadjustr

답변:


72

Éric Malenfant의 답변에서 :

AFAIK, 표준 C ++에서는이 작업을 수행 할 방법이 없습니다. 플랫폼에 따라 표준 라이브러리 구현은 파일 설명자를 입력으로 사용하는 fstream 생성자를 (비표준 확장으로) 제공 할 수 있습니다. (libstdc ++, IIRC의 경우) 또는 FILE *.

위의 관찰과 아래의 연구를 기반으로 두 가지 변형으로 작동하는 코드가 있습니다. 하나는 libstdc ++ 용이고 다른 하나는 Microsoft Visual C ++ 용입니다.


libstdc ++

다음 생성자 __gnu_cxx::stdio_filebuf를 상속 std::basic_streambuf하고 갖는 비표준 클래스 템플릿이 있습니다.

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

with description 이 생성자는 파일 스트림 버퍼를 열린 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;
}

마이크로 소프트 비주얼 C ++

POSIX 파일 설명자를 사용하는 ifstream 생성자의 비표준 버전 이 있었지만 현재 문서와 코드에서 모두 누락되었습니다 . 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;
}

2
이제 완전성으로 인해 받아 들여지는 대답입니다. 다른 사람들은 boost를 사용하는 내 솔루션에 관심이있을 수 있으며 별도의 답변으로 이동되었습니다.
BD at Rivenhill 2013

1
Linux의 경우 : gcc에서 ios_init.cc를 보면 (내가 가지고있는 소스는 버전 4.1.1 용입니다) std :: cout은 파일 설명자 주변에 stdio_sync_filebuf <char>를 초기화 한 다음 stdio_sync_filebuf <주변의 ostream에서 초기화하여 초기화됩니다. char>. 나는 이것이 안정 될 것이라고 주장 할 수 없다.
Sparky

@Sparky std::cout구현을 살펴 보는 것은 좋은 생각입니다. stdio_filebuf과 의 차이점이 무엇인지 궁금합니다 stdio_sync_filebuf.
Piotr Dobrogost

MSVC의 POSIX fd는 에뮬레이션입니다. 파일 작업을위한 Windows API는 POSIX API와 여러 가지면에서 다릅니다-다른 함수 이름과 매개 변수의 데이터 유형. Windows는 내부적으로 소위 "핸들"을 사용하여 다양한 Windows API 개체를 식별하고 Windows API 유형 HANDLE은 void *로 정의됩니다. 최소한 64 비트 플랫폼에서 "int"(32 비트)에 맞지 않습니다. 따라서 Windows의 경우 Windows API 파일 HANDLE에서 작업 할 수있는 스트림을 찾는 데 관심이있을 수 있습니다.
ivan.ukr

40

AFAIK, 표준 C ++에서는이 작업을 수행 할 방법이 없습니다. 플랫폼에 따라 표준 라이브러리 구현은 파일 설명자 (libstdc ++, IIRC의 경우) 또는 FILE*입력을 사용 하는 fstream 생성자를 비표준 확장으로 제공 할 수 있습니다 .

또 다른 대안은 boost :: iostreams :: file_descriptor 장치 를 사용하는 것입니다. std :: stream 인터페이스를 사용하려면 boost :: iostreams :: stream으로 래핑 할 수 있습니다.


4
이것이 유일한 휴대용 솔루션이라는 점을 고려할 때 이것이 허용되거나 최고 등급의 답변이 아닌 이유를 이해할 수 없습니다.
Maarten

8

비표준 임에도 불구하고 컴파일러가 FILE 기반 fstream 생성자를 제공 할 가능성이 높습니다. 예를 들면 :

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

그러나 내가 아는 한, 이것을 할 수있는 휴대용 방법은 없습니다.


2
g ++ (올바르게)는 C ++ 11 모드에서이를 허용하지 않습니다
Mark K Cowan

8

이 질문의 원래 (명시되지 않은) 동기의 일부는 안전하게 생성 된 임시 파일을 사용하여 프로그램간에 또는 테스트 프로그램의 두 부분간에 데이터를 전달할 수있는 기능을 갖는 것이지만 tmpnam ()은 gcc에서 경고를 표시하므로 원했습니다. 대신 mkstemp ()를 사용하십시오. 다음은 Éric Malenfant의 답변을 기반으로 작성했지만 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);
  }
}


4

위의 Piotr Dobrogost가 libstdc ++에 대해 제안한 솔루션을 시도한 결과 고통스러운 결함이 있음을 발견했습니다. istream에 대한 적절한 이동 생성자가 없기 때문에 새로 구성된 istream 개체를 생성 기능에서 가져 오는 것이 매우 어렵습니다. . 또 다른 문제는 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사용 하고 C ++ 11을 사용 하는 것 외에는 C ++ 03 모드에서 잘 빌드되어야합니다.


이동 생성자의 적절한 버전 이란 무엇을 의미 합니까? 어떤 버전의 gcc를 사용하셨습니까? 이 버전에는 아직 이동 생성자가 구현되지 않았을 수 있습니다. ifsteam의 이동 생성자가 암시 적으로 삭제 되었습니까?를 참조하십시오 . ?
Piotr Dobrogost

1
이것은 기본 구현 세부 사항에 의존하는 해킹입니다. 아무도 이것을 프로덕션 코드에서 사용하지 않기를 바랍니다.
davmac

-4

내 이해는 코드 이식성을 유지하기 위해 C ++ iostream 개체 모델의 FILE 포인터 또는 파일 설명자와 관련이 없다는 것입니다.

즉, 여러 곳에서 mds-utils 또는 boost를 참조하여 그 격차를 해소하는 것을 보았습니다 .


9
FILE *이 표준 C 때문에 C ++ 나 C ++를 활성화하면 휴대 손상시킬 수 스트림 C와 작업 스트림을 표시되지 않습니다 그래서입니다
표트르 Dobrogost
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.