質問

C++ std::fstream から C FILE* ハンドルを取得する (クロスプラットフォームの) 方法はありますか?

私が尋ねる理由は、私の C++ ライブラリが fstream を受け入れ、ある特定の関数で FILE* を受け入れる C ライブラリを使用したいからです。

役に立ちましたか?

解決

簡単に言うと「ノー」です。

その理由は、 std::fstream を使用する必要はありません FILE* その実装の一環として。したがって、たとえファイル記述子を抽出できたとしても、 std::fstream オブジェクトを削除し、手動で FILE オブジェクトを構築すると、2 つのバッファリングされたオブジェクトが同じファイル記述子に書き込むことになるため、別の問題が発生します。

本当の質問は、なぜ変換したいのかということです。 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/~kreckel/fileno/ 状況を適切に説明し、少なくとも GCC に関しては、プラットフォーム固有の粗雑さをすべて隠すコードを提供します。これが GCC だけでどれほどひどいことかを考えると、できればこれをまとめて行うことは避けたいと思います。

アップデート:@Jettatura が最良の答えだと思うものを参照してください https://stackoverflow.com/a/33612982/225186 (Linux のみ?)。

オリジナル:

(おそらくクロスプラットフォームではありませんが、単純です)

ハッキングを簡素化する http://www.ginac.de/~kreckel/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. 重要だと思います fflushfprintf に印刷する std::ofstream, そうでない場合は、上の例では「sample2」が「sample1」の前に表示されます。を使用するよりも良い回避策があるかどうかはわかりません fflush. 。特に ofs << flush 役に立ちません。

  2. ファイル*を抽出できません 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 でこれを行うもう 1 つの方法:

#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