Technik zur Verwendung von std :: ifstream, std :: ofstream in python über SWIG?

Gibt es eine Möglichkeit, std::[io]fstream 's in python über swig zu benutzen?

Ich habe eine C-Klasse mit Funktionen wie:

 void readFrom(std::istream& istr); void writeTo(std::ostream& ostr); 

Ich möchte in python eine std::ofstream Instanz zu konstruieren und es als das Argument zu writeTo zu writeTo (und das gleiche zum Lesen zu tun).

Ich habe versucht, eine Funktion wie zu machen

 std::ostream& make_ostream(const std::string& file_name){ return std::ofstream( file_name.c_str() ); } 

Innerhalb der swig .i Datei, damit diese Funktion Teil der Schnittstelle wäre. Das geht aber nicht. Es gibt ein Problem, da die Stream-Klassen nicht kopierbar sind.

Obwohl std_iostream.i bei der Verwendung der generischen [io]stream Klassen zu helfen scheint, hilft es nicht bei der Herstellung der Datei Streams, die ich brauche.

4 Solutions collect form web for “Technik zur Verwendung von std :: ifstream, std :: ofstream in python über SWIG?”

Ich weiß nicht, Swig aber vorausgesetzt, Sie müssen ein kopierbares Objekt zu erstellen, können Sie weg mit einer Funktion wie

 std::shared_ptr<std::ostream> make_ostream(std::string const& filename) { return std::make_shared<std::ofstream>(filename); } 

… und dann eine Weiterleitungsfunktion verwenden, um die Funktion anzurufen, die du eigentlich anrufen möchte:

 void writeTo(std::shared_ptr<std::ostream> stream) { if (stream) { writeTo(*stream); } } 

(Wenn Überlastung die Namen Ursachen verursacht, können Sie die Weiterleitungsfunktion anders anrufen, natürlich).

Meine bevorzugte Lösung für dieses Problem wäre, die Schnittstelle Python-Entwickler als "Pythonic" wie möglich ausgesetzt zu machen. In diesem Fall wäre das, Python- file Objekte als Ihre ostream und istream Argumente zu akzeptieren.

Um dies zu erreichen, müssen wir eine Schreibweise schreiben, um jede Mapping einzurichten.

Ich habe die folgende Header-Datei geschrieben, um dies in Aktion zu demonstrieren:

 #ifndef TEST_HH #define TEST_HH #include <iosfwd> void readFrom(std::istream& istr); void writeTo(std::ostream& ostr); #endif 

Was ich schrieb eine Dummy-Implementierung für die Prüfung als:

 #include <iostream> #include <cassert> #include "test.hh" void readFrom(std::istream& istr) { assert(istr.good()); std::cout << istr.rdbuf() << "\n"; } void writeTo(std::ostream& ostr) { assert(ostr.good()); ostr << "Hello" << std::endl; assert(ostr.good()); } 

Mit diesem Platz konnte ich es erfolgreich umwickeln:

 %module test %{ #include <stdio.h> #include <boost/iostreams/stream.hpp> #include <boost/iostreams/device/file_descriptor.hpp> namespace io = boost::iostreams; typedef io::stream_buffer<io::file_descriptor_sink> boost_ofdstream; typedef io::stream_buffer<io::file_descriptor_source> boost_ifdstream; %} %typemap(in) std::ostream& (boost_ofdstream *stream=NULL) { FILE *f=PyFile_AsFile($input); // Verify the semantics of this if (!f) { SWIG_Error(SWIG_TypeError, "File object expected."); SWIG_fail; } else { // If threaded incrment the use count stream = new boost_ofdstream(fileno(f), io::never_close_handle); $1 = new std::ostream(stream); } } %typemap(in) std::istream& (boost_ifdstream *stream=NULL) { FILE *f=PyFile_AsFile($input); // Verify that this returns NULL for non-files if (!f) { SWIG_Error(SWIG_TypeError, "File object expected."); SWIG_fail; } else { stream = new boost_ifdstream(fileno(f), io::never_close_handle); $1 = new std::istream(stream); } } %typemap(freearg) std::ostream& { delete $1; delete stream$argnum; } %typemap(freearg) std::istream& { delete $1; delete stream$argnum; } %{ #include "test.hh" %} %include "test.hh" 

Das Kernstück davon ist grundsätzlich PyFile_AsFile() , um eine FILE* aus dem Python- file Objekt zu erhalten. Damit können wir dann ein Boost-Objekt konstruieren, das einen Dateideskriptor als Quelle / Senke verwendet.

Das einzige, was bleibt, ist, die Objekte zu bereinigen, die wir nach dem Aufruf erstellt haben (oder wenn ein Fehler den Anruf verhindert hat).

Mit dem an Ort und Stelle können wir es dann wie erwartet aus Python verwenden:

 import test outf=open("out.txt", "w") inf=open("in.txt", "r") outf.write("Python\n"); test.writeTo(outf) test.readFrom(inf) outf.close() inf.close() 

Beachten Sie die Pufferung Semantik möglicherweise nicht die Ergebnisse, die Sie erwartet haben, zum Beispiel in out.txt bekomme ich:

Hallo
Python

Das ist die entgegengesetzte Reihenfolge der Anrufe. Wir können das auch beheben, indem wir einen Aufruf von file.flush() auf dem Python-Dateiobjekt in unserer Typkarte erzwingen, bevor wir einen C ++ – Stream erstellen:

 %typemap(in) std::ostream& (boost_ofdstream *stream=NULL) { PyObject_CallMethod($input, "flush", NULL); FILE *f=PyFile_AsFile($input); // Verify the semantics of this if (!f) { SWIG_Error(SWIG_TypeError, "File object expected."); SWIG_fail; } else { // If threaded incrment the use count stream = new boost_ofdstream(fileno(f), io::never_close_handle); $1 = new std::ostream(stream); } } 

Was das gewünschte verhalten hat

Weitere Hinweise:

  1. Wenn du Multithread-Code hast und die C ++ – Anrufe ohne die GIL passieren, musst du PyFile_IncUseCount und PyFile_DecUseCount in den PyFile_IncUseCount und PyFile_DecUseCount Typemaps aufrufen, um sicherzustellen, dass nichts die Datei schließen kann, während du es immer noch benutzt hast.
  2. Ich habe davon ausgegangen, dass PyFile_AsFile NULL zurückgibt, wenn das Objekt, das es gegeben hat, keine file – die Dokumentation scheint das nicht so anzugeben, also könntest du PyFile_Check , um sicher zu sein.
  3. Wenn du super flexibel sein wolltest, könntest du Strings von Python akzeptieren und einen std::ifstream wie PyString_Check mit PyString_Check / PyFile_Check , um zu entscheiden, welche Aktion in die typkarte eingegangen werden soll.
  4. Einige C ++ – Standardbibliotheken stellen einen ifstream / ofstream Konstruktor ofstream , der FILE* als Erweiterung benötigt. Wenn du eines von denen hast, kannst du es benutzen, anstatt dich auf den Boost zu verlassen.

Ich habe am Ende nur meine eigene Proxy-Klasse für den Einsatz innerhalb der Schnittstelle. Also habe ich SWIG benutzt, um diese Klasse zu verpacken:

  /** * Simple class to expose std::streams in the python * interface. works around some issues with trying to directy * the file stream objects */ class ifstream_proxy: boost::noncopyable{ public: ifstream_proxy(): m_istr(){ // no op } virtual ~ifstream_proxy(){ // no op } void open(const std::string& fname ){ m_istr.close(); m_istr.open( fname.c_str(), std::ifstream::in|std::ifstream::binary) ; } std::istream& stream(){ return m_istr; } // TBD: do I want to add additional stream manipulation functions? private: std::ifstream m_istr; }; 

Und in python call rufen sie an

 >>> proxy=ifstream_proxy() >>> proxy.open('file_to_read_from.txt') >>> readFrom( stream_proxy.stream() ) 

Working .i-Datei auf der Grundlage von Dietmars Vorschlag, gemeinsame Zeiger zu verwenden:

 %module ptrtest %include "boost_shared_ptr.i" %include "std_string.i" %shared_ptr( std::ostream ) %{ #include <iostream> #include <fstream> #include <boost/shared_ptr.hpp> typedef boost::shared_ptr< std::ostream > ostream_ptr; ostream_ptr mk_out(const std::string& fname ){ return ostream_ptr( new std::ofstream( fname.c_str() ) ); } void writeTo(std::ostream& ostr){ ostr<<"OK"<<std::endl; } %} namespace std{ class ostream{ public: // I think we could expose things like write,put here // and actually make this class useful from python protected: // Need to declare this as protected otherwise swig tries // to make/use a public default constructor. ostream(); }; } // Tell swig to put these into the interface typedef boost::shared_ptr< std::ostream > ostream_ptr; ostream_ptr mk_out(const std::string& fname ); void writeTo(std::ostream& ostr); // Usage: //>>>ostr=mk_out('/path/to/file.txt') //>>>writeTo(ostr) # no need to cast/call-function! 
  • Probleme beim Installieren von M2Crypto auf Mint
  • SWIG_SHARED_PTR Makro mit vorbereiteter Klasse
  • Mit stdint mit swig und numpy.i
  • SWIG C-to-Python Int Array
  • Wie kann Python binäre Daten (char *) von C ++ von SWIG bekommen?
  • Wie soll ich einen Codegenerator testen?
  • % Typemap und% Ausnahme für Fehlercodes aus C-Funktionen für SWIG, Python
  • Python ctypes Rückruffunktion an SWIG
  • Swig: wie man void * in generische Funktion übergibt
  • Extrahieren von SWIG-Ummantelten C ++ - Instanz / Zeiger für den Einsatz in Cython
  • Verwenden Sie Methode, die von der Abbruch-C ++ - Klasse in einer Python-Klasse (SWIG)
  • Python ist die beste Programmiersprache der Welt.