我正在尝试编写一些包装类或函数,它允许我在包装函数之前和之后执行一些代码。
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));
但在我看来应该可以将指针传递给函数(&amp; foo_wrapper)并让编译器找出类型。
有没有人知道我可以使用的技术或指向正确的方向?
干杯!
答案 0 :(得分: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()
'''),
)
答案 1 :(得分:0)
你看过Stroustrup在他的“Wrapping C++ Member Function Calls”论文中描述的函数包装技术吗?还有一个SO响应here,它演示了如何以简洁的方式实现它。基本上你要实现一个重载operator->()
的模板。在operator
的实现中,您将在实际函数调用之前构造一个临时对象。临时对象的构造函数和析构函数分别负责在实际函数调用之前和之后调用“ pre - ”和“ post - ”代码。