何かさらにタイプの階層以上に進む必要があります。典型的にはPython/SWIGシナリオで、次のいずれかで十分である:基底クラスで
virtual
関数(すなわち、RTTI)
- 何とか所与のインスタンスの最も派生型を識別するいくつかのメンバー変数または関数(例えば、Cの共通パターンは、何らかの種類の型識別子である構造体の最初のフィールドを含めることです)。
- オブジェクトの作成時にフックのいくつかの種類、あなたはすべてのインスタンスは、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
。
あなたはもっときれいにいくつかのことを行うことができます:
- は種類を登録するためのマクロを追加します。 (またはそれ以外の方法で自動的に行う)
- 必要に応じてオブジェクトを定期的に返すタイプマップを追加します。
これまで説明したように、これはこの問題を解決する唯一の方法ではなく、最も一般的なものです。
'dynamic_cast'とそれに続く' NULL'との比較で構成される関数をラップすることができます。あなたのpythonラップされたインスタンスは決して 'B'または' C'のインスタンスではありません。 –
@ JensMunkでは、SwigPyIteratorクラスをカスタマイズして、基底クラス(Aなど)ではなく、派生クラス(BやCなど)の型を返すことができますか? – ldlchina
'A'がインターフェースである場合、異なるオブジェクトはもちろん' B'と 'C'のように振る舞います –