문제

C++ std::fstream 에서 C FILE* 핸들을 얻는 (크로스 플랫폼) 방법이 있습니까?

내가 묻는 이유는 내 C++ 라이브러리가 fstream을 허용하고 특정 함수에서 FILE*을 허용하는 C 라이브러리를 사용하고 싶기 때문입니다.

도움이 되었습니까?

해결책

짧은 대답은 '아니요'입니다.

그 이유는, std::fstream 반드시 사용할 필요는 없습니다. FILE* 구현의 일부로.따라서 파일 설명자를 추출하더라도 std::fstream 개체를 만들고 수동으로 FILE 개체를 빌드하면 이제 동일한 파일 설명자에 두 개의 버퍼링된 개체를 쓰게 되므로 다른 문제가 발생하게 됩니다.

진짜 질문은 왜 std::fstream 에 반대하다 FILE*?

추천하진 않지만 찾아보시면 될 것 같아요 funopen().
불행하게도 이것은 ~ 아니다 POSIX API(BSD 확장)이므로 이식성이 문제입니다.그래서 아마도 내가 포장한 사람을 찾을 수 없는 이유일 것이다. std::stream 이런 개체로.

FILE *funopen(
              const void *cookie,
              int    (*readfn )(void *, char *, int),
              int    (*writefn)(void *, const char *, int),
              fpos_t (*seekfn) (void *, fpos_t, int),
              int    (*closefn)(void *)
             );

이를 통해 다음을 구축할 수 있습니다. FILE 객체를 지정하고 실제 작업을 수행하는 데 사용될 일부 기능을 지정합니다.적절한 함수를 작성하면 해당 함수가 다음에서 읽도록 할 수 있습니다. std::fstream 실제로 파일이 열려 있는 개체입니다.

다른 팁

표준화된 방법은 없습니다.나는 이것이 C++ 표준화 그룹이 파일 핸들이 fd로 표현될 수 있다고 가정하고 싶지 않았기 때문이라고 가정합니다.

대부분의 플랫폼은 이를 수행하는 비표준적인 방법을 제공하는 것 같습니다.

http://www.ginac.de/~krekel/fileno/ 상황에 대한 좋은 기록을 제공하고 적어도 GCC의 경우 모든 플랫폼별 총체성을 숨기는 코드를 제공합니다.이것이 GCC에서 얼마나 심한지 생각하면 가능하다면 이 모든 작업을 함께 수행하는 것을 피하는 것이 좋을 것 같습니다.

업데이트:@Jettatura를 참조하세요. 이것이 최선의 답변이라고 생각합니다. https://stackoverflow.com/a/33612982/225186 (리눅스만?)

원래의:

(아마도 크로스 플랫폼은 아니지만 간단합니다)

해킹 단순화 http://www.ginac.de/~krekel/fileno/ (dvorak 답변) 이 gcc 확장을 보면 http://gcc.gnu.org/onlinedocs/gcc-4.6.2/libstdc++/api/a00069.html#a59f78806603c619eafcd4537c920f859,이 솔루션이 작동합니다 GCC (적어도 4.8) 그리고 clang (적어도 3.3)

#include<fstream>
#include<ext/stdio_filebuf.h>

typedef std::basic_ofstream<char>::__filebuf_type buffer_t;
typedef __gnu_cxx::stdio_filebuf<char>            io_buffer_t; 
FILE* cfile_impl(buffer_t* const fb){
    return (static_cast<io_buffer_t* const>(fb))->file(); //type std::__c_file
}

FILE* cfile(std::ofstream const& ofs){return cfile_impl(ofs.rdbuf());}
FILE* cfile(std::ifstream const& ifs){return cfile_impl(ifs.rdbuf());}

이것을 사용할 수 있습니다.

int main(){
    std::ofstream ofs("file.txt");
    fprintf(cfile(ofs), "sample1");
    fflush(cfile(ofs)); // ofs << std::flush; doesn't help 
    ofs << "sample2\n";
}

제한사항: (의견은 환영합니다)

  1. 나는 다음이 중요하다고 생각한다. fflush ~ 후에 fprintf 인쇄 std::ofstream, 그렇지 않으면 위 예에서 "sample2"가 "sample1" 앞에 나타납니다.사용하는 것보다 더 나은 해결 방법이 있는지 모르겠습니다. fflush.특히 ofs << flush 도움이 되지 않습니다.

  2. 에서 FILE*을 추출할 수 없습니다. std::stringstream, 가능한지조차 모르겠습니다.(업데이트 내용은 아래 참조)

  3. 아직 C를 추출하는 방법을 모르겠습니다. stderr ~에서 std::cerr 등, 예를 들어 다음에서 사용합니다. fprintf(stderr, "sample"), 다음과 같은 가상의 코드에서 fprintf(cfile(std::cerr), "sample").

마지막 제한 사항과 관련하여 제가 찾은 유일한 해결 방법은 다음 오버로드를 추가하는 것입니다.

FILE* cfile(std::ostream const& os){
    if(std::ofstream const* ofsP = dynamic_cast<std::ofstream const*>(&os)) return cfile(*ofsP);
    if(&os == &std::cerr) return stderr;
    if(&os == &std::cout) return stdout;
    if(&os == &std::clog) return stderr;
    if(dynamic_cast<std::ostringstream const*>(&os) != 0){
       throw std::runtime_error("don't know cannot extract FILE pointer from std::ostringstream");
    }
    return 0; // stream not recognized
}
FILE* cfile(std::istream const& is){
    if(std::ifstream const* ifsP = dynamic_cast<std::ifstream const*>(&is)) return cfile(*ifsP);
    if(&is == &std::cin) return stdin;
    if(dynamic_cast<std::ostringstream const*>(&is) != 0){
        throw std::runtime_error("don't know how to extract FILE pointer from std::istringstream");
    }
    return 0; // stream not recognized
}

처리를 시도합니다 iostringstream

으로 읽으면 가능하다 fscanf ~에서 istream 사용하여 fmemopen, 그러나 C 읽기와 C++ 읽기를 결합하려는 경우 각 읽기 후에 스트림의 입력 위치를 유지하고 업데이트하는 데 많은 양의 책이 필요합니다.이것을 로 변환할 수 없었습니다. cfile 위와 같은 기능을 합니다.(아마도 cfile 수업 읽을 때마다 계속 업데이트하는 것이 좋습니다).

// hack to access the protected member of istreambuf that know the current position
char* access_gptr(std::basic_streambuf<char, std::char_traits<char>>& bs){
    struct access_class : std::basic_streambuf<char, std::char_traits<char>>{
        char* access_gptr() const{return this->gptr();}
    };
    return ((access_class*)(&bs))->access_gptr();
}

int main(){
    std::istringstream iss("11 22 33");
    // read the C++ way
    int j1; iss >> j1;
    std::cout << j1 << std::endl;

    // read the C way
    float j2;

    char* buf = access_gptr(*iss.rdbuf()); // get current position
    size_t buf_size = iss.rdbuf()->in_avail(); // get remaining characters
    FILE* file = fmemopen(buf, buf_size, "r"); // open buffer memory as FILE*
    fscanf(file, "%f", &j2); // finally!
    iss.rdbuf()->pubseekoff(ftell(file), iss.cur, iss.in); // update input stream position from current FILE position.

    std::cout << "j2 = " << j2 << std::endl;

    // read again the C++ way
    int j3; iss >> j3;
    std::cout << "j3 = " << j3 << std::endl;
}

글쎄, 파일 설명자를 얻을 수 있습니다. 메소드가 fd()인지 getfd()인지는 잊어버렸습니다. 내가 사용한 구현은 그러한 방법을 제공하지만 언어 표준에서는 이를 요구하지 않는다고 생각합니다. 표준은 귀하의 플랫폼이 파일에 fd를 사용하는지 여부를 신경 쓰지 않아야 합니다.

여기에서 fdopen(fd, mode)를 사용하여 FILE*을 얻을 수 있습니다.

그러나 표준에서 STDIN/cin, STDOUT/cout 및 STDERR/cerr을 동기화하기 위해 요구하는 메커니즘이 사용자에게 표시될 필요는 없다고 생각합니다.따라서 fstream과 FILE*을 모두 사용하는 경우 버퍼링으로 인해 문제가 발생할 수 있습니다.

또한 fstream이나 FILE 중 하나가 닫히면 기본 fd도 닫힐 수 있으므로 둘 중 하나를 닫기 전에 둘 다 플러시해야 합니다.

단일 스레드 POSIX 애플리케이션에서는 이식 가능한 방식으로 쉽게 fd 번호를 얻을 수 있습니다.

int fd = dup(0);
close(fd);
// POSIX requires the next opened file descriptor to be fd.
std::fstream file(...);
// now fd has been opened again and is owned by file

이 코드가 파일 설명자를 여는 다른 스레드와 경쟁하는 경우 이 메서드는 다중 스레드 응용 프로그램에서 중단됩니다.

Linux에서 이를 수행하는 또 다른 방법은 다음과 같습니다.

#include <stdio.h>
#include <cassert>

template<class STREAM>
struct STDIOAdapter
{
    static FILE* yield(STREAM* stream)
    {
        assert(stream != NULL);

        static cookie_io_functions_t Cookies =
        {
            .read  = NULL,
            .write = cookieWrite,
            .seek  = NULL,
            .close = cookieClose
        };

        return fopencookie(stream, "w", Cookies);
    }

    ssize_t static cookieWrite(void* cookie,
        const char* buf,
        size_t size)
    {
        if(cookie == NULL)
            return -1;

        STREAM* writer = static_cast <STREAM*>(cookie);

        writer->write(buf, size);

        return size;
    }

    int static cookieClose(void* cookie)
    {
         return EOF;
    }
}; // STDIOAdapter

사용법은 다음과 같습니다.

#include <boost/iostreams/filtering_stream.hpp>
#include <boost/iostreams/filter/bzip2.hpp>
#include <boost/iostreams/device/file.hpp>

using namespace boost::iostreams;

int main()
{   
    filtering_ostream out;
    out.push(boost::iostreams::bzip2_compressor());
    out.push(file_sink("my_file.txt"));

    FILE* fp = STDIOAdapter<filtering_ostream>::yield(&out);
    assert(fp > 0);

    fputs("Was up, Man", fp);

    fflush (fp);

    fclose(fp);

    return 1;
}

파일 설명자를 가져오는 방법이 있습니다. fstream 그런 다음 다음으로 변환하십시오. FILE* (을 통해 fdopen).개인적으로 딱히 필요는 없어보이네요 FILE*, 하지만 파일 설명자를 사용하면 리디렉션(dup2).

해결책:

#define private public
#define protected public
#include <fstream>
#undef private
#undef protected

std::ifstream file("some file");
auto fno = file._M_filebuf._M_file.fd();

마지막 문자열은 libstdc++에서 작동합니다.다른 라이브러리를 사용하는 경우 약간의 리버스 엔지니어링이 필요합니다.

이 트릭은 더럽고 fstream의 모든 비공개 및 공개 구성원을 노출시킵니다.프로덕션 코드에서 사용하려면 별도로 생성하는 것이 좋습니다. .cpp 그리고 .h 단일 기능으로 int getFdFromFstream(std::basic_ios<char>& fstr);.헤더 파일에는 fstream이 포함되어서는 안 됩니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top