7

std::[io]fstreamswigを介してPythonで 'sを使用する方法はありますか?

次のような関数を持つcクラスがあります。

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

Pythonでstd::ofstreamインスタンスを構築し、それを引数として渡したいwriteToと思います(そして、読み取りのために同じことを行います)。

のような関数を作ってみました

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

swig.iファイル内で、この関数がインターフェイスの一部になるようにします。ただし、これは機能しません。ストリーム クラスはコピーできないため、問題があります。

std_iostream.iジェネリック クラスの使用には役立つようですが、[io]stream必要なファイル ストリームの作成には役立ちません。

4

5 に答える 5

7

この問題に対する私の好ましい解決策は、Python 開発者に公開されるインターフェイスを可能な限り「Pythonic」にすることです。fileこの例では、Pythonオブジェクトをostream引数istreamとして受け入れることになります。

これを実現するには、typemap を記述して各マッピングを設定する必要があります。

これを実際に示すために、次のヘッダー ファイルを作成しました。

#ifndef TEST_HH
#define TEST_HH
#include <iosfwd>

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

テスト用にダミーの実装を次のように作成しました。

#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());
}

その場所で、次を使用して正常にラップできました。

%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) {
  int fd = -1;

  #if PY_VERSION_HEX >= 0x03000000
  fd = PyObject_AsFileDescriptor($input);
  #else 
  FILE *f=PyFile_AsFile($input); // Verify the semantics of this
  if (f) fd = fileno(f);
  #endif
  if (fd < 0) {
    SWIG_Error(SWIG_TypeError, "File object expected.");
    SWIG_fail;
  }
  else {
    // If threaded incrment the use count
    stream = new boost_ofdstream(fd, io::never_close_handle);
    $1 = new std::ostream(stream);
  }
}

%typemap(in) std::istream& (boost_ifdstream *stream=NULL) {
  int fd = -1;

  #if PY_VERSION_HEX >= 0x03000000
  fd = PyObject_AsFileDescriptor($input);
  #else 
  FILE *f=PyFile_AsFile($input); // Verify the semantics of this
  if (f) fd = fileno(f);
  #endif
  if (fd < 0) {
    SWIG_Error(SWIG_TypeError, "File object expected.");  
    SWIG_fail;
  }
  else {
    stream = new boost_ifdstream(fd, 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"

これの核心部分は、基本的に PythonオブジェクトからPyFile_AsFile()を取得する呼び出しです。これにより、必要に応じてファイル記述子をソース/シンクとして使用するブースト オブジェクトを構築できます。FILE*file

残っている唯一のことは、呼び出しが発生した後 (またはエラーによって呼び出しが発生しなかった場合) に作成したオブジェクトをクリーンアップすることです。

それが整ったら、Python 内から期待どおりに使用できます。

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()

バッファリングのセマンティクスは、たとえば out.txt で期待した結果を生成しない可能性があることに注意してください。

こんにちは
パイソン

これは呼び出しの逆の順序です。C++ ストリームを構築する前に、typemap 内file.flush()の Pythonオブジェクトでへの呼び出しを強制することによって、これを修正することもできます。file

%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);
  }
}

これは望ましい動作をしています。

その他の注意事項:

  1. マルチスレッド コードがあり、C++ 呼び出しが GIL なしで行われている場合は、in および freearg タイプマップでそれぞれ and を呼び出す必要がありますPyFile_IncUseCountPyFile_DecUseCount
  2. 私は、それが与えられたオブジェクトがそうでない場合にPyFile_AsFile返すと仮定しました-ドキュメントはどちらの方法でもそれを指定していないようですので、確実に使用できます。NULLfilePyFile_Check
  3. 非常に柔軟にしたい場合は、Python から文字列を受け入れ、/をstd::ifstream使用して適切に を構築し、タイプマップで実行するアクションを決定できます。PyString_CheckPyFile_Check
  4. 一部の C++ 標準ライブラリは、拡張子として ,を取るifstream/ofstreamコンストラクターを提供します。FILE*それらのいずれかを持っている場合は、ブーストに頼る代わりに使用できます。
于 2013-09-21T13:08:03.333 に答える
1

最終的には、インターフェイス内で使用する独自のプロキシ クラスを作成することになりました。そこで、SWIG を使用してこのクラスをラップしました。

 /**
 * 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;
};

そしてPython呼び出しで呼び出しを行います

>>> proxy=ifstream_proxy()
>>> proxy.open('file_to_read_from.txt')
>>> readFrom( stream_proxy.stream() )
于 2013-09-18T17:59:38.223 に答える
1

swigはわかりませんが、コピー可能なオブジェクトを作成する必要があると仮定すると、次のような関数を使用して逃げることができます

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

...そして、転送関数を使用して、実際に呼び出したい関数を呼び出します。

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

(名前をオーバーロードすると問題が発生する場合は、もちろん、転送関数を別の方法で呼び出すことができます)。

于 2013-09-17T22:25:34.707 に答える
0

共有ポインターを使用するという Dietmar の提案に基づいた .i ファイルの作業:

%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!
于 2013-09-20T13:19:52.713 に答える