2016-08-29 1 views
1

私はC++でクラスA、B、Cを持っています。 BとCはAから派生しています。そして、私はCで関数を持っており、BとCのベクトルを返します:std::vector<A*> getAllObjects()のように。私はswigを使ってPythonラッパーを生成します。その後、私は以下のようにPythonでgetAllObjects()を呼び出します。SwigPyIteratorの値は常に基本クラス

objects = getAllObjects() 
for obj in objects: 
    if isinstance(obj, B): 
     print("OK") 
    elif isinstance(obj, C): 
     print("OK") 

私はイテレータから取得するオブジェクトがインスタンスAであるが、それは問題を解決する方法BまたはCすべきですか?

+0

'dynamic_cast'とそれに続く' NULL'との比較で構成される関数をラップすることができます。あなたのpythonラップされたインスタンスは決して 'B'または' C'のインスタンスではありません。 –

+0

@ JensMunkでは、SwigPyIteratorクラスをカスタマイズして、基底クラス(Aなど)ではなく、派生クラス(BやCなど)の型を返すことができますか? – ldlchina

+0

'A'がインターフェースである場合、異なるオブジェクトはもちろん' B'と 'C'のように振る舞います –

答えて

3

何かさらにタイプの階層以上に進む必要があります。典型的にはPython/SWIGシナリオで、次のいずれかで十分である:基底クラスで

  1. virtual関数(すなわち、RTTI)
  2. 何とか所与のインスタンスの最も派生型を識別するいくつかのメンバー変数または関数(例えば、Cの共通パターンは、何らかの種類の型識別子である構造体の最初のフィールドを含めることです)。
  3. オブジェクトの作成時にフックのいくつかの種類、あなたはすべてのインスタンスは、Pythonが所有/

を作成されることがわかっている場合たとえば、私は最初のタイプが十分であることを前提に作業していて、さらには他のためにしていますケースは適応するのが難しくありません。我々はRTTIを利用して、次の操作を行うことができ++純粋なCで、このヘッダファイルを考えると

class A { 
public: 
    virtual ~A() {} 
}; 

class B : public A { 
}; 

class C: public A { 
}; 

#include "test.hh" 
#include <typeinfo> 
#include <iostream> 

int main() { 
    const auto& t1 = typeid(A); 
    const auto& t2 = typeid(B); 
    const auto& t3 = typeid(C); 
    A *a = new A; 
    A *b = new B; 
    A *c = new C; 
    const auto& at = typeid(*a); 
    const auto& bt = typeid(*b); 
    const auto& ct = typeid(*c); 

    std::cout << t1.name() << "\n"; 
    std::cout << t2.name() << "\n"; 
    std::cout << t3.name() << "\n"; 
    std::cout << at.name() << "\n"; 
    std::cout << bt.name() << "\n"; 
    std::cout << ct.name() << "\n"; 
} 

これがあることを示して、私は次のヘッダーファイルを書いてこれを説明するために

私たちが解決しようとしている問題(実際にどのようなタイプですか?)は、実際には標準のC++を使って解くことができます。

A*を返す関数ではなく、std::vectorの繰り返しを使用することで、この問題が少し複雑になっていることには注意してください。関数の戻り値を使って作業していただけの場合は、typemap(out)と書くでしょう。しかし、std::vector<A*>の場合、反復の振る舞いをカスタマイズして、余分なコードを挿入してPythonが基底だけでなく派生型を認識できるようにすることも可能です。 SWIGは、標準コンテナの大部分が過度の重複なしに共通の使用(例えば反復)でそれらを助けるために使用する型特性機構を有する。 (これについては、これはstd_common.iにあると思います)。

基本的な計画は、これをカスタマイズするためにSWIGが導入する特性タイプを使用して、反復プロセスの出力にフックすることです(SwigPyIterator、この場合はSwigPyIteratorClosed_Tとして実装されます)。そのフックの中で、A*のSWIGタイプ情報を盲目的に使用するのではなく、を使用して、std::mapでタイプを動的に検索します。このマップはモジュール内部で管理されています。このマップで何かを見つけたら、Pythonプログラマーが期待するように、より派生したPythonオブジェクトを返すためにそれを使用します。最後に、初期化時にマップに型を登録する必要があります。

だから私のインターフェイスは、次のように見てしまった:私は物事を始めるには十分だ事柄を説明するために書いた%inline機能付き

%module test 

%{ 
#include "test.hh" 
#include <vector> 
#include <map> 
#include <string> 
#include <typeindex> // C++11! - see: http://stackoverflow.com/a/9859605/168175 
%} 

%include <std_vector.i> 

%{ 
namespace { 
    // Internal only, store the type mappings 
    std::map<std::type_index, swig_type_info*> aheirarchy; 
} 

namespace swig { 
    // Forward declare traits, the fragments they're from won't be there yet 
    template <class Type> struct traits_from_ptr; 
    template <class Type> 
    inline swig_type_info *type_info(); 

    template <> struct traits_from_ptr<A> { 
    static PyObject *from(A *val, int owner = 0) { 
     auto ty = aheirarchy[typeid(*val)]; 
     if (!ty) { 
     // if there's nothing in the map, do what SWIG would have done 
     ty = type_info<A>(); 
     } 
     return SWIG_NewPointerObj(val, ty, owner); 
    } 
    }; 
} 
%} 

%template(AList) std::vector<A*>; 

%inline %{ 
const std::vector<A*>& getAllObjects() { 
    // Demo only 
    static auto ret = std::vector<A*>{new A, new B, new C, new C, new B}; 
    return ret; 
} 
%} 

%include "test.hh" 
%init %{ 
    // Register B and C here 
    aheirarchy[typeid(B)] = SWIG_TypeQuery("B*"); 
    aheirarchy[typeid(C)] = SWIG_TypeQuery("C*"); 
%} 

。それは私が私の解決策を示すために、次のテストはPythonの実行を許可:あなたがgetAllObjectsの私のダミー実装で作成されたタイプと一致しました気づく

from test import getAllObjects, A, B, C 

objects = getAllObjects() 
for obj in objects: 
    print obj 
    if isinstance(obj, B): 
     print("OK") 
    elif isinstance(obj, C): 
     print("OK") 
swig3.0 -c++ -python -Wall test.i 
g++ -std=c++11 -Wall test_wrap.cxx -o _test.so -shared -I/usr/include/python2.7/ -fPIC 
python run.py 
<test.A; proxy of <Swig Object of type 'A *' at 0xf7442950> > 
<test.B; proxy of <Swig Object of type 'B *' at 0xf7442980> > 
OK 
<test.C; proxy of <Swig Object of type 'C *' at 0xf7442fb0> > 
OK 
<test.C; proxy of <Swig Object of type 'C *' at 0xf7442fc8> > 
OK 
<test.B; proxy of <Swig Object of type 'B *' at 0xf7442f98> > 
OK 

あなたはもっときれいにいくつかのことを行うことができます:

  1. は種類を登録するためのマクロを追加します。 (またはそれ以外の方法で自動的に行う)
  2. 必要に応じてオブジェクトを定期的に返すタイプマップを追加します。

これまで説明したように、これはこの問題を解決する唯一の方法ではなく、最も一般的なものです。

+0

それはまさに私が期待したものです。どうもありがとうございました。 – ldlchina

0

これを解決する別の方法は、.iファイルでマクロを使用することです。

まず、各タイプをチェックするために使用することができるマクロ定義:リストまたはベクトルの出力をマッピングする関数を定義し、そして

// dcast and pPyObj defined below... 
%define %_container_typemap_dispatch(Type) 
if (!dcast) { 
    Type *obj = dynamic_cast<Type *>(*it); 
    if (obj != NULL) { 
     dcast = true; 
     pPyObj = SWIG_NewPointerObj(%as_voidptr(obj), $descriptor(Type *), $owner | %newpointer_flags); 
    } 
} 
%enddef 

を:

最後
// LIST_TYPEMAP macro to create the proper wrappers in std::list<AbstractElement*> 
// It works like %factory but inside std::list elements. 
// It only works with types, not specific methods like %factory. 
// Usage: container_typemap(OutputType, CastType1, CastType2, ...); 
%define %container_typemap(ItemType, Types...) 
%typemap(out) ItemType { 
    PyObject *res = PyList_New($1.size()); 
    int i = 0; 
    for (ItemType::iterator it = $1.begin(); 
     it != $1.end(); 
     ++it, ++i) 
    { 
     PyObject* pPyObj = NULL; 
     bool dcast = false; 
     %formacro(%_container_typemap_dispatch, Types) 
     if (!dcast) 
      // couldn't cast to proper type, use abstract type: 
      pPyObj = SWIG_NewPointerObj(%as_voidptr(*it), $descriptor, $owner | %newpointer_flags); 
     if (pPyObj != NULL) 
      PyList_SetItem(res, i, pPyObj); 
    } 
    %set_output(res); 
} 
%enddef 

、使用このオブジェクトをオブジェクトに適用するcontainer_typemap関数:

%container_typemap(A, B, C); 

いつでもこれは新しいベクトル/リストのためだけに呼び出します:

%container_typemap(Base, Derived1, Derived2, Derived3); 
関連する問題