使用swig将python StringIO传递给C++字符串流

Passing python StringIO to C++ stringstream using swig

本文关键字:C++ 字符串 swig python StringIO 使用      更新时间:2023-10-16

我有一些python代码,可以生成StringIO变量。我想使用字符串流参数将这个变量传递给C++函数(假设C++字符串流与python StringIO最匹配)。有没有一种简单的方法使用swig来进行翻译?

我在swig模块中看到一个std_stream.I,它包含一些字符串流代码,但在swig文档中找不到任何对它的引用,也找不到它在网络搜索中使用的任何示例。

为了让它更加清晰,我将在下面包含一些非常简单的代码。这是test.cpp,它是固定的,我不能更改:

#include <iostream>
#include <sstream>
#include "test.h"
using namespace std;
void StTest(stringstream &ss)
{
    cout << ss << endl;
}

这是test.h,也是固定的:

#include <sstream>
using namespace std;
void StTest(stringstream &ss);

这是我可以编辑的python代码,它调用C++StTest函数(我们将称之为test_test.py):

#!/usr/bin/env python
import StringIO
import test
ss = StringIO.StringIO()
ss.write("Hello")
# Maybe some thing here to convert ss from StringIO to stringstream
test.StTest(ss)

因此,我在上面的评论行中寻找可以从StringIO转换为stringstream的东西。

以下是我对测试的初步挖掘

/* test.i */
%module test
%{
#include "test.h"
%}
%include "std_sstream.i"
%include "test.h"

我已经在test.I中包含了"std_stream.I",因为我认为这是应该使用的。我需要向该文件添加一些内容以使其执行某些操作吗?

当前运行test_test.py时的错误报告为:

TypeError: in method 'StTest', argument 1 of type 'stringstream &'

为什么不使用字符串来调用PyArg_ParseTuple呢StringIO.getvalue()

用这样的东西?

const char* cstring;
if (!PyArg_ParseTuple(args,"s", &cstring)) //Note the &
        return NULL;
std::string stdstring = cstring;
std::stringstream buffer(stdstring);

另请参阅此处的cStringIO的C API。

在附加需求之后,我尝试在固定约束条件下使用SWIG来发现一个解决方案。

第一个可能的解决方案是内联或扩展解决方案(扩展StTest以获取一个字符串,然后让扩展生成一个字符串流并调用StTest),但由于所提供的示例只有一个方法,没有类,因此这不符合限制。

最终的接口文件是这样的。

/* test.i */
%module test
%{
#include "test.h"
#include <sstream>
#include <iostream>
%}
using namespace std;
%include "std_sstream.i"
%include "test.h"
%include <typemaps.i>
%typemap(in) std::stringstream & (char *buf = 0, size_t size = 0, int alloc = 0, std::stringstream stream)
{
  if (SWIG_AsCharPtrAndSize($input, &buf, &size, &alloc) != SWIG_OK)
  {
    %argument_fail(SWIG_TypeError, "(TYPEMAP, SIZE)", $symname, $argnum);
  }
  stream.write(buf, size - 1);
  $1 = &stream;
}

请注意,SWIG忽略了上面接口文件中的typemap,不确定原因。它设法在Python中创建了字符串流对象,但它们不会传递给StTest,因为对象的类型不是字符串流的类型,所以会出现相同的类型错误;(映射的swig类型与std_stream.i生成的类型不匹配,可能是因为它不包含延迟类型映射)。

接下来是我建议重写SWIG的包装器的方法的实现,得到的包装函数如下。(不包括整个包装文件,因为它超过18000行长)

SWIGINTERN PyObject *_wrap_StTest(PyObject *SWIGUNUSEDPARM(self), PyObject *args) {
  PyObject *resultobj = 0;
  void *argp1 = 0 ;
  int res1 = 0 ;
  PyObject * obj0 = 0 ;
  int argc;
  PyObject *argv[3];
  int ii;
  char *cstring;
  std::stringstream testbuffer;
  if (!PyTuple_Check(args)) SWIG_fail;
  argc = args ? (int)PyObject_Length(args) : 0;
  for (ii = 0; (ii < 2) && (ii < argc); ii++) {
    argv[ii] = PyTuple_GET_ITEM(args,ii);
  }
  if (argc == 1) {
  if (!PyArg_ParseTuple(args,"s",&cstring)) 
SWIG_fail;
  std::string teststring = cstring;
  testbuffer.str(cstring);
  std::cout << "the string in the stream: " << testbuffer.str() << std::endl;
  }  
  StTest(testbuffer);
  resultobj = SWIG_Py_Void();
  return resultobj;
fail:
  return NULL;
}   

流行中的字符串打印正确,但是将字符串流传递给StTest函数会打印字符串流在内存中的位置。这是意料之中的事,因为字符串流不像下面的堆栈溢出问题的公认答案那样绑定。

我认为StTest(testbuffer);可以被一个使用boost绑定的版本所取代,它可以根据需要工作,但已经能够进行验证。

有帮助的参考文献包括

  • PyTutorial98

  • C++中使用SWIG 的Python扩展

  • SWIG1.3a5 Python,Perl5(,Tcl)的Typemap常见问题解答