在 SWIG 中将结构从 C++ 函数返回到 Python

Posted

技术标签:

【中文标题】在 SWIG 中将结构从 C++ 函数返回到 Python【英文标题】:Return Struct from a C++ function to Python in SWIG 【发布时间】:2013-06-04 15:35:35 【问题描述】:

我有一个 C++ 标头,它返回一个包含 3 个元素的结构。如何让 python 正确接受 struct 变量?

这就是我在 C++ 函数中所拥有的:

  // Function name myfunc
 struct velocity
 
 std::vector< std::vector<double> > u;
 std::vector< std::vector<double> > v;
 std::vector< std::vector<double> > w;
 ; 

 velocity velo;  //Creating object

 velo.u = sum(umean,pu);
 velo.v = sum(vmean,pv);
 velo.w = sum(wmean,pw);

 return(velo)

这是我使用 SWIG 后的 Python 实现

 import numpy
 from myfunc import *    # importing C++ myfunc library
 My = 100       # Matrix dimensions
 Mz = 100
 z = myfunc(My,Mz)    # Supplying the matrix dimensions to the myfunc library
 print(z)

执行此操作时收到的错误消息:

 <myfunc.velocity; proxy of <Swig Object of type 'velocity *' at 0x2951ae0> >

我知道我必须以某种方式在 SWIG 中定义以使 python 采用“原样”的结构。 有什么办法吗?或者您可能建议的任何替代方法?这是我的 SWIG 文件

 %module myfunc
 %
 #include "myfunc.h"  
 %

 %include "std_vector.i"
 // Instantiate templates used by example
  namespace std 
  %template(IntVector) vector<int>;
  %template(DoubleVector) vector<double>;
  %template(twodvector) std::vector< std::vector<double> >; 
  

 struct velocity
 
 std::vector< std::vector<double> > u;
 std::vector< std::vector<double> > v;
 std::vector< std::vector<double> > w;
 ;

 %include "myfunc.h"

请注意,我在这里声明了一个结构。这在 SWIG 上编译成功,但我不知道如何在 Python 中使用它来实际获取 C++ 结构!

【问题讨论】:

请同时发布您的 swig 接口文件 【参考方案1】:

这不是错误消息:

<myfunc.velocity; proxy of <Swig Object of type 'velocity *' at 0x2951ae0> >

它只是表明你有一个 SWIG 代理对象,它包装了一个指向速度对象的指针。例如,您只需访问 z.u[0][0] 即可访问其中一个双向量的元素。

编辑

这是一个为vector&lt;vector&lt;double&gt;&gt; 定义类型映射的示例。它们并不漂亮,但允许将 Python“列表列表”直接分配给 velocity 成员:

%module x

%begin %
#pragma warning(disable:4127 4701 4706 4996)
#include <vector>
#include <algorithm>
#include <sstream>
%

%include <std_vector.i>
%include <std_string.i>
%template(vector_double) std::vector<double>;
%template(vector_vector_double) std::vector<std::vector<double> >;

// Input typemap converts from Python object to C++ object.
// Note error checking not shown for brevity.
// $input is the Python object, $1 is the C++ result.
//
%typemap(in) std::vector<std::vector<double> >* (std::vector<std::vector<double> > tmp) %
    for(Py_ssize_t i = 0; i < PySequence_Size($input); ++i)
    
        auto t = PySequence_GetItem($input,i);
        std::vector<double> vd;
        for(Py_ssize_t j = 0; j < PySequence_Size(t); ++j) 
            auto d = PySequence_GetItem(t,j);
            vd.push_back(PyFloat_AsDouble(d));
            Py_DECREF(d);
        
        Py_DECREF(t);
        tmp.push_back(vd);
    
    $1 = &tmp;
%

// Output typemap converts from C++object to Python object.
// Note error checking not shown for brevity.
// $1 is the C++ object, $result is the Python result.
//
%typemap(out) std::vector<std::vector<double> >* %
    $result = PyList_New($1->size()); // Create outer Python list of correct size
    for(size_t i = 0; i < $1->size(); ++i)
    
        auto t = PyList_New((*$1)[i].size()); // Create inner Python list of correct size for this element.
        for(size_t j = 0; j < (*$1)[i].size(); ++j) 
            PyList_SET_ITEM(t,j,PyFloat_FromDouble((*$1)[i][j]));
        
        PyList_SET_ITEM($result,i,t);
    
%

%inline %
    struct velocity
    
        std::vector<std::vector<double> > u;
        std::vector<std::vector<double> > v;
        std::vector<std::vector<double> > w;
    ;

    // A test function with an in/out velocity parameter.
    void myfunc(velocity& vel)
    
        for(auto& v : vel.u)
            std::transform(begin(v),end(v),begin(v),[](double d)return d*1.1;);
        for(auto& v : vel.v)
            std::transform(begin(v),end(v),begin(v),[](double d)return d*2.2;);
        for(auto& v : vel.w)
            std::transform(begin(v),end(v),begin(v),[](double d)return d*3.3;);
    
%

使用示例:

>>> import x
>>> vel=x.velocity()
>>> vel.u = [1,2,3],[4.5,6,7]
>>> vel.v = [1,2],[3,4,5]
>>> vel.w = [1],[2,3]
>>> vel.u
[[1.0, 2.0, 3.0], [4.5, 6.0, 7.0]]
>>> vel.v
[[1.0, 2.0], [3.0, 4.0, 5.0]]
>>> vel.w
[[1.0], [2.0, 3.0]]
>>> x.myfunc(vel)
>>> vel.u
[[1.1, 2.2, 3.3000000000000003], [4.95, 6.6000000000000005, 7.700000000000001]]
>>> vel.v
[[2.2, 4.4], [6.6000000000000005, 8.8, 11.0]]
>>> vel.w
[[3.3], [6.6, 9.899999999999999]]

【讨论】:

谢谢,我可以通过这种方式访问​​元素。但是,我仍然需要能够在 Python 中将每个 2D std 向量 u,v 或 w 作为一个完整数组访问,而无需遍历元素。这可能吗? 可以按行访问,通过z.u[0]得到一个元组;否则,您可以编写 %typemap 以将 vector&lt;vector&lt;double&gt;&gt; 完全转换为 Python 列表列表。现在没有时间,但如果你愿意,我稍后会更新一个示例。 感谢@Mark Tolonen。我现在可以访问元组了。如果你能发布一个例子,那对我们所有人来说都很棒。我是 SWIG 的新手。【参考方案2】:

Mark Tolonen 是对的,您看到的行为不是错误。如果我理解正确,您希望能够打印 myfunc.velocity 的实例

任何可打印的python对象的AFAIK,都需要定义__str__方法。通常这是内置的,但是由于您要包装 C 结构,因此您必须明确定义一个。我认为有不同的方法可以做到这一点。

扩展结构:

您可以使用 Swig 的extend directive 来定义 C/C++ 中缺少的函数。

%extend 
   const char* velocity::__str__() 
      std::stringtream ss;
      ss<< "[ ";
      std::copy($self->u.begin(), $self->u.end(), std::ostream_iterator<double>(ss," "));
      ss << std::endl;
      std::copy($self->u.begin(), $self->u.end(), std::ostream_iterator<double>(ss," "));
      ss << std::endl;
      std::copy($self->u.begin(), $self->u.end(), std::ostream_iterator<double>(ss," "));
      ss << "]" << std::endl;

      return ss.str().c_str();
   

这应该向速度结构添加一个函数 __str__,该结构随后将集成到生成的 Python 包装器中。

扩展生成的 Python 代码:

另一种方法是define additional methods to the auto-generated Python-Wrapper。您可以使用 %pythoncode% 指令将 Python 代码添加到 Swig 接口文件。根据您的语言偏好,这可能更容易做到:

%pythoncode %
def __str__(self):
    # this is probably horribly inefficient
    return "[" + str(self.u) + "\n" + str(self.v) + "\n" + str(self.w) + " ]"
%

Swig 有大量文档,请查看 Python chapter 以了解有关更紧密集成的更多信息。

编写方便的包装器

当然,您也可以定义第二个 Python 包装器类,它继承自 myfunc.velocity 并在那里定义所有缺少的功能。

【讨论】:

感谢所有的努力!我需要将 C++ 中的数据保存为合适的 python 数据类型。但是,当我需要打印时,我想您的提示会派上用场。

以上是关于在 SWIG 中将结构从 C++ 函数返回到 Python的主要内容,如果未能解决你的问题,请参考以下文章

SWIG (Java):如何将带有回调函数的结构从 Android 应用程序传递给 C++?

如何在 SWIG 中将向量的锯齿状 C++ 向量转换(类型映射)为 Python

如何使用 swig C++ 命名空间作为 python 模块公开

swig shared_ptr 导致一个不透明的对象

通过 SWIG 从 C++ 调用 Go 回调函数

使用 swig 类型映射从 c++ 方法返回向量<pair<int,int>> & 到 python 元组列表