4

3 つの要素を持つ構造体を返す C++ ヘッダーがあります。Pythonに構造体変数を適切に受け入れさせるにはどうすればよいですか?

これは私が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> >

Pythonが構造体を「そのまま」取るようにするには、SWIGで何らかの形で定義する必要があることを知っています。何か方法はありますか?または、あなたが提案する代替方法はありますか?これが私の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 で正常にコンパイルされますが、実際に C++ 構造体を取得するために Python で使用する方法がわかりません!

4

2 に答える 2

5

これはエラー メッセージではありません。

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

これは、ポインタを速度オブジェクトにラップする SWIG プロキシ オブジェクトがあることを示しているだけです。z.u[0][0]たとえば、 double ベクトルの 1 つの要素にアクセスするために、 にアクセスするだけです。

編集

に対して定義されたタイプマップの例を次に示しvector<vector<double>>ます。これらはきれいではありませんが、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]]
于 2013-06-05T02:13:59.793 に答える
3

Mark Tolonen の言うとおりです。あなたが目にしている動作はエラーではありません。私の理解が正しければ、あなたは myfunc.velocity のインスタンスを印刷できるようにしたいと考えています。

私の知る限り、任意の python オブジェクトを印刷可能にするには、__str__ メソッドを定義する必要があります。通常は組み込みですが、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();
   }
}

これにより、関数 __str__ が速度構造体に追加され、その後、生成された Python ラッパーに統合されます。

生成された Python コードの拡張:

もう 1 つの方法は、自動生成された 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 の章を参照してください。

便利なラッパーを書く

もちろん、myfunc.velocity から継承する 2 番目の Python ラッパー クラスを定義し、そこにないすべての機能を定義することもできます。

于 2013-06-05T08:00:09.673 に答える