¿Cómo escribir una envoltura sobre funciones y funciones miembro que ejecuta algún código antes y después de la función envuelta?

Estoy tratando de escribir alguna clase de envoltura o función que me permita ejecutar algo de código antes y después de la función de envoltura.

float foo(int x, float y) { return x * y; } BOOST_PYTHON_MODULE(test) { boost::python::def("foo", ); } 

Idealmente, la envoltura debe ser genérica, trabajar para funciones y funciones miembro por igual, con cualquier firma.

Más información:

Estoy buscando una forma sencilla de liberar / re-adquirir el GIL en torno a mis costosas llamadas de C ++ sin tener que escribir manualmente envoltorios delgados como este:

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

Este tipo de envoltura se repetirá varias veces para todo tipo de funciones, y me gustaría encontrar una solución que me permita evitar la encoding de todas ellas.

    He intentado algunos enfoques, pero lo mejor que pude hacer fue que el usuario estableciera explícitamente los tipos de valores y parámetros de retorno, como:

     boost::python::def("foo", &wrap_gil(&foo_wrapper)); 

    Pero me parece que debería ser posible pasar el puntero a la función (& foo_wrapper) y dejar que el comstackdor determine los tipos.

    ¿Alguien sabe una técnica que podría usar o me indica la dirección correcta?

    ¡Aclamaciones!

    En este caso, puede escribir una clase de Functor que envuelva su función, y luego sobrecargue boost :: python :: detail :: get_signature para aceptar su Functor.

    ACTUALIZACIÓN: Se agregó soporte para funciones de miembros también!

    Ejemplo:

     #include  #include  #include  #include  #include  #include  #include  static boost::shared_ptr 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 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 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  struct FuncWrapper; // You can code-generate specializations for other arities... template  struct FuncWrapper { typedef R_ (*func_type)(A0_, A1_); typedef typename boost::add_const::type>::type AC0_; typedef typename boost::add_const::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  struct MemberFuncWrapper { typedef R_ (C_::*member_func_type)(A0_); typedef typename boost::add_const::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  inline boost::mpl::vector get_signature(FuncWrapper, void* = 0) { return boost::mpl::vector(); } template  inline boost::mpl::vector get_signature(MemberFuncWrapper, void* = 0) { return boost::mpl::vector(); } } } } // ------------------------------------------------------------------- template  void make_wrapper(FuncPtr_); // You can code-generate specializations for other arities... template  FuncWrapper make_wrapper(R_ (*p_wrapped_func)(A0_, A1_)) { return FuncWrapper(p_wrapped_func); } template  MemberFuncWrapper make_wrapper(R_ (C_::*p_wrapped_method)(A0_)) { return MemberFuncWrapper(p_wrapped_method); } template  MemberFuncWrapper make_wrapper(R_ (C_::*p_wrapped_method)(A0_, A1_)) { return MemberFuncWrapper(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, boost::noncopyable > c("MyClass", init()); c.def("CloneAndChange", &MyClass::clone_and_change); c.def("GetName", &MyClass::get_name); c.def("WrappedCloneAndChange", make_wrapper(&MyClass::clone_and_change)); } } 

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

    ¿Ha analizado la técnica de ajuste de funciones descrita por Stroustrup en su documento ” Envolver llamadas de funciones de miembro de C ++ “? También hay una respuesta SO aquí que demuestra cómo implementarla de manera concisa. Básicamente implementaría una plantilla que sobrecarga al operator->() . Dentro de la implementación de ese operator , usted construiría un objeto temporal antes de su llamada a la función real. El constructor y destructor del objeto temporal se encarga de invocar su código ” pre- ” y ” post- ” antes y después de su llamada a la función real, respectivamente.