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_PYTHON_MODULE(test)
{
boost::python::def("foo", <somehow wrap "&foo">);
}
Ideally, the wrapper should be generic, working for functions and member functions alike, with any signature.
More info:
I'm looking for a simple way to release/re-acquire the GIL around my expensive C++ calls without having to manually write thin wrappers like this:
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);
}
This kind of wrapper will be repeated several times for all kinds of functions, and I would like to find a solution that would allow me to avoid coding all of them.
I have tried some approaches, but the best I could come with required the user to explicitly state the types of return values and parameters, like:
boost::python::def("foo", &wrap_gil<float, int, float>(&foo_wrapper));
But it seems to me it should be possible to just pass the pointer to the function (&foo_wrapper) and let the compiler figure out the types.
Does anyone know a technique I could use or point me in the right direction?
Cheers!
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;
}
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));
}
}
And on 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()
'''),
)
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.