2010-01-25 7 views
8

मैं कुछ रैपर वर्ग या फ़ंक्शन लिखने की कोशिश कर रहा हूं जो मुझे लपेटा हुआ फ़ंक्शन से पहले और बाद में कुछ कोड निष्पादित करने की अनुमति देता है।फ़ंक्शंस और सदस्य फ़ंक्शंस पर एक रैपर कैसे लिखें जो लिपटे फ़ंक्शन से पहले और बाद में कुछ कोड निष्पादित करता है?

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

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

आदर्श रूप से, रैपर सामान्य होना चाहिए, कार्यों और सदस्य कार्यों के लिए काम करना चाहिए, किसी भी हस्ताक्षर के साथ।

और जानकारी:

मैं/जारी करने के लिए मेरी महंगा सी ++ मैन्युअल इस तरह पतली रैपर लिखने के बिना कॉल के आसपास जीआईएल फिर से प्राप्त एक आसान तरीका के लिए देख रहा हूँ:

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) और कंपाइलर को प्रकारों को समझने दें।

क्या कोई ऐसी तकनीक जानता है जिसे मैं सही दिशा में उपयोग या इंगित कर सकता हूं?

चीयर्स!

उत्तर

11

इस मामले में, आप एक फ़ंक्शन क्लास लिख सकते हैं जो आपके फ़ंक्शन पर लपेटता है, और फिर ओवरलोड बूस्ट :: पायथन :: विवरण :: 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)); 
    } 
} 

और अजगर पर:

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

बिल्कुल सही समाधान! बहुत धन्यवाद! =) –

+1

आप पैरामीटर के संदर्भ संदर्भों के बजाय केवल संदर्भ जोड़ सकते हैं, ताकि वे स्थिरता खराब न हों। –

+0

इस उत्कृष्ट समाधान के लिए धन्यवाद! मुझे पता है कि यह एक पुराना धागा है, लेकिन अगर किसी और के पास आता है, तो मुझे लगता है कि एक सुधार किया जाना है। 'AC0_',' AC1_', आदि टाइपिफ़ के लिए मुझे लगता है कि आप 'add_const' और' add_reference' के स्थानों को स्वैप करना चाहते हैं। स्निपेट में जिस तरह से किया गया है, 'add_const' का कोई प्रभाव नहीं प्रतीत होता है। – histumness

0

क्या आपने स्ट्रॉस्ट्रप द्वारा वर्णित फ़ंक्शन रैपिंग तकनीक को अपने "Wrapping C++ Member Function Calls" पेपर में देखा है? एक SO प्रतिक्रिया here भी है जो दर्शाता है कि इसे संक्षिप्त तरीके से कैसे कार्यान्वित किया जाए। असल में आप एक टेम्पलेट को लागू करेंगे जो operator->() ओवरलोड करता है। उस operator के कार्यान्वयन के भीतर आप अपने वास्तविक फ़ंक्शन कॉल से पहले एक अस्थायी वस्तु का निर्माण करेंगे। अस्थायी ऑब्जेक्ट का कन्स्ट्रक्टर और विनाशक आपके "प्री -" और "पोस्ट -" क्रमशः आपके वास्तविक फ़ंक्शन कॉल से पहले और बाद में कोड का आह्वान करने का ख्याल रखता है।