2012-01-26 21 views
5

पृष्ठभूमि: मैं इस खिलौना की समस्या को प्रस्तुत करके निरंतरता/कोरआउट/जेनरेटर (जिसे भी निम्नलिखित कहा जाता है) को कार्यान्वित करने का तरीका जानने का प्रयास कर रहा हूं। पर्यावरण जीसीसी 4.6 और लिनक्स 3.0 x86_64 पर सी ++ 11 है। गैर पोर्टेबल ठीक है लेकिन बाहरी पुस्तकालय का उपयोग (boost.coroutine, COROUTINE, आदि) की अनुमति नहीं है। मुझे लगता है कि longjmp(3) और/या makecontext(2) और मित्र मदद कर सकते हैं लेकिन सुनिश्चित नहीं हैं।सी ++/जीसीसी/लिनक्स में निरंतरता/कोरआउट/जेनरेटर

विवरण:

निम्नलिखित खिलौना पार्सर के रूप में के दृश्यों और समान लंबाई के bs पार्स करने के लिए माना जाता है। यानी

((a+)(b+))+ 

जैसे कि दूसरे ब्रैकेट किए गए उत्पादन की लंबाई तीसरे के बराबर होती है।

जब इसे उत्पादन मिलता है (उदाहरण के लिए aaabbb) यह a एस की संख्या को आउटपुट करता है (उदाहरण 3)।

कोड:

#include <stdlib.h> 
#include <iostream> 
using namespace std; 

const char* s; 

void yield() 
{ 
     // TODO: no data, return from produce 
     abort(); 
} 

void advance() 
{ 
     s++; 
     if (*s == 0) 
       yield(); 
} 

void consume() 
{ 
     while (true) 
     { 
       int i = 0; 

       while (*s == 'a') 
       { 
         i++; 
         advance(); 
       } 

       cout << i << " "; 

       while (i-- > 0) 
       { 
        if (*s != 'b') 
         abort(); 
        advance(); 
       } 
     } 
} 

void produce(const char* s_) 
{ 
     s = s_; 

     // TODO: data available, continue into consume() 
     consume(); 
} 

int main() 
{ 
     produce("aaab"); 
     produce("bba"); 
     produce("baa"); 
     produce("aabbb"); 
     produce("b"); 

     // should print: 3 1 4 

     return 0; 
} 

समस्या:

आप देख सकते हैं जब yield कहा जाता है consume कॉल स्टैक के राज्य सहेजा जाना चाहिए और उसके बाद produce रिटर्न। जब produce को फिर से कॉल किया जाता है, consumeyield से लौटकर पुनरारंभ किया जाना चाहिए। चुनौती produce कॉल consume पर संशोधित करने के लिए होगी, और yield लागू करें ताकि वे इरादे के अनुसार काम कर सकें।

(। जाहिर है तो उपभोग कि यह बचत होती है और अपने राज्य अभ्यास का उद्देश्य धरा पुनर्निर्माण reimplementing)

मुझे लगता है कि क्या किया जाना चाहिए makecontext आदमी पृष्ठ के निचले भाग पर उदाहरण की तरह कुछ है: http://www.kernel.org/doc/man-pages/online/pages/man3/makecontext.3.html , लेकिन यह स्पष्ट नहीं है कि इस समस्या पर इसका अनुवाद कैसे करें। (और मैं नींद की जरूरत)

समाधान:

(धन्यवाद क्रिस के लिए डिजाइन के लिए डोड)

#include <stdlib.h> 
#include <iostream> 
#include <ucontext.h> 
using namespace std; 

const char* s; 
ucontext_t main_context, consume_context; 

void yield() 
{ 
    swapcontext(&consume_context, &main_context); 
} 

void advance() 
{ 
    s++; 
    if (*s == 0) 
      yield(); 
} 

void consume() 
{ 
    while (true) 
    { 
      int i = 0; 

      while (*s == 'a') 
      { 
        i++; 
        advance(); 
      } 

      cout << i << " "; 

      while (i-- > 0) 
      { 
        advance(); 
      } 
    } 
} 

void produce(const char* s_) 
{ 
    s = s_; 

    swapcontext(&main_context, &consume_context); 
} 

int main() 
{ 
    char consume_stack[4096]; 

    getcontext(&consume_context); 
    consume_context.uc_stack.ss_sp = consume_stack; 
    consume_context.uc_stack.ss_size = sizeof(consume_stack); 
    makecontext(&consume_context, consume, 0); 

    produce("aaab"); 
    produce("bba"); 
    produce("baa"); 
    produce("aabbb"); 
    produce("b"); 

    // should print: 3 1 4 

    return 0; 
} 
+0

आपका मतलब है 'longjmp' के बारे में बहुत ज्यादा चिंता की जरूरत नहीं है? मुझे 'लांगजंप' वर्तनी वाले किसी भी फ़ंक्शन से अवगत नहीं है। –

+0

makecontext iirc को बहिष्कृत किया गया है। –

+0

आपको क्यों लगता है कि makecontext को बहिष्कृत किया गया है? यह मैन पेज पर इसके बारे में कुछ भी नहीं कहता है? –

उत्तर

3

इसकी काफी इस के लिए makecontext/swapcontext उपयोग करने के लिए सीधी-सपाट - आप makecontext का उपयोग उनके बीच स्वैप करने के लिए एक नया कोरआउट संदर्भ और swapcontext बनाने के लिए। आपके मामले में, आपको consume अनंत लूप चलाने के लिए एक अतिरिक्त कोरआउटिन की आवश्यकता है, और आप मुख्य संदर्भ में मुख्य भाग लेते हैं और उत्पादन करते हैं।

तो main कि पाश की खपत चलेंगे एक नया संदर्भ बनाने के लिए getcontext + makecontext बुलाना चाहिए:

getcontext(&consume_ctxt); 
// set up stack in consume_context 
makecontext(&consume_ctxt, consume, 0); 

और फिर produceconsume सीधे कॉल करने के बजाय यह करने के लिए स्विच जाएगा:

void produce(const char* s_) 
{ 
    s = s_; 
    swapcontext(&main_ctxt, &consume_ctxt); 
} 

और अंत में yield मुख्य संदर्भ पर वापस जाने के लिए बस swapcontext(&consume_ctxt, &main_ctxt); पर कॉल करता है (जो produce में जारी रहेगा और तुरंत वापस आ जाएगा)।

ध्यान दें कि जब से consume अनंत लूप है, तो आप क्या होता है जब यह रिटर्न (ताकि लिंक का इस्तेमाल कभी नहीं किया जाएगा)

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