2012-12-05 20 views
5

तो मैं एक छोटी परियोजना पर काम कर रहा हूं जिसमें मैं पाइथन को एम्बेडेड स्क्रिप्टिंग इंजन के रूप में उपयोग कर रहा हूं। अभी तक मुझे boost.python का उपयोग करके इसमें बहुत परेशानी नहीं है, लेकिन अगर ऐसा संभव है तो मैं इसके साथ कुछ करना चाहता हूं।बूस्ट। पायथन - पासिंग बूस्ट :: पायथन :: ऑब्जेक्ट पाइथन फ़ंक्शन के लिए तर्क के रूप में?

असल में, पायथन का उपयोग कक्षाओं में कार्यों और डेटा मानों को जोड़कर मेरे सी ++ कक्षाओं को बढ़ाने के लिए किया जा सकता है। मैं इन्हें सी ++ पक्ष में जारी रखने में सक्षम होना चाहता हूं, इसलिए एक पायथन फ़ंक्शन कक्षा के सदस्यों को कक्षा में जोड़ सकता है, और उसके बाद बाद में एक ही फ़ंक्शन को एक अलग फ़ंक्शन में पास कर दिया जाएगा। यहां लक्ष्य सी ++ में एक सामान्य कोर इंजन लिखना है, और उपयोगकर्ताओं को इसे पायथन में विस्तारित करने के लिए किसी भी तरह से सी ++ को छूने के बिना इसकी आवश्यकता होती है।

मैं तो क्या सोचा काम करेगा था कि मैं एक मूल्य self, और जब सी ++ से अजगर बुला, मैं boost::python::ptr() के माध्यम से उस अजगर वस्तु भेजना चाहते हैं के रूप में सी ++ कक्षा में एक boost::python::object संग्रहीत करेंगे, ताकि पर संशोधनों पायथन पक्ष सी ++ कक्षा में वापस बनेगा।

TypeError: No to_python (by-value) converter found for C++ type: boost::python::api::object

वहाँ इस तरह एक अजगर समारोह के लिए सीधे एक वस्तु पास करने का कोई तरीका है, या प्राप्त करने के लिए किसी अन्य तरीके से मैं इस बारे में जा सकते हैं मेरी वांछित: दुर्भाग्य से जब मैं यह कोशिश, मैं निम्नलिखित त्रुटि मिलती है परिणाम?

किसी भी मदद के लिए अग्रिम धन्यवाद। :)

+0

एक समाधान मिला। डीईएफ़ ProcessEvent (घटना, obj): वैश्विक() [घटना] (obj.PyObj) लौट C++ में, मैं जोड़ा एक 'बढ़ावा देने :: अजगर :: object' अजगर में, मैं एक समारोह जोड़ा मेरे सी ++ वर्ग में 'पायओब्ज' नाम दिया गया है, जिसे 'बूस्ट :: पायथन :: पीआरटी (यह)' में शुरू किया गया है, और इसके साथ मेरी घटनाओं को बुलाया गया है, जिस घटना को मैं पहले पैरामीटर के रूप में कॉल करना चाहता हूं और ' boost :: python :: ptr' ऑब्जेक्ट में मैं इसे दूसरे के रूप में पास करना चाहता हूं। यह काम करता है जैसा कि मैंने आशा की थी - जब मैंने एक घटना में एक विशेषता जोड़ा, तो यह तब भी था जब मैंने इसे दूसरे पास कर दिया। शायद सबसे अच्छा समाधान नहीं है ... लेकिन, यह काम करता है। –

उत्तर

5

सी ++ सिग मेलिंग सूची से यह शानदार समाधान मिला।

सी ++ कक्षा में std::map<std::string, boost::python::object> लागू करें, फिर उस std :: मानचित्र से पढ़ने और लिखने के लिए __getattr__() और __setattr__() ओवरलोड करें। फिर इसे सामान्य रूप से boost::python::ptr() के साथ पाइथन पर भेजें, किसी ऑब्जेक्ट को C++ पक्ष पर रखने की आवश्यकता नहीं है या एक पायथन पर भेजें। यह पूरी तरह से काम करता है।

संपादित करें: मैंने यह भी पाया कि मुझे __setattr__() फ़ंक्शन को विशेष तरीके से ओवरराइड करना था क्योंकि यह add_property() के साथ जोड़े गए चीज़ों को तोड़ रहा था। उन चीजों को प्राप्त करते समय ठीक काम किया, क्योंकि पाइथन __getattr__() पर कॉल करने से पहले कक्षा के गुणों की जांच करता है, लेकिन __setattr__() के साथ ऐसी कोई जांच नहीं है। यह सिर्फ इसे सीधे कहते हैं। तो मुझे इसे एक पूर्ण समाधान में बदलने के लिए कुछ बदलाव करना पड़ा।

boost::python::object PyMyModule_global; 

एक वर्ग (आप इसे में जोड़ना चाहते हैं जो कुछ भी अन्य जानकारी के साथ) के रूप में बनाएँ::

class MyClass 
{ 
public: 
    //Python checks the class attributes before it calls __getattr__ so we don't have to do anything special here. 
    boost::python::object Py_GetAttr(std::string str) 
    { 
     if(dict.find(str) == dict.end()) 
     { 
     PyErr_SetString(PyExc_AttributeError, JFormat::format("MyClass instance has no attribute '{0}'", str).c_str()); 
     throw boost::python::error_already_set(); 
     } 
     return dict[str]; 
    } 

    //However, with __setattr__, python doesn't do anything with the class attributes first, it just calls __setattr__. 
    //Which means anything that's been defined as a class attribute won't be modified here - including things set with 
    //add_property(), def_readwrite(), etc. 
    void Py_SetAttr(std::string str, boost::python::object val) 
    { 
     try 
     { 
     //First we check to see if the class has an attribute by this name. 
     boost::python::object obj = PyMyModule_global["MyClass"].attr(str.c_str()); 
     //If so, we call the old cached __setattr__ function. 
     PyMyModule_global["MyClass"].attr("__setattr_old__")(ptr(this), str, val); 
     } 
     catch(boost::python::error_already_set &e) 
     { 
     //If it threw an exception, that means that there is no such attribute. 
     //Put it on the persistent dict. 
     PyErr_Clear(); 
     dict[str] = val; 
     } 
    } 
private: 
    std::map<std::string, boost::python::object> dict; 
}; 

पहले एक वैश्विक चर बनाने: यहाँ समाधान का पूरा कार्यान्वयन है फिर पाइथन मॉड्यूल को निम्नानुसार परिभाषित करें, जो भी अन्य डिफ ​​और गुण आप चाहते हैं:

BOOST_PYTHON_MODULE(MyModule) 
{ 
    boost::python::class_<MyClass>("MyClass", boost::python::no_init) 
     .def("__getattr__", &MyClass::Py_GetAttr) 
     .def("__setattr_new__", &MyClass::Py_SetAttr); 
} 
void PyInit() 
{ 
    //Initialize module 
    PyImport_AppendInittab("MyModule", &initMyModule); 
    //Initialize Python 
    Py_Initialize(); 

    //Grab __main__ and its globals 
    boost::python::object main = boost::python::import("__main__"); 
    boost::python::object global = main.attr("__dict__"); 

    //Import the module and grab its globals 
    boost::python::object PyMyModule = boost::python::import("MyModule"); 
    global["MyModule"] = PyMyModule; 
    PyMyModule_global = PyMyModule.attr("__dict__"); 

    //Overload MyClass's setattr, so that it will work with already defined attributes while persisting new ones 
    PyMyModule_global["MyClass"].attr("__setattr_old__") = PyMyModule_global["MyClass"].attr("__setattr__"); 
    PyMyModule_global["MyClass"].attr("__setattr__") = PyMyModule_global["MyClass"].attr("__setattr_new__"); 
} 

एक बार जब आप यह सब किया है, तो आप सी ++ के लिए खत्म हो अजगर में किए गए उदाहरण में परिवर्तन लागू करने के लिए सक्षम हो जाएगा:तो अजगर आरंभ कर देगा। किसी विशेषता के रूप में C++ में परिभाषित कुछ भी ठीक से संभाला जाएगा, और जो भी नहीं है उसे कक्षा के __dict__ के बजाय dict में जोड़ा जाएगा।

+0

बढ़िया! साझा करने के लिए धन्यवाद! – mrmclovin

संबंधित मुद्दे