可変数の引数で Python 関数を呼び出すことができます。次の 2 つの方法があります。
- 関数のセットを作成して、可変数の引数を模倣します
call_fn
。
- 複数のオブジェクトをタプルにパックおよびアンパックする Python の機能を活用します。
最初のアプローチは、一般的な C++ プログラミングではかなり一般的です。引数の量によって異なるオーバーロードされた関数のセットを作成する必要があります: call_fn( fn )
、call_fn( fn, A1 )
、call_fn( fn, A1, ...An)
. 定型コードの量は、C++11 可変個引数テンプレート関数を使用して削減できます。ただし、ラッパーを定義するときは、テンプレート関数の正確なタイプを指定する必要があります。したがって、このアプローチでは、ラップされるテンプレートのインスタンス化に基づいて、引数の数に制限があります。
#include <boost/python.hpp>
using boost::python;
object call_fn1( object fn, object a1 )
{
return fn( a1 );
}
object call_fn2( object fn, object a1, object a2 )
{
return fn( a1, a2 );
}
object call_fn3( object fn, object a1, object a2, object a3 )
{
return fn( a1, a2, a3 );
}
BOOST_PYTHON_MODULE(example)
{
def( "call_fn", &call_fn1 );
def( "call_fn", &call_fn2 );
def( "call_fn", &call_fn3 );
}
そして、ここにデモンストレーションがあります:
>>> def py_fn1( a ): return 2 * a
...
>>> def py_fn2( a, b ): return a + b
...
>>> def call_fn_py( fn, *args ):
... from example import call_fn
... return call_fn( fn, *args )
...
>>> call_fn_py( py_fn1, 15 )
30
>>> call_fn_py( py_fn2, 10, 11 )
21
2 番目のアプローチは、タプルを活用します。呼び出し元が引数をタプルにパックし、呼び出し元の関数が引数をアンパックする必要があります。ただし、タプルのパッキングとアンパッキングは Python で行う方が簡単なexample.call_fn
ので、デリゲートする前に引数をアンパックするヘルパー関数で関数の引数をデコレートするように、Python でパッチを適用できます。
C++ でcall_fn
、単一のboost::python::tuple
引数を受け入れる関数を作成します。
#include <boost/python.hpp>
using namespace boost::python;
object call_fn( object fn, tuple args )
{
return fn( args );
}
BOOST_PYTHON_MODULE(example)
{
def( "call_fn", &call_fn );
}
example_ext.py
次に、パッチを作成しますexample.call_fn
:
import example
def patch_call_fn():
# Store handle to unpatched call_fn.
original = example.call_fn
# Helper function to create a closure that unpacks arguments
# before delegating to the user function.
def unpack_args( fn ):
def wrapper( args ):
return fn( *args )
return wrapper
# The patched function that will wrap the user function.
def call_fn( fn, *args ):
return original( unpack_args( fn ), args )
return call_fn
# Patch example.
example.call_fn = call_fn = patch_call_fn()
同じデモンストレーションを使用できます。必要な唯一の変更は、 のexample_ext
代わりにインポートする必要があることですexample
。
>>> def py_fn1( a ): return 2 * a
...
>>> def py_fn2( a, b ): return a + b
...
>>> def call_fn_py( fn, *args ):
... from example_ext import call_fn
... return call_fn( fn, *args )
...
>>> call_fn_py( py_fn1, 15 )
30
>>> call_fn_py( py_fn2, 10, 11 )
21