8

ラップされた関数の前後にコードを実行できるラッパークラスまたは関数を作成しようとしています。

float foo(int x, float y)
{
    return x * y;
}

BOOST_PYTHON_MODULE(test)
{
     boost::python::def("foo", <somehow wrap "&foo">);
}

理想的には、ラッパーは汎用であり、関数とメンバー関数に対して同様に機能し、任意の署名を使用する必要があります。

より詳しい情報:

私は、次のような薄いラッパーを手動で作成することなく、高価なC++呼び出しの周りでGILをリリース/再取得する簡単な方法を探しています。

float foo_wrapper(int x, float y)
{
    Py_BEGIN_ALLOW_THREADS
    int result = foo(x, y);
    Py_END_ALLOW_THREADS
    return result;
}

BOOST_PYTHON_MODULE(test)
{
     boost::python::def("foo", &foo_wrapper);
}

この種のラッパーは、すべての種類の関数に対して数回繰り返されます。すべての関数をコーディングしないようにするソリューションを見つけたいと思います。

私はいくつかのアプローチを試しましたが、私が得ることができる最善の方法は、ユーザーが次のような戻り値とパラメーターのタイプを明示的に述べることを要求しました。

boost::python::def("foo", &wrap_gil<float, int, float>(&foo_wrapper));

しかし、関数(&foo_wrapper)へのポインターを渡して、コンパイラーに型を認識させることは可能であると私には思えます。

誰かが私が使用できるテクニックを知っているか、正しい方向に私を向けることができますか?

乾杯!

4

2 に答える 2

11

この場合、関数をラップするFunctorクラスを記述してから、boost :: python :: detail::get_signatureをオーバーロードしてFunctorを受け入れることができます。

更新:メンバー関数のサポートも追加されました!

例:

#include <boost/shared_ptr.hpp>
#include <boost/python.hpp>
#include <boost/python/signature.hpp>
#include <boost/mpl/vector.hpp>

#include <iostream>
#include <string>
#include <sstream>

static boost::shared_ptr<std::ostringstream> test_stream_data;

std::ostringstream& test_stream()
{
    if (!test_stream_data) {
        test_stream_data.reset(new std::ostringstream);
    }
    return *test_stream_data;
}


std::string get_value_and_clear_test_stream()
{
    std::string result;
    if (test_stream_data) {
        result = test_stream_data->str();
    }
    test_stream_data.reset(new std::ostringstream);
    return result;
}


std::string func(int a, double b)
{
    std::ostringstream oss;
    oss << "func(a=" << a << ", b=" << b << ")";
    std::string result = oss.str();
    test_stream() << "- In " << result << std::endl;
    return result;
}


class MyClass
{
public:
    MyClass(std::string p_name)
        : m_name(p_name)
    {
        test_stream() << "- In MyClass::MyClass(p_name=\"" << p_name << "\")" << std::endl;
    }

    MyClass(MyClass const& p_another)
        : m_name(p_another.m_name)
    {
        test_stream()
            << "- In MyClass::MyClass(p_another=MyClass(\""
            << p_another.m_name << "\"))" << std::endl;
    }

    ~MyClass()
    {
        test_stream() << "- In MyClass(\"" << this->m_name << "\")::~MyClass()" << std::endl;
    }

    boost::shared_ptr<MyClass> clone_and_change(std::string p_new_name)
    {
        test_stream()
            << "- In MyClass(\"" << this->m_name << "\").clone_and_change(p_new_name=\""
            << p_new_name << "\")" << std::endl;

        boost::shared_ptr<MyClass> result(new MyClass(*this));
        result->m_name = p_new_name;

        return result;
    }

    std::string get_name()
    {
        test_stream() << "- In MyClass(\"" << this->m_name << "\").get_name()" << std::endl;
        return this->m_name;
    }

    std::string m_name;
};


struct ScopePreAndPostActions
{
    ScopePreAndPostActions()
    {
        test_stream() << "[Before action...]" << std::endl;
    }

    ~ScopePreAndPostActions()
    {
        test_stream() << "[After action...]" << std::endl;
    }
};





template <class FuncType_>
struct FuncWrapper;

// You can code-generate specializations for other arities...

template <class R_, class A0_, class A1_>
struct FuncWrapper<R_ (A0_, A1_)>
{
    typedef R_ (*func_type)(A0_, A1_);

    typedef typename boost::add_const<typename boost::add_reference<typename A0_>::type>::type AC0_;
    typedef typename boost::add_const<typename boost::add_reference<typename A1_>::type>::type AC1_;

    func_type m_wrapped_func;

    FuncWrapper(func_type p_wrapped_func)
        : m_wrapped_func(p_wrapped_func)
    {
    }

    R_ operator()(AC0_ p0, AC1_ p1)
    {
        ScopePreAndPostActions actions_guard;
        return this->m_wrapped_func(p0, p1);
    }
};

template <
    class R_,
    class C_,
    class A0_=void,
    class A1_=void,
    class A2_=void
    // ...
>
struct MemberFuncWrapper;

template <class R_, class C_, class A0_>
struct MemberFuncWrapper<R_, C_, A0_>
{
    typedef R_ (C_::*member_func_type)(A0_);

    typedef typename boost::add_const<typename boost::add_reference<typename A0_>::type>::type AC0_;

    member_func_type m_wrapped_method;

    MemberFuncWrapper(member_func_type p_wrapped_method)
        : m_wrapped_method(p_wrapped_method)
    {
    }

    R_ operator()(C_* p_self, AC0_ p0)
    {
        ScopePreAndPostActions actions_guard;
        return (p_self->*(this->m_wrapped_method))(p0);
        return R_();
    }
};



namespace boost { namespace python { namespace detail {

    // You can code-generate specializations for other arities...

    template <class R_, class P0_, class P1_>
    inline boost::mpl::vector<R_, P0_, P1_>
    get_signature(FuncWrapper<R_ (P0_, P1_)>, void* = 0)
    {
        return boost::mpl::vector<R_, P0_, P1_>();
    }

    template <class R_, class C_, class P0_>
    inline boost::mpl::vector<R_, C_*, P0_>
    get_signature(MemberFuncWrapper<R_, C_, P0_>, void* = 0)
    {
        return boost::mpl::vector<R_, C_*, P0_>();
    }

} } }

// -------------------------------------------------------------------

template <class FuncPtr_>
void make_wrapper(FuncPtr_);

// You can code-generate specializations for other arities...

template <class R_, class A0_, class A1_>
FuncWrapper<R_ (A0_, A1_)> make_wrapper(R_ (*p_wrapped_func)(A0_, A1_))
{
    return FuncWrapper<R_ (A0_, A1_)>(p_wrapped_func);
}

template <class R_, class C_, class A0_>
MemberFuncWrapper<R_, C_, A0_> make_wrapper(R_ (C_::*p_wrapped_method)(A0_))
{
    return MemberFuncWrapper<R_, C_, A0_>(p_wrapped_method);
}

template <class R_, class C_, class A0_, class A1_>
MemberFuncWrapper<R_, C_, A0_, A1_> make_wrapper(R_ (C_::*p_wrapped_method)(A0_, A1_))
{
    return MemberFuncWrapper<R_, C_, A0_, A1_>(p_wrapped_method);
}


using namespace boost::python;

void RegisterTestWrapper()
{
    def("GetValueAndClearTestStream", &get_value_and_clear_test_stream);
    def("TestFunc", &func);
    def(
        "TestWrappedFunctor",
        make_wrapper(&func)
    );

    {
        class_< MyClass, shared_ptr<MyClass>, boost::noncopyable > c("MyClass", init<std::string>());
        c.def("CloneAndChange", &MyClass::clone_and_change);
        c.def("GetName", &MyClass::get_name);
        c.def("WrappedCloneAndChange", make_wrapper(&MyClass::clone_and_change));
    }
}

そしてPythonで:

import unittest
from _test_wrapper import GetValueAndClearTestStream, TestFunc, TestWrappedFunctor, MyClass

class Test(unittest.TestCase):

    def setUp(self):
        GetValueAndClearTestStream()

    def testWrapper(self):
        self.assertEqual(TestFunc(69, 1.618), 'func(a=69, b=1.618)')
        self.assertEqual(GetValueAndClearTestStream(), '- In func(a=69, b=1.618)\n')

        self.assertEqual(TestWrappedFunctor(69, 1.618), 'func(a=69, b=1.618)')
        self.assertEqual(
            GetValueAndClearTestStream(),
            (
                '[Before action...]\n'
                '- In func(a=69, b=1.618)\n'
                '[After action...]\n'
            ),
        )

def testWrappedMemberFunction(self):
    from textwrap import dedent
    x = MyClass("xx")
    y = x.WrappedCloneAndChange("yy")
    z = y.WrappedCloneAndChange("zz")

    self.assertEqual(x.GetName(), "xx")
    self.assertEqual(y.GetName(), "yy")
    self.assertEqual(z.GetName(), "zz")

    self.assertEqual(
        GetValueAndClearTestStream(),
        dedent('''\
        - In MyClass::MyClass(p_name="xx")
        [Before action...]
        - In MyClass("xx").clone_and_change(p_new_name="yy")
        - In MyClass::MyClass(p_another=MyClass("xx"))
        [After action...]
        [Before action...]
        - In MyClass("yy").clone_and_change(p_new_name="zz")
        - In MyClass::MyClass(p_another=MyClass("yy"))
        [After action...]
        - In MyClass("xx").get_name()
        - In MyClass("yy").get_name()
        - In MyClass("zz").get_name()
        '''),
    )
于 2010-01-26T14:49:10.077 に答える
0

Stroustrupが「C++メンバー関数呼び出しのラッピング」の論文で説明している関数ラッピング手法を見たことがありますか?簡潔な方法でそれを実装する方法を示すSO応答もここにあります。基本的に、オーバーロードするテンプレートを実装しますoperator->()。その実装内でoperator、実際の関数呼び出しの前に一時オブジェクトを作成します。一時オブジェクトのコンストラクタとデストラクタは、実際の関数呼び出しの前後に、それぞれ「pre-」コードと「post-」コード呼び出しを処理します。

于 2010-01-25T22:09:11.130 に答える