この問題に対する私の好ましい解決策は、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);
}
}
これは望ましい動作をしています。
その他の注意事項:
- マルチスレッド コードがあり、C++ 呼び出しが GIL なしで行われている場合は、in および freearg タイプマップでそれぞれ and を呼び出す必要があります
PyFile_IncUseCount
。PyFile_DecUseCount
- 私は、それが与えられたオブジェクトがそうでない場合に
PyFile_AsFile
返すと仮定しました-ドキュメントはどちらの方法でもそれを指定していないようですので、確実に使用できます。NULL
file
PyFile_Check
- 非常に柔軟にしたい場合は、Python から文字列を受け入れ、/を
std::ifstream
使用して適切に を構築し、タイプマップで実行するアクションを決定できます。PyString_Check
PyFile_Check
- 一部の C++ 標準ライブラリは、拡張子として ,を取る
ifstream
/ofstream
コンストラクターを提供します。FILE*
それらのいずれかを持っている場合は、ブーストに頼る代わりに使用できます。