2012-01-08 13 views
6

मैंने सी पुस्तकालय के लिए एक पायथन एक्सटेंशन लिखा है।एसडब्ल्यूआईजी लाइब्रेरी में सी लाइब्रेरी इंटरफेसिंग (सी 'अनुक्रम' संरचना से 'पुनरावर्तनीय' पायथन डेटा प्रकार बनाना)

typedef struct _mystruct{ 
    double * clientdata; 
    size_t len; 
} MyStruct; 

इस डेटाप्रकार के प्रयोजन के अजगर में सूची डेटा प्रकार के लिए सीधे नक्शे: मैं एक डेटा संरचना है कि इस तरह दिखता है। इसलिए, मैं निर्यातित संरचना के लिए 'सूची-जैसे' व्यवहार बनाना चाहता हूं, ताकि मेरे सी एक्सटेंशन का उपयोग करके लिखा गया कोड अधिक 'पायथनिक' हो।

विशेष रूप से, मैं यही करना चाहता हूं (पायथन कोड से) नोट: py_ctsruct पाइथन में एक ctsruct डेटाटाइप का उपयोग किया जा रहा है।

मेरे आवश्यकताओं के रूप में sumarized जा सकता है:

  1. सूची (py_ctsruct) रिटर्न सी struct से बाहर की नकल की सभी सामग्री के साथ एक अजगर सूची
  2. py_cstruct [i] रिटर्न ith तत्व (अधिमानतः अवैध सूचकांक पर IndexError फेंकता)
  3. py_ctsruct में ELEM के लिए: की गणना करने की क्षमता

PEP234 के अनुसार, एक वस्तु अगर यह लागू करता _ आईटीईआर "के लिए" के साथ खत्म हो गया दोहराया जा सकता है _() या _ GetItem _()। उस तर्क का प्रयोग करते हुए, मुझे लगता है कि मेरे एसडब्ल्यूआईजी इंटरफ़ेस फ़ाइल में निम्नलिखित विशेषताओं को जोड़कर (rename के माध्यम से), मेरे पास वांछित व्यवहार होगा (ऊपर req # 1 के अलावा - जो मुझे अभी भी नहीं पता है कि कैसे प्राप्त किया जाए):

__len__ 
__getitem__ 
__setitem__ 

अब मैं पाइथन में सी ऑब्जेक्ट को अनुक्रमणित करने में सक्षम हूं। मैंने अभी तक पायथन अपवाद फेंकने को लागू नहीं किया है, हालांकि यदि सरणी सीमा पार हो गई है, तो एक जादू संख्या (त्रुटि कोड) लौटा दी जाती है।

दिलचस्प बात यह है कि जब मैं उदाहरण के लिए वाक्य रचना 'में एक्स के लिए' struct का उपयोग करने पर पुनरावृति करने का प्रयास:

for i in py_cstruct: 
    print i 

अजगर अनंत लूप कि बस जादू (त्रुटि) संख्या का उल्लेख प्रिंट में प्रवेश करती है कंसोल पर, ऊपर। जो मुझे बताता है कि अनुक्रमण के साथ कुछ गड़बड़ है।

अंतिम लेकिन कम से कम नहीं, मैं आवश्यकता 1 को कैसे कार्यान्वित कर सकता हूं? इस शामिल है (मैं यह समझ के रूप में):

  • से निपटने 'समारोह कॉल सूची() अजगर
  • एक अजगर (सूची) डेटा सी कोड से प्रकार रिटर्निंग से

[[ अद्यतन]]

मुझे अपनी इंटरफ़ेस फ़ाइल में क्या (यदि कोई है) घोषणाओं पर एक छोटा कोड स्निपेट देखने में दिलचस्पी होगी, ताकि मैं सी सेंट के तत्वों पर पुन: प्रयास कर सकूं पाइथन से रैक।

उत्तर

17

को सरल समाधान:

PyErr_SetString(PyExc_StopIteration,"End of list"); 
return NULL; 

यहाँ इस मुद्दे पर आगे पढ़ने के लिए एक और महान जवाब है:

अपने समारोह में

इसलिए, जब आप सरणी के अंत तक पहुँचते हैं, तो आप सिर्फ इस की जरूरत है यह __getitem__ को लागू करना है और एक अवैध अनुक्रमणिका के लिए IndexError अपवाद फेंकना है।

मैं एक साथ इस का एक उदाहरण रखा, __getitem__ को लागू करने और क्रमश: एक अपवाद को बढ़ाने के लिए %extend और बड़ा घूँट में %exception का उपयोग कर:

static MyStruct *test() { 
    static MyStruct inst = {0,0}; 
    if (!inst.clientdata) { 
    inst.len = 10; 
    inst.clientdata = malloc(sizeof(double)*inst.len); 
    for (size_t i = 0; i < inst.len; ++i) { 
     inst.clientdata[i] = i; 
    } 
    } 
    return &inst; 
} 
: मैं test.h को जोड़कर यह परीक्षण किया

%module test 

%include "exception.i" 

%{ 
#include <assert.h> 
#include "test.h" 
static int myErr = 0; // flag to save error state 
%} 

%exception MyStruct::__getitem__ { 
    assert(!myErr); 
    $action 
    if (myErr) { 
    myErr = 0; // clear flag for next time 
    // You could also check the value in $result, but it's a PyObject here 
    SWIG_exception(SWIG_IndexError, "Index out of bounds"); 
    } 
} 

%include "test.h" 

%extend MyStruct { 
    double __getitem__(size_t i) { 
    if (i >= $self->len) { 
     myErr = 1; 
     return 0; 
    } 
    return $self->clientdata[i]; 
    } 
} 

और निम्न पायथन चला रहा है:

import test 

for i in test.test(): 
    print i 

कौन सा प्रिंट:

python run.py 
0.0 
1.0 
2.0 
3.0 
4.0 
5.0 
6.0 
7.0 
8.0 
9.0 

और फिर समाप्त होता है।


एक वैकल्पिक दृष्टिकोण एक typemap का उपयोग कर एक PyList सीधे MyStruct मैप करने के लिए भी संभव है:

%module test 

%{ 
#include "test.h" 
%} 

%typemap(out) (MyStruct *) { 
    PyObject *list = PyList_New($1->len); 
    for (size_t i = 0; i < $1->len; ++i) { 
    PyList_SetItem(list, i, PyFloat_FromDouble($1->clientdata[i])); 
    } 

    $result = list; 
} 

%include "test.h" 

यह किसी भी समारोह है कि एक MyStruct * रिटर्न से वापसी मान वाला PyList पैदा करेगा। मैंने पिछली विधि के समान कार्य के साथ इस %typemap(out) का परीक्षण किया।

तुम भी एक इसी %typemap(in) और %typemap(freearg) रिवर्स के लिए, इस अपरीक्षित कोड की तरह कुछ लिख सकते हैं: पुनरावर्तक का उपयोग जुड़ा हुआ सूचियों की तरह कंटेनरों के लिए और अधिक समझ बनाने हैं

%typemap(in) (MyStruct *) { 
    if (!PyList_Check($input)) { 
    SWIG_exception(SWIG_TypeError, "Expecting a PyList"); 
    return NULL; 
    } 
    MyStruct *tmp = malloc(sizeof(MyStruct)); 
    tmp->len = PyList_Size($input); 
    tmp->clientdata = malloc(sizeof(double) * tmp->len); 
    for (size_t i = 0; i < tmp->len; ++i) { 
    tmp->clientdata[i] = PyFloat_AsDouble(PyList_GetItem($input, i)); 
    if (PyErr_Occured()) { 
     free(tmp->clientdata); 
     free(tmp); 
     SWIG_exception(SWIG_TypeError, "Expecting a double"); 
     return NULL; 
    } 
    } 
    $1 = tmp; 
} 

%typemap(freearg) (MyStruct *) { 
    free($1->clientdata); 
    free($1); 
} 

, लेकिन पूर्णता के लिए यहां MyStruct__iter__ के साथ ऐसा करने के बारे में आप कैसे जा सकते हैं। मुख्य बात यह है कि आपको __iter__() और next() की आवश्यकता होती है, जो MyStructIter को %inline का उपयोग करके एक ही समय में परिभाषित और लपेटा जाता है क्योंकि यह सामान्य सी एपीआई का हिस्सा नहीं है:

%module test 

%include "exception.i" 

%{ 
#include <assert.h> 
#include "test.h" 
static int myErr = 0; 
%} 

%exception MyStructIter::next { 
    assert(!myErr); 
    $action 
    if (myErr) { 
    myErr = 0; // clear flag for next time 
    PyErr_SetString(PyExc_StopIteration, "End of iterator"); 
    return NULL; 
    } 
} 

%inline %{ 
    struct MyStructIter { 
    double *ptr; 
    size_t len; 
    }; 
%} 

%include "test.h" 

%extend MyStructIter { 
    struct MyStructIter *__iter__() { 
    return $self; 
    } 

    double next() { 
    if ($self->len--) { 
     return *$self->ptr++; 
    } 
    myErr = 1; 
    return 0; 
    } 
} 

%extend MyStruct { 
    struct MyStructIter __iter__() { 
    struct MyStructIter ret = { $self->clientdata, $self->len }; 
    return ret; 
    } 
} 

iteration over containers के लिए आवश्यकताओं को ऐसी है कि कंटेनर __iter__() को लागू करने और एक नया इटरेटर वापस जाने के लिए की जरूरत है कर रहे हैं, लेकिन next() जो अगले आइटम वापस आती है और इटरेटर इटरेटर खुद भी एक __iter__() विधि की आपूर्ति करनी होगी वृद्धि कर देता है के अलावा। इसका मतलब है कि कंटेनर या इटरेटर को समान रूप से इस्तेमाल किया जा सकता है।

MyStructIter को पुनरावृत्ति की वर्तमान स्थिति का ट्रैक रखने की आवश्यकता है - जहां हम हैं और हमने कितना छोड़ा है। इस उदाहरण में मैंने अगले आइटम पर एक पॉइंटर रखकर और एक काउंटर जिसे हम अंत में मारते समय बताने के लिए उपयोग करते थे। (

%inline %{ 
    struct MyStructIter { 
    MyStruct *list; 
    size_t pos; 
    }; 
%} 

%include "test.h" 

%extend MyStructIter { 
    struct MyStructIter *__iter__() { 
    return $self; 
    } 

    double next() { 
    if ($self->pos < $self->list->len) { 
     return $self->list->clientdata[$self->pos++]; 
    } 
    myErr = 1; 
    return 0; 
    } 
} 

%extend MyStruct { 
    struct MyStructIter __iter__() { 
    struct MyStructIter ret = { $self, 0 }; 
    return ret; 
    } 
} 

इस उदाहरण में हम वास्तव में सिर्फ इस्तेमाल किया जा सकता था: तुम भी तरह MyStruct iterator उपयोग कर रहा है के लिए एक सूचक है और उस के भीतर की स्थिति के लिए एक काउंटर, कुछ रखकर sate पर नज़र रखी जा सकता था __iter__() की आपूर्ति करके प्रति और पहले प्रकार के समान next() की प्रतिलिपि बनाकर इसे एक पुनरावर्तक के रूप में कंटेनर के रूप में।मैं क्या किया नहीं है कि मेरी मूल जवाब में क्योंकि मैंने सोचा की तुलना में दो भिन्न प्रकार की है कि कम स्पष्ट हो सकता है - एक कंटेनर और कहा कि कंटेनर के लिए एक इटरेटर)

+0

स्निपेट के लिए धन्यवाद। त्वरित सवाल, क्या आपने पाइथन अंत से इसका परीक्षण किया? मैं टाइपमैप और पायलिस्ट/पायटुपल के बारे में बहुत कुछ नहीं जानता लेकिन मुझे संदेह है कि वे जो कुछ मैं प्राप्त करना चाहता हूं उसके करीब हैं (यानी सी स्ट्रक्चर को पायथन अनुक्रम प्रकार के रूप में देखें)। मुख्य बिंदु हालांकि, यह है कि मेरी मुख्य समस्या यह है कि मैं सी संरचना में तत्वों को पुन: सक्रिय करने में सक्षम नहीं हूं। शायद सी स्ट्रक्चर को सूची या टुपल के रूप में उजागर करने में दो पक्षियों (पुनरावृत्ति और सरणी सीमाओं की जांच) को मारने में मदद मिलती है। वे दो समस्याएं हैं जिन्हें मैं हल करने की कोशिश कर रहा हूं - क्या टाइपमैप + पिलिस्ट आगे बढ़ रहा है? तुम क्या सोचते हो? –

+0

@ होमुनकुलस रेटिकुली - मैंने दिखाए गए 'टेस्ट()' फ़ंक्शन का उपयोग करके पाइथन पक्ष से सब कुछ परीक्षण किया, हालांकि टाइपमैप के लिए मैंने केवल '% टाइपमैप (आउट) 'का परीक्षण किया, लेकिन दोनों विधियों ने' के लिए ' में ... पायथन निर्माण। – Flexo

+0

@ होमुनकुलस रेटिकुली - मैंने अपने उत्तर में '__iter __()' संस्करण भी जोड़ा है, हालांकि मैं इस उदाहरण में '__getitem __()' संस्करण को अधिक पसंद करता हूं। उपयोगी स्निपेट और विस्तृत स्पष्टीकरण के लिए अब तक – Flexo

1
  1. % typemap swig कमांड का उपयोग करके देखें। http://www.swig.org/Doc2.0/SWIGDocumentation.html#Typemaps http://www.swig.org/Doc2.0/SWIGDocumentation.html#Typemaps_nn25 सदस्यिन टाइपमैप जो भी आप चाहते हैं वह कर सकता है। http://www.swig.org/Doc2.0/SWIGDocumentation.html#Typemaps_nn35 मेरे पास एक टाइपमैप है जो मुझे पायथन खंड में मिला है जो मुझे पाइथन स्ट्रिंग्स की सूची के रूप में सी **+ में चार + डेटा स्थानांतरित करने की अनुमति देता है। मुझे लगता है कि समान कार्यक्षमता होगी।
  2. इसके अलावा, आप स्विंग "i" फ़ाइल के अंदर संरचना के अंदर अपने इंटरफ़ेस में% पायथनकोड परिभाषित कर सकते हैं। यह आपको उस ऑब्जेक्ट में पाइथन विधियों को जोड़ने की अनुमति देगा जो संरचना के लिए बनाए जाते हैं। एक और कमांड% addmethod (मुझे लगता है) है जो आपको संरचना या कक्षा में विधियों को जोड़ने की अनुमति देता है। फिर आप चाहें तो सी ++ या सी में ऑब्जेक्ट्स को अनुक्रमणित करने के तरीकों को बना सकते हैं। इसे हल करने के कई तरीके हैं।

एक इंटरफेस के लिए मैं काम कर रहा हूं, मैंने कक्षा ऑब्जेक्ट का उपयोग किया है जिसमें मेरे कोड में डेटा तक पहुंचने के कुछ तरीके हैं। उन तरीकों को सी ++ में लिखा गया है। फिर मैंने "i" फ़ाइल के अंदर कक्षा के अंदर% pythoncode निर्देश का उपयोग किया और "getitem" और "setitem" पाइथन कोड में विधियों का उपयोग किया जो सी ++ विधियों का उपयोग करने के लिए एक शब्दकोश शैली की पहुंच की तरह दिखने के लिए उपयोग करता है।

+0

% pythoncode सुझाव के लिए +1। यह मुझे कुछ अन्य कार्यक्षमता (ऑब्जेक्ट गुणों तक पहुंचने) को लागू करने की अनुमति देता है। मैं अभी भी पाइथन में अपने सी structs पर फिर से (समझदारी से) करने में सक्षम नहीं हूँ। –

+0

मुझे लगता है कि आपको संरचना में डेटा टाइप करने की आवश्यकता है। मैंने कुछ लिंक शामिल किए। सदस्य कार्य वह हो सकता है जो आप सोच रहे हैं। आपको कुछ सी कोड फ़ंक्शंस लिखने की आवश्यकता हो सकती है जो ऑब्जेक्ट्स की सरणी में हेरफेर करते हैं और% pythoncode विधियों के अंदर उन कार्यों का उपयोग करते हैं। अगर आपको और विचारों की आवश्यकता है तो मुझे बताएं। – Demolishun

0

आप कहते हैं कि आपने अभी तक पायथन अपवाद फेंकने को लागू नहीं किया है - यही समस्या है। पीईपी 234:

एक नया अपवाद परिभाषित किया गया है, रोकथाम, जिसे पुनरावृत्ति के अंत को सिग्नल करने के लिए उपयोग किया जा सकता है।

आपको अपने पुनरावृत्ति के अंत में यह अपवाद सेट करना होगा। के बाद से अपने कोड ऐसा नहीं करती है, तो आप स्थिति आप का वर्णन किया है में चल रहा है:

  1. दुभाषिया अपनी सूची के कस्टम iternext समारोह के माध्यम से लूप
  2. आपका समारोह सरणी के अंत तक हो जाता है, और StopIteration अपवाद को सही ढंग से सेट करने के बजाय, बस अपना 'जादू नंबर' लौटाता है।
  3. दुभाषिया, इसे फिर से रोकने के लिए कोई अच्छा कारण नहीं देख रहा है, बस iternext ... आपके जादू संख्या द्वारा लौटाए गए मान को प्रिंट करना जारी रखता है। दुभाषिया के लिए, यह अभी तक एक और सूची सदस्य है।

सौभाग्य से, यह एक बहुत ही सरल फिक्स है, हालांकि यह सीधा नहीं लग सकता है, क्योंकि सी में कोई अपवाद सुविधा नहीं है। पायथन सी एपीआई बस एक वैश्विक त्रुटि संकेतक का उपयोग करता है जिसे आपने अपवाद स्थिति उठाए जाने पर सेट किया है, और उसके बाद एपीआई मानकों को निर्देश दिया जाता है कि आप दुभाषिया को ढेर तक सभी तरह से वापस लौटते हैं, जो कि PyErr_Occurred() के आउटपुट को देखने के लिए देखता है या नहीं एक त्रुटि सेट है, और यदि यह है, तो प्रासंगिक अपवाद और ट्रेसबैक प्रिंट करता है। How to create a generator/iterator with the Python C API?

1

मैं के साथ बहुत ही समस्या का सामना करना पड़ा अजगर 2.6, और इसे @hehex उत्तर के लिए धन्यवाद। लेकिन मैं किसी भी जादू मूल्य, या अतिरिक्त बुलियन से बचने के लिए अंत-सूची की स्थिति को पारित करना चाहता था। निश्चित रूप से, मेरे इटरेटर के पास atEnd() विधियां हैं जो मुझे बताती हैं कि मैं सूची का अंत कर चुका हूं।

तो वास्तव में, यह SWIG अपवाद हैंडलिंग के साथ काफी आसान है।

%ignore MyStructIter::atEnd(); 
%except MyStructIter::next { 
    if($self->list->atEnd()) { 
     PyErr_SetString(PyExc_StopIteration,"End of list"); 
     SWIG_fail; 
    } 
    $action 
} 

बिंदु() को पूरी तरह से कॉल एक बार आप सूची के अंत अतीत हैं इस snipet अगले छोड़ देता है: मैं बस निम्नलिखित जादू को जोड़ने के लिए किया था।

%except MyStructIter::next { 
    if($self->pos >= $self->list->len) { 
     PyErr_SetString(PyExc_StopIteration,"End of list"); 
     SWIG_fail; 
    } 
    $action 
} 

नोट के लिए अजगर 3.x:

आप अपने मुहावरे पर कायम हैं, तो यह की तरह दिखना चाहिए

आप अपने अगले() साथ समारोह का नाम होगा जादू "__" उपसर्ग & पोस्टफिक्स नाम। एक विकल्प बस जोड़ने के लिए है:

%rename(__next__) MyStructIter::next; 
संबंधित मुद्दे