복붙노트

[PYTHON] 래핑 된 함수 앞과 뒤에 어떤 코드를 실행하는 함수와 멤버 함수 위에 래퍼를 쓰는 방법?

PYTHON

래핑 된 함수 앞과 뒤에 어떤 코드를 실행하는 함수와 멤버 함수 위에 래퍼를 쓰는 방법?

래퍼 클래스 또는 래핑 된 함수 전후에 일부 코드를 실행할 수있는 함수를 작성하려고합니다.

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

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

이상적으로, 래퍼는 함수와 멤버 함수 모두에 대해 시그너처를 사용하여 일반적으로 작동해야합니다.

더 많은 정보:

나는 간단한 방법을 찾고 릴리스 / 재 비싼 C ++ 호출 주위에 수동으로 얇은 래퍼를 쓰지 않고도 다시 얻으려면 :

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)에 포인터를 전달하고 컴파일러가 유형을 알아낼 수 있어야합니다.

누구든지 내가 사용할 수 있거나 올바른 방향으로 나를 가리킬 수있는 기술을 알고 있습니까?

건배!

해결법

  1. ==============================

    1.이 경우 함수를 감싸는 Functor 클래스를 작성한 다음 boost :: python :: detail :: get_signature를 오버로드하여 Functor를 수락 할 수 있습니다.

    이 경우 함수를 감싸는 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));
        }
    }
    

    그리고 파이썬에서 :

    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()
            '''),
        )
    
  2. ==============================

    2.Stroustrup이 "Wrapping C ++ Member Function Calls"논문에서 설명한 함수 랩핑 기법을 살펴 보았습니까? 여기에는 간결한 방식으로 구현하는 방법을 보여주는 SO 응답도 있습니다. 기본적으로 연산자 -> ()를 오버로드하는 템플릿을 구현합니다. 해당 연산자의 구현 내에서 실제 함수 호출 전에 임시 객체를 생성합니다. 임시 객체의 생성자와 소멸자는 실제 함수 호출 전후의 "pre"와 "post"코드를 호출합니다.

    Stroustrup이 "Wrapping C ++ Member Function Calls"논문에서 설명한 함수 랩핑 기법을 살펴 보았습니까? 여기에는 간결한 방식으로 구현하는 방법을 보여주는 SO 응답도 있습니다. 기본적으로 연산자 -> ()를 오버로드하는 템플릿을 구현합니다. 해당 연산자의 구현 내에서 실제 함수 호출 전에 임시 객체를 생성합니다. 임시 객체의 생성자와 소멸자는 실제 함수 호출 전후의 "pre"와 "post"코드를 호출합니다.

  3. from https://stackoverflow.com/questions/2135457/how-to-write-a-wrapper-over-functions-and-member-functions-that-executes-some-co by cc-by-sa and MIT license