わかりました、これは一般的に答えるのがかなり難しい質問です。問題の根本的な原因は、C 関数ポインターとまったく同等の python 型が実際には存在しないことです。Python 関数は似ていますが、いくつかの理由でインターフェイスが一致しません。
まず、ここからコンストラクターをラップする手法について言及したいと思います:
http://wiki.python.org/moin/boost.python/HowTo#namedconstructors.2BAC8factories.28asPythoninitializers.29。これにより__init__
、実際の C++ コンストラクターに直接対応しないオブジェクトの関数を作成できます。また、オブジェクトがデフォルトで構築可能でない場合は、構築で指定boost::python::no_init
し、後で実際の関数を指定する必要がある場合があることに注意してください。boost::python::class_
def
__init__
質問に戻ります。通常、渡したい関数のセットはごくわずかですか? その場合、特別な列挙型 (または特殊化されたクラス) を宣言し、列挙型を受け入れるコンストラクターのオーバーロードを作成し、それを使用して実際の関数ポインターを検索することができます。このアプローチを使用して Python から直接関数を呼び出すことはできませんが、それほど悪くはなく、パフォーマンスは実際の関数ポインターを使用する場合と同じになります。
任意の python callable で機能する一般的なアプローチを提供したい場合、事態はさらに複雑になります。boost::function
または などを使用して、一般的なファンクターを受け入れるコンストラクターを C++ オブジェクトに追加する必要がありますstd::tr1::function
。関数ポインターはこの型に正しく変換されるため、必要に応じて既存のコンストラクターを置き換えることができます。
boost::function
したがって、コンストラクターをに追加したと仮定するとSomeClass
、これらの関数を python ラッピング コードに追加する必要があります。
struct WrapPythonCallable
{
typedef float * result_type;
explicit WrapPythonCallable(const boost::python::object & wrapped)
: wrapped_(wrapped)
{ }
float * operator()(vector<float>* arg) const
{
//Do whatever you need to do to convert into a
//boost::python::object here
boost::python::object arg_as_python_object = /* ... */;
//Call out to python with the object - note that wrapped_
//is callable using an operator() overload, and returns
//a boost::python::object.
//Also, the call can throw boost::python::error_already_set -
//you might want to handle that here.
boost::python::object result_object = wrapped_(arg_as_python_object);
//Do whatever you need to do to extract a float * from result_object,
//maybe using boost::python::extract
float * result = /* ... */;
return result;
}
boost::python::object wrapped_;
};
//This function is the "constructor wrapper" that you'll add to SomeClass.
//Change the return type to match the holder type for SomeClass, like if it's
//held using a shared_ptr.
std::auto_ptr<SomeClass> CreateSomeClassFromPython(
const boost::python::object & callable)
{
return std::auto_ptr<SomeClass>(
new SomeClass(WrapPythonCallable(callable)));
}
//Later, when telling Boost.Python about SomeClass:
class_<SomeClass>("some_class", no_init)
.def("__init__", make_constructor(&CreateSomeClassFromPython));
ポインターを python との間で変換する方法の詳細は省略しました。オブジェクトの有効期間の問題があるため、これは明らかに解決しなければならないことです。
Python からこの関数に渡す関数ポインターを呼び出す必要がある場合は、def
ある時点で Boost.Python を使用してこれらの関数を呼び出す必要があります。この 2 番目の方法は、これらの定義された関数で問題なく動作しますが、呼び出されるたびにオブジェクトが不必要に Python との間で変換されるため、それらの呼び出しは遅くなります。
これを修正するには、CreateSomeClassFromPython
既知または一般的な関数オブジェクトを認識するように変更し、それらを実際の関数ポインターに置き換えることができます。object1.ptr() == object2.ptr()
Pythonと同等の を使用して、C++ で Python オブジェクトの ID を比較できますid(object1) == id(object2)
。
最後に、もちろん、一般的なアプローチと列挙型アプローチを組み合わせることができます。これを行うときは、boost::python のオーバーロード規則が C++ のものとは異なることに注意してくださいCreateSomeClassFromPython
。Boost.Python は、ランタイム引数を C++ 引数型に変換できるかどうかを確認するために、定義されている順序で関数をテストします。そのCreateSomeClassFromPython
ため、引数が任意の python オブジェクトと一致するため、後で定義された単一引数のコンストラクターが使用されるのを防ぎます。他の単一引数__init__
関数の後に必ず配置してください。
この種のことを頻繁に行っている場合は、一般的な boost::function ラッピング手法 (名前付きコンストラクター手法と同じページに記載されています) を確認することをお勧めします: http://wiki.python.org/ moin/boost.python/HowTo?action=AttachFile&do=view&target=py_boost_function.hpp .