Dav*_*ave 7 c++ python file-io swig
有没有办法std::[io]fstream通过swig在python中使用?
我有一个c-class,其功能如下:
void readFrom(std::istream& istr);
void writeTo(std::ostream& ostr);
Run Code Online (Sandbox Code Playgroud)
我想在python中构造一个std::ofstream实例并将其作为参数传递给writeTo(并为读取做同样的事情).
我尝试过像这样的功能
std::ostream& make_ostream(const std::string& file_name){
return std::ofstream( file_name.c_str() );
}
Run Code Online (Sandbox Code Playgroud)
在swig .i文件中,这个函数将成为接口的一部分.但这不起作用.由于流类是不可复制的,因此存在问题.
虽然std_iostream.i似乎有助于使用泛型[io]stream类,但它无助于制作我需要的文件流.
我对这个问题的首选解决方案是将接口暴露给Python开发人员尽可能"Pythonic".在这种情况下,这将是接受蟒蛇file对象作为你ostream和istream参数.
为了实现这一点,我们必须编写一个typemap来设置每个映射.
我编写了以下头文件来演示这个实际操作:
#ifndef TEST_HH
#define TEST_HH
#include <iosfwd>
void readFrom(std::istream& istr);
void writeTo(std::ostream& ostr);
#endif
Run Code Online (Sandbox Code Playgroud)
我为测试编写了一个虚拟实现:
#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());
}
Run Code Online (Sandbox Code Playgroud)
有了这个,我能够使用以下方法成功包装:
%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"
Run Code Online (Sandbox Code Playgroud)
这个核心部分基本上PyFile_AsFile()是FILE*从Python file对象中获取一个.然后,我们可以构造一个boost对象,该对象使用文件描述符作为源/接收器.
唯一剩下的就是在调用发生后清理我们创建的对象(或者如果错误阻止了调用的发生).
有了这个,我们就可以在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()
Run Code Online (Sandbox Code Playgroud)
请注意,缓冲语义可能不会产生您期望的结果,例如在out.txt中我得到:
你好
Python
这是与电话相反的顺序.我们也可以通过在构造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);
}
}
Run Code Online (Sandbox Code Playgroud)
哪个有所需的行为.
其他说明:
PyFile_IncUseCount,并PyFile_DecUseCount分别在中和freearg typemaps,以确保没有任何东西可以,而你还在使用它关闭文件.PyFile_AsFile返回NULL如果它给出的对象不是file-文档似乎并没有指定任何一种方式,因此您可以使用PyFile_Check以确保万无一失.std::ifstream使用PyString_Check/ PyFile_Check来构造一个适当的用来决定在typemap中采取哪个动作.ifstream/ ofstream构造函数,它FILE*作为扩展.如果你有其中一个你可以使用它而不是依靠提升.我不知道 swig 但假设你需要创建一个可复制的对象,你可能会使用像这样的函数
std::shared_ptr<std::ostream> make_ostream(std::string const& filename) {
return std::make_shared<std::ofstream>(filename);
}
Run Code Online (Sandbox Code Playgroud)
...然后使用转发函数来调用您实际要调用的函数:
void writeTo(std::shared_ptr<std::ostream> stream) {
if (stream) {
writeTo(*stream);
}
}
Run Code Online (Sandbox Code Playgroud)
(如果重载名称导致问题,当然,您可以以不同的方式调用转发函数)。