Skip to content Skip to sidebar Skip to footer

How To Write A Wrapper Over Functions And Member Functions That Executes Some Code Before And After The Wrapped Function?

I'm trying to write some wrapper class or function that allows me to execute some code before and after the wrapped function. float foo(int x, float y) { return x * y; } BOOST

Solution 1:

In this case, you can write a Functor class that wraps over your function, and then overload boost::python::detail::get_signature to accept your Functor!

UPDATE: Added support for member functions too!

Example:

#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;
}


classMyClass
{
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;
        returnthis->m_name;
    }

    std::string m_name;
};


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

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





template <classFuncType_>
structFuncWrapper;

// You can code-generate specializations for other arities...template <classR_, classA0_, classA1_>
structFuncWrapper<R_ (A0_, A1_)>
{
    typedefR_(*func_type)(A0_, A1_);

    typedeftypename boost::add_const<typename boost::add_reference<typename A0_>::type>::type AC0_;
    typedeftypename 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;
        returnthis->m_wrapped_func(p0, p1);
    }
};

template <
    classR_,
    classC_,
    classA0_=void,
    class A1_=void,
    class A2_=void// ...
>
struct MemberFuncWrapper;

template <classR_, classC_, classA0_>
structMemberFuncWrapper<R_, C_, A0_>
{
    typedefR_(C_::*member_func_type)(A0_);

    typedeftypename 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);
        returnR_();
    }
};



namespace boost { namespace python { namespace detail {

    // You can code-generate specializations for other arities...template <classR_, classP0_, classP1_>
    inline boost::mpl::vector<R_, P0_, P1_>
    get_signature(FuncWrapper<R_ (P0_, P1_)>, void* = 0){
        return boost::mpl::vector<R_, P0_, P1_>();
    }

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

} } }

// -------------------------------------------------------------------template <classFuncPtr_>
voidmake_wrapper(FuncPtr_);

// You can code-generate specializations for other arities...template <classR_, classA0_, classA1_>
FuncWrapper<R_(A0_, A1_)> make_wrapper(R_ (*p_wrapped_func)(A0_, A1_)){
    returnFuncWrapper<R_ (A0_, A1_)>(p_wrapped_func);
}

template <classR_, classC_, classA0_>
MemberFuncWrapper<R_, C_, A0_> make_wrapper(R_ (C_::*p_wrapped_method)(A0_)){
    returnMemberFuncWrapper<R_, C_, A0_>(p_wrapped_method);
}

template <classR_, classC_, classA0_, classA1_>
MemberFuncWrapper<R_, C_, A0_, A1_> make_wrapper(R_ (C_::*p_wrapped_method)(A0_, A1_)){
    returnMemberFuncWrapper<R_, C_, A0_, A1_>(p_wrapped_method);
}


usingnamespace boost::python;

voidRegisterTestWrapper(){
    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));
    }
}

And on python:

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

classTest(unittest.TestCase):

    defsetUp(self):
        GetValueAndClearTestStream()

    deftestWrapper(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'
            ),
        )

deftestWrappedMemberFunction(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()
        '''),
    )

Solution 2:

Have you looked at the function wrapping technique described by Stroustrup in his "Wrapping C++ Member Function Calls" paper? There's also a SO response here that demonstrates how to implement it in a concise manner. Basically you'd implement a template that overloads operator->(). Within that operator's implementation you'd construct a temporary object before your actual function call. The temporary object's constructor and destructor take care of invoking your "pre-" and "post-" code before and after your actual function call, respectively.

Post a Comment for "How To Write A Wrapper Over Functions And Member Functions That Executes Some Code Before And After The Wrapped Function?"