5

boost::function を引数として取る boost python を使用して Python にエクスポートされたメソッドがあります。

私が読んだことから、boost::python は大騒ぎせずに boost::function をサポートする必要がありますが、python メソッドで関数を呼び出そうとすると、このエラーが発生します

Boost.Python.ArgumentError: Python argument types in
    Class.createTimer(Class, int, method, bool)
did not match C++ signature:
    createTimer(class Class {lvalue}, unsigned long interval, 
    class boost::function<bool _cdecl(void)> function, bool recurring=False)

このコードでPythonから呼び出しています

self.__class.createTimer( 3, test.timerFunc, False )

C++ では次のように定義されます。

boost::int32_t createTimer( boost::uint32_t interval, boost::function< bool() > function, bool recurring = false );

ここでの目標は、次のようなことができるタイマー クラスです。

class->createTimer( 3, boost::bind( &funcWithArgs, arg1, arg2 ) )

funcWithArgs を実行するタイマーを作成します。ブーストバインドのおかげで、これはほとんどすべての関数またはメソッドで機能します。

では、boost::python が自分の python 関数を boost::function として受け入れるために使用する必要がある構文は何ですか?

4

1 に答える 1

12

Pythonメーリングリストで回答を得て、少し作り直してさらに調査した後、まさに私が望んでいたものを手に入れました:)

私はミスランディの前にその投稿を見ましたが、そのように関数を宣言しなければならないという考えは好きではありませんでした. 派手なラッパーと少しの python マジックを使えば、これは機能すると同時に見栄えも良くなります!

まず、Python オブジェクトを次のようなコードでラップします。

struct timer_func_wrapper_t
{
    timer_func_wrapper_t( bp::object callable ) : _callable( callable ) {}

    bool operator()()
    {
        // These GIL calls make it thread safe, may or may not be needed depending on your use case
        PyGILState_STATE gstate = PyGILState_Ensure();
        bool ret = _callable();
        PyGILState_Release( gstate );
        return ret;
    }

    bp::object _callable;
};

boost::int32_t createTimerWrapper( Class* class, boost::uint64_t interval, bp::object function, bool recurring = false )
{
    return class->createTimer( interval, boost::function<bool ()>( timer_func_wrapper_t( function ) ), recurring );
}

あなたのクラスでそのようにメソッドを定義するとき

.def( "createTimer", &createTimerWrapper, ( bp::arg( "interval" ), bp::arg( "function" ), bp::arg( "recurring" ) = false ) )

その小さなラッパーで、このような魔法を働かせることができます

import MyLib
import time

def callMePls():
    print( "Hello world" )
    return True

class = MyLib.Class()

class.createTimer( 3, callMePls )

time.sleep( 1 )

C++ を完全に模倣するには、http: //code.activestate.com/recipes/440557/にある boost::bind 実装も必要です。

これで、次のようなことができるようになりました

import MyLib
import time

def callMePls( str ):
    print( "Hello", str )
    return True

class = MyLib.Class()

class.createTimer( 3, bind( callMePls, "world" ) )

time.sleep( 1 )

編集:

できる限り質問をフォローアップしたいと思います。私はしばらくの間、このコードをうまく使っていましたが、オブジェクト コンストラクターで boost::function を使用したい場合、これがうまくいかないことがわかりました。これと同じように機能させる方法はありますが、構築する新しいオブジェクトは最終的に異なる署名になり、それ自体のような他のオブジェクトでは機能しません。

これは最終的にそれについて何かをするのに十分なほど私を悩ませました.boost::pythonについてもっと知っているので、コンバーターを使用してかなり良い「すべてに適合する」ソリューションを思いつきました。このコードは、python callable を boost::python< bool() > オブジェクトに変換します。他のブースト関数に変換するように簡単に変更できます。

// Wrapper for timer function parameter
struct timer_func_wrapper_t
{
    timer_func_wrapper_t( bp::object callable ) : _callable(callable) {}

    bool operator()()
    {
        return _callable();
    }

    bp::object _callable;
};

struct BoostFunc_from_Python_Callable
{
    BoostFunc_from_Python_Callable()
    {
        bp::converter::registry::push_back( &convertible, &construct, bp::type_id< boost::function< bool() > >() );
    }

    static void* convertible( PyObject* obj_ptr )
    {
        if( !PyCallable_Check( obj_ptr ) ) return 0;
        return obj_ptr;
    }

    static void construct( PyObject* obj_ptr, bp::converter::rvalue_from_python_stage1_data* data )
    {
        bp::object callable( bp::handle<>( bp::borrowed( obj_ptr ) ) );
        void* storage = ( ( bp::converter::rvalue_from_python_storage< boost::function< bool() > >* ) data )->storage.bytes;
        new (storage)boost::function< bool() >( timer_func_wrapper_t( callable ) );
        data->convertible = storage;
    }
};

次に、初期化コード、つまり BOOST_PYTHON_MODULE() で、構造体を作成して型を登録するだけです

BOOST_PYTHON_MODULE(Foo)
{
    // Register function converter
    BoostFunc_from_Python_Callable();
于 2010-02-02T03:27:30.610 に答える