如何编写函数和成员函数的包装器,以便在包装函数之前和之后执行一些代码?

发布于 2024-08-19 03:47:46 字数 899 浏览 8 评论 0原文

我正在尝试编写一些包装类或函数,使我可以在包装函数之前和之后执行一些代码。

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

但在我看来,应该可以仅将指针传递给函数(&foo_wrapper )并让编译器找出类型。

有谁知道我可以使用的技术或为我指明正确的方向?

干杯!

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!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

睫毛上残留的泪 2024-08-26 03:47:46

在这种情况下,您可以编写一个封装函数的 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()
        '''),
    )

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()
        '''),
    )
感性不性感 2024-08-26 03:47:46

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文