Как записать обертку по функциям и функциям членства, который выполняет некоторый код прежде и после перенесенной функции?

Я пытаюсь записать некоторый класс обертки или функцию, которая позволяет мне выполнять некоторый код прежде и после перенесенной функции.

float foo(int x, float y)
{
    return x * y;
}

BOOST_PYTHON_MODULE(test)
{
     boost::python::def("foo", <somehow wrap "&foo">);
}

Идеально, обертка должна быть универсальной, работающий на функции и функции членства одинаково, с любой подписью.

Подробнее:

Я ищу простой путь к release/re-acquire GIL вокруг моих дорогих вызовов C++, не имея необходимость вручную писать тонкие обертки как это:

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) и позволить компилятору выяснить типы.

Кто-либо знает технику, я мог использовать или указать на меня в правильном направлении?

Удачи!

8
задан Bruno Oliveira 25 January 2010 в 22:46
поделиться

2 ответа

-- 2876341-

В этом случае вы можете написать класс функтора, который оборачивает свою функцию, а затем перегружать Boost :: Python :: Detail :: Get_Signature, чтобы принять ваш функтор!

Обновление: добавлена ​​поддержка функций для членов тоже!

Пример:

#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()
        '''),
    )
11
ответ дан 5 December 2019 в 15:23
поделиться

Вы смотрели на технику упаковки функций, описываемые Strourstrup в его «, оборудованных функциями функции члена C ++ ? Здесь также есть такой ответ , который демонстрирует, как реализовать его в кратчайшие сроки. По сути, вы реализуете шаблон, который перегрузки Оператор -> () . В рамках этого оператора реализация вы построите временный объект до вашего фактического вызова функции. Конструктор временного объекта и деструктор позаботитется о том, чтобы вызвать ваш « Pre- и« Post- код до и после фактического вызова функции соответственно.

0
ответ дан 5 December 2019 в 15:23
поделиться
Другие вопросы по тегам:

Похожие вопросы: