Kann man einen std::istream in ein C FILE* "pipen"



  • Hallo Zusammen,

    Ich habe folgendes Problem: Ich benutze eine C library die Daten parst in C++. Die Signatur der Funktion die ich aufrufen möchte, sieht etwa so aus:

    something *parse(FILE *stream);
    

    Nun habe ich aber einen std::istream mit den Daten (dieser holt seine Daten meistens über einen Socket, kann aber auch irgendwas Userspezifisches sein - ich schreibe eine Library). Da die Daten sehr gross werden können, will ich streamen und nicht alles zuerst ins memory lesen (zudem könnte ich das gar nicht, da ich die Daten parsen muss, um zu wissen, wann fertig ist).

    Daher meine Frage: gibt es eine Standard-Möglichkeit wie man einen std::istream in ein C FILE * schreiben kann? Was ich im Moment mache (und das funktioniert auch) ist folgendes:

    - Ich hole mir mir zuest zwei file descriptoren in dem ich eine Pipe öffne:

    int fd[2];
    int err = pipe(fd);
    

    - dann hole ich mir zwei FILE* objekte:

    FILE *infile = fdopen(fd[0], "r");
    FILE *outfile - fdopen(fd[1], "a");
    

    - Danach starte ich einen neuen thread und übergebe ihm outfile und den std::istream.
    - Der thread schreibt einfach von std::istream in outfile.
    - Der main-thread ruft dann die C-Funktion mit infile als parameter auf.

    Diese Lösung gefällt mir aber aus folgenden Gründen nicht:
    - wahrscheinlich nicht all zu performant, da viele zusätzliche System calls gemacht werden müssen
    - ist nicht Portabel (lies: funktioniert nur auf Posix Systemen) und ich würde meine Library zu einem späteren Zeitpunkt gerne auch auf Windows portieren.
    - ist nicht besonders elegant

    Kennt irgendjemand vielleicht eine bessere, portablere und elegantere Lösung? Für jegliche Tips bin ich sehr Dankbar.



  • Hallo,

    Sehe ich das richtig: Du bekommst Daten über einen std::istream und möchtest diese in einen FILE* schreiben? Falls ja, könntest du dir einen eigenen streambuf basteln und einen std::ostream damit erstellen. Oder du benutzt einfach direkt fwrite().

    Grüße,
    PI



  • Ich denke, er möchte eher Daten, die aus einem istream kommen, aus einem FILE* lesen können. Eine parse-Funktion, die schreibt, erscheint mir jedenfalls nicht so sinnig.

    Einen Standardweg dafür gibt es jedenfalls nicht.



  • 314159265358979 schrieb:

    Hallo,

    Sehe ich das richtig: Du bekommst Daten über einen std::istream und möchtest diese in einen FILE* schreiben? Falls ja, könntest du dir einen eigenen streambuf basteln und einen std::ostream damit erstellen. Oder du benutzt einfach direkt fwrite().

    Grüße,
    PI

    wie LordJaxom bemerkt hat, will ich aus einem FILE* einen std::istream lesen... Denke aber leider auch, dass es wirklich was besseres gibt als meine Lösung 😞



  • Ev. hilfts das:
    http://www.c-plusplus.net/forum/139122
    (Beitrag von Werner)



  • Das was Werner dort Skizziert ist genau der umgekehrte Fall. Andersrum wirds schwierig, weil man unter ein FILE* soweit ich weiß nicht einfach was anderes runterklemmen kann, oder? Für einen std::stream kann man ja immer den streambuf passend schreiben, so dass man quasi aus allem möglichen lesen kann...



  • Jo dieser Vorschlag macht leider das umgekehrte und das ist ziemlich einfach (std::streambuf habe ich schon mehrmals implementiert). Ich habe nun schon ziemlich viel gesucht und so wie es aussieht gibt es wirklich keine bessere Lösung als meine.



  • Musst du denn einen std::istream verwenden? Zumindest auf Unix kannst du den socket auch direkt an fdopen übergeben. Und auch Windows kennt ein _fdopen, ob das allerdings mit Sockets funktioniert weiß ich nicht.



  • In boost.iostreams gibt es einen Stream-Wrapper mit dem man Mithilfe eines Filedescriptors einen Stream anlegen kann. Und aus einem FILE* kann man sowohl unter *NIX als auch unter Windows mit fileno bekommen.
    Irgendwie so:

    void do_something_with_stream(std::ostream & ostr)
    {
        //...
    }
    
    int main()
    {
        FILE* file = fopen( "somefile.ext", "w" ) ;
        int fd = fileno(file) ;
    
        boost::iostreams::file_descriptor_sink fdsink(fd) ;
        boost::iostreams::stream< boost::iostreams::file_descriptor_sink > ostr( fdsink ) ;
        do_something_with_stream(ostr);
    }
    


  • Tachyon schrieb:

    [...]

    Das ist auch die Andersrum-Variante. Was der OP möchte ist folgendes:

    void doSomethingWithFILE(FILE* f);
    
    int foo()
    {
      std::istream iHaveIt;
      FILE* iNeedIt = fGimmeAFilePleaseMrCppStream(iHaveIt); //wie implementieren?
    
      doSomethingWithFILE(iNeedIt);
    }
    


  • manni66 schrieb:

    Musst du denn einen std::istream verwenden? Zumindest auf Unix kannst du den socket auch direkt an fdopen übergeben.

    Er schreibt eine Library, d.h. der Socket ist nicht gesetzt, es kann auch eine beliebige andere Quelle sein.


Log in to reply