1

boost::python 内から可変数の入力引数を持つ python 関数を呼び出すことは可能ですか?

Pythonでは、関数を定義します

def py_fn1(a):
    return 2 * a

def py_fn2(a, b):
    return a+b

def call_fn_py(fn, *args):
    return fn(*args)

a = call_fn_py(py_fn1, 15)  # returns 30
b = call_fn_py(py_fn2, 10, 11)  # returns 21

コメントに従って結果を提供します。ブースト python を使用して C++ で call_fn(fn, *args) を実装したいと思います。これまでのところ、私は持っています:

using namespace boost::python;
object call_fn(object fn, tuple arg_in)
{
return fn(arg_in);
}


BOOST_PYTHON_MODULE(python_addins)
{
    using namespace boost::python;
def("call_fn", &call_fn);
}

しかし、次のようなことをします:

import python_addins

d = python_addins.call_fn(py_fn1, (5,))  # returns (5,5)

それは私が求めているものではありません。call_fn_py のように動作するように call_fn を記述するにはどうすればよいですか?

ありがとう、マーク

4

1 に答える 1

1

可変数の引数で 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
于 2012-08-16T22:45:19.507 に答える