简体   繁体   English

在CIG函数中将结构返回到SWIG中的Python

[英]Return Struct from a C++ function to Python in SWIG

I have a C++ header which returns a struct having 3 elements. 我有一个C ++标头,它返回一个有3个元素的结构。 How can I make python accept the struct variable properly? 如何让python正确接受struct变量?

This is what I have in the C++ function : 这就是我在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)

This is my Python implementation, after using SWIG 这是我使用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)

The error message I get upon executing this: 执行此操作时收到的错误消息:

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

I know that I have to somehow define in SWIG to make python take a struct "as is". 我知道我必须以某种方式在SWIG中定义以使python“按原样”获取结构。 Is there any way? 有什么办法吗? Or any alternative method you may suggest? 或者您可能建议的任何替代方法? Here is my SWIG file 这是我的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"

Note that I have declared a struct here. 请注意,我在这里声明了一个结构。 This compiles successfully on SWIG but I do not know how to use it in Python to actually get the C++ struct! 这在SWIG上成功编译,但我不知道如何在Python中使用它来实际获取C ++结构!

This is not an error message: 这不是错误消息:

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

It just indicates you have a SWIG proxy object that wraps a pointer to a velocity object. 它只是表明你有一个SWIG代理对象,它包含一个指向速度对象的指针。 You can just access zu[0][0] , for example, to access an element of one of the double vectors. 例如,您可以访问zu[0][0]以访问其中一个双向量的元素。

Edit 编辑

Here's an example with typemaps defined for vector<vector<double>> . 这是为vector<vector<double>>定义的类型映射示例。 They are not pretty, but allow assignment of Python "list of lists" to the velocity members directly: 它们并不漂亮,但允许直接为velocity成员分配Python“列表列表”:

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

Use example: 使用示例:

>>> 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]]

Mark Tolonen is right, the behavior you're seeing is not an error. Mark Tolonen是对的,你所看到的行为不是错误。 If I understand you correctly, you want to be able to print an instance of myfunc.velocity 如果我理解正确,您希望能够打印myfunc.velocity的实例

AFAIK for any python object to be printable, it needs to have the __str__ method defined. AFAIK可以打印任何python对象,它需要定义__str__方法。 Normaly that's built-in, but since you're wrapping a C-struct, you have to define one explicitly. Normaly是内置的,但是由于你要包装一个C-struct,你必须明确定义一个。 I reckon there are different ways to do this. 我估计有不同的方法可以做到这一点。

Extending the struct: 扩展结构:

You can use Swig's extend directive to define the missing function in C/C++. 您可以使用Swig的extend指令在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();
   }
}

This should add a function __str__ to the velocity struct that will subsequently integrated into the generated Python wrapper. 这应该将函数__str__添加到velocity结构中,该结构随后将集成到生成的Python包装器中。

Extending the generated Python code: 扩展生成的Python代码:

Another approach would be to define additional methods to the auto-generated Python-Wrapper . 另一种方法是为自动生成的Python-Wrapper定义其他方法 You can add Python code to the Swig interface file using the %pythoncode% directive. 您可以使用%pythoncode%指令将Python代码添加到%pythoncode%接口文件中。 Depending on your language preferences, this might be easier to do: 根据您的语言偏好,这可能更容易:

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

Swig has an extensive documentation, have a look at the Python chapter to learn more about tighter integration. Swig有大量文档,请查看Python章节以了解有关更紧密集成的更多信息。

Writing convenience wrapper 写便利包装

Of course, you could also define a second Python wrapper class which inherits from myfunc.velocity and define all the missing features there. 当然,您还可以定义第二个Python包装类,它继承自myfunc.velocity并定义那里缺少的所有功能。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM