2008-09-17 13 views
11

में मैक्रो मैं एक 'foreach' मैक्रो मैं सी में अक्सर उपयोग ++ कि ज्यादातर एसटीएल कंटेनरों के लिए काम करता है:"foreach मूल्यों" जीसीसी और सीपीपी

#define foreach(var, container) \ 
    for(typeof((container).begin()) var = (container).begin(); \ 
     var != (container).end(); \ 
     ++var) 

(ध्यान दें कि 'typeof' एक जीसीसी विस्तार है।) इसका उपयोग इस प्रकार किया जाता है:

std::vector<Blorgus> blorgi = ...; 
foreach(blorgus, blorgi) { 
    blorgus->draw(); 
} 

मैं कुछ ऐसा करना चाहता हूं जो मानचित्र के मूल्यों पर पुनरावृत्त हो। इसे "foreach_value" कहें, शायद। तो लेखन के बजाय

foreach(pair, mymap) { 
    pair->second->foo(); 
} 

मैं लिखते थे

foreach_value(v, mymap) { 
    v.foo(); 
} 

मैं नहीं, ऐसा मैक्रो यह कर देगा के साथ आ सकता है क्योंकि यह दो चर घोषित करने की आवश्यकता है: इटरेटर और मूल्य चर (' वी ', ऊपर)। मैं नहीं जानता कि लूप के प्रारंभकर्ता में, जीसीसी एक्सटेंशन का उपयोग करने के लिए इसे कैसे करें। मैं इसे foreach_value कॉल से ठीक पहले घोषित कर सकता हूं, लेकिन फिर यह उसी दायरे में foreach_value मैक्रो के अन्य उदाहरणों के साथ संघर्ष करेगा। अगर मैं वर्तमान लाइन नंबर को इटरेटर वैरिएबल नाम पर प्रत्ययित कर सकता हूं, तो यह काम करेगा, लेकिन मुझे नहीं पता कि यह कैसे करना है।

उत्तर

4

आप दो लूप का उपयोग करके ऐसा कर सकते हैं। पहले इटरेटर को एक नाम के साथ घोषित करता है जो कंटेनर वैरिएबल का एक फ़ंक्शन है (और यदि आप अपने कोड के साथ संघर्ष के बारे में चिंतित हैं तो आप यह उलझन बना सकते हैं)। दूसरा वैल्यू वैरिएबल घोषित करता है।

#define ci(container) container ## iter 
#define foreach_value(var, container) \ 
    for (typeof((container).begin()) ci(container) = container.begin(); \ 
     ci(container) != container.end();) \ 
     for (typeof(ci(container)->second)* var = &ci(container)->second; \ 
      ci(container) != container.end(); \ 
      (++ci(container) != container.end()) ? \ 
       (var = &ci(container)->second) : var) 

ही पाश समाप्ति हालत का उपयोग करके, बाहरी पाश केवल एक बार होता (और यदि आप भाग्यशाली हैं, दूर अनुकूलित जाता है)। इसके अलावा, यदि आप मानचित्र खाली हैं तो कॉलर से>> इटरेटर पर दूसरा। आंतरिक लूप की वृद्धि में टर्नरी ऑपरेटर के लिए यही कारण है; अंत में, हम अंतिम मूल्य पर var छोड़ देते हैं, क्योंकि इसे फिर से संदर्भित नहीं किया जाएगा।

आप सीआई (कंटेनर) इनलाइन कर सकते हैं, लेकिन मुझे लगता है कि यह मैक्रो को और अधिक पठनीय बनाता है।

+0

बिल्कुल सही! लेकिन मैं एक अतिरिक्त वस्तु बनाने के बारे में थोड़ा समझ में नहीं आता। var एक सूचक है। क्या आपके पास पहले संस्करण था जो एक मूल्य का उपयोग करता था? और var को अभी तक एक और (innermost) लूप के साथ संदर्भ बनाया जा सकता है: के लिए (typeof (...) var = * ptr; ptr; ptr = NULL)। उस के साथ अद्यतन करने के लिए देखभाल? – sfink

0

आप टेम्पलेट क्लास को परिभाषित कर सकते हैं जो टेम्पलेट पैरामीटर के रूप में mymap का प्रकार लेता है, और ओवरलोडिंग * और -> द्वारा मानों पर एक इटरेटर की तरह कार्य करता है।

8

आप BOOST_FOREACH की तलाश करेंगे - उन्होंने आपके लिए पहले से ही सभी काम किए हैं!

आप अपने खुद के रोल करना चाहते हैं, तो आप सी ++ में कहीं भी एक ब्लॉक है, जो itr- के अपने मध्यवर्ती भंडारण के साथ अपने दायरे समस्या का समाधान> दूसरा घोषणा कर सकते हैं ...

// Valid C++ code (which does nothing useful) 
{ 
    int a = 21; // Which could be storage of your value type 
} 
// a out of scope here 
{ 
    int a = 32; // Does not conflict with a above 
} 
+0

लिंक के लिए धन्यवाद; यह मूल के लिए शायद एक अच्छा प्रतिस्थापन है। मुख्य अंतर यह है कि इसे लूप वैरिएबल के प्रकार की घोषणा करने की आवश्यकता होती है, जो कि कभी-कभी ठीक है लेकिन बालों के प्रकार के लिए समस्याग्रस्त है। आपका अद्यतन उत्तर काम नहीं करता है - यह दो चर घोषित करता है। यह संकलित नहीं होगा। – sfink

+0

सच है! आप दो मैप में स्टोर करने के लिए हमेशा एक std :: जोड़ी का उपयोग कर सकते हैं या अपने मैक्रो में स्कोप का उपयोग कर सकते हैं। मैं (के लिए (itr_type आईटीआर = शुरू, VALUE_TYPE वैल = itr-> दूसरा; आईटीआर = अंत; ++ आईटीआर, वैल = itr-> सेकेंड) ) मेरी पोस्ट से उल्लंघनकारी कोड हटा देंगे –

+0

जोड़ी काम नहीं करती है क्योंकि मुझे अभी भी सादे परिवर्तनीय घोषित करने की आवश्यकता है। दायरा काम नहीं करता है क्योंकि मैक्रो में बनाए गए किसी भी दायरे में मैक्रो के लिए स्थानीय नहीं है(), या इसके लिए एक स्कोप बंद करने के लिए एक मिलान foreach_end मैक्रो की आवश्यकता है। (या एक नंगे '}', लेकिन यक!) – sfink

1

आपने सोचा है Boost libraries का उपयोग करने के लिए? उनके पास foreach macro implemented है जो शायद आप जो भी लिखेंगे उससे कहीं अधिक मजबूत है ... और transform_iterator भी है जो आप चाहते हैं कि दूसरे निष्कर्षण भाग करने के लिए इस्तेमाल किया जा सकता है। Boost transform_iterator use casecomp.lang.c++.moderated,:

दुर्भाग्य से मैं आप बिल्कुल नहीं बता सकता है कि कैसे इसका इस्तेमाल करने की वजह से मैं काफी सी ++ पता नहीं है :) This Google search जाता कुछ आशाजनक जवाब।

+0

मुझे लगता है कि आप transform_iterator के बारे में सही हो सकते हैं, लेकिन मुझे यह सुनिश्चित करने के लिए इसके साथ खेलना होगा। (और हाँ, मैं पहले से ही बढ़ावा देने के विभिन्न बिट्स का उपयोग कर रहा हूं, हालांकि मैं जो कुछ भी ऑफर करता हूं उसके केवल एक अंश से परिचित हूं।) – sfink

3

एसटीएल transform फ़ंक्शन कुछ समान करता है।

तर्कों (क्रम में) कर रहे हैं:

  1. एक इनपुट इटरेटर एक कंटेनर
  2. एक इनपुट इटरेटर कंटेनर
  3. एक उत्पादन iterator के अंत को परिभाषित जहां डाल करने के लिए तय किया की शुरुआत तय किया आउटपुट (इन-प्लेस ट्रांसफॉर्म के लिए, प्रत्येक के लिए समान, केवल इनपुट इटरेटर को # 1 में पास करें)
  4. प्रत्येक तत्व पर प्रदर्शन करने के लिए एक यूनरी फ़ंक्शन (फ़ंक्शन ऑब्जेक्ट)

एक बहुत ही सरल उदाहरण के लिए, आप से एक स्ट्रिंग में प्रत्येक चरित्र भुनाने सकता है:

#include <iostream> 
#include <string> 
#include <algorithm> 
#include <cctype> 

int main(int argc, char* argv[]) { 
    std::string s("my lowercase string"); 
    std::transform(s.begin(), s.end(), s.begin(), toupper); 
    std::cout << s << std::endl; // "MY LOWERCASE STRING" 
} 

वैकल्पिक रूप से वहाँ भी accumulate समारोह है, जो कुछ मूल्यों समारोह वस्तु के लिए कॉल के बीच बनाए रखा जा करने की अनुमति देता है। संचय इनपुट कंटेनर में डेटा को संशोधित नहीं करता है जैसा कि को बदलता है।

1

बूस्ट :: For_each अब तक का सबसे अच्छा शर्त है। निफ्टी बात यह है कि वे वास्तव में आपको जो मैक्रो BOOST_FOREACH() है, जिसे आप तब लपेट सकते हैं और # जो भी आप वास्तव में अपने कोड में कॉल करना चाहते हैं उसे परिभाषित करें। अधिकांश लोग अच्छे पुराने "foreach" का चयन करेंगे, लेकिन अन्य दुकानों में अलग-अलग कोडिंग मानदंड हो सकते हैं, इसलिए यह उस मानसिकता के साथ फिट बैठता है। बूस्ट में सी ++ डेवलपर्स के लिए कई अन्य उपहार भी हैं! उपयोग करने के लायक है।

0
#define foreach(var, container) for (typeof((container).begin()) var = (container).begin(); var != (container).end(); ++var) 

सी ++ में कोई टाइप नहीं है ... यह आपके लिए कैसे संकलित है? (यह निश्चित रूप से पोर्टेबल नहीं है)

+0

यही कारण है कि मैंने शीर्षक में "जीसीसी" का उल्लेख किया - यह एक जीसीसी विस्तार है। एक बहुत आसान एक। मानक सी ++ अंततः 'ऑटो' कीवर्ड (या इसके बजाय, उस कीवर्ड के लिए नया अर्थ) प्राप्त करेगा जो कि मैं किस प्रकार का उपयोग करता हूं। लेकिन मैं जीसीसी-आईएसएम को बेहतर तरीके से इंगित करने के लिए प्रश्न अपडेट करूंगा। – sfink

1

मैंने थोड़ी सी Foreach.h बनाया है जो फॉरेच() के कुछ प्रकारों के साथ सहायक है, जिसमें स्थानीय चर और ऑपरेटरों पर चल रहे दोनों शामिल हैं, साथ ही लूप के भीतर से तत्वों को हटाने के खिलाफ सुरक्षित संस्करण भी शामिल है ।

#include <cstdio> 
#include <vector> 
#include "foreach.h" 

int main() 
{ 
    // make int vector and fill it 
    vector<int> k; 
    for (int i=0; i<10; ++i) k.push_back(i); 

    // show what the upper loop filled 
    foreach_ (it, k) printf("%i ",(*it)); 
    printf("\n"); 

    // show all of the data, but get rid of 4 
    // http://en.wikipedia.org/wiki/Tetraphobia :) 
    foreachdel_ (it, k) 
    { 
     if (*it == 4) it=k.erase(it); 
     printf("%i ",(*it)); 
    } 
    printf("\n"); 

    return 0; 
} 

उत्पादन:

0 1 2 3 4 5 6 7 8 9 
0 1 2 3 5 6 7 8 9 

मेरे Foreach.h प्रदान करता है निम्न मैक्रो: तो कोड मेरी मैक्रो का उपयोग करता है अच्छा और इस तरह आरामदायक लग रहा है

  • foreach() - संकेत के लिए नियमित रूप से foreach
  • foreach_() - स्थानीय चर के लिए नियमित foreach
  • foreachdel() - पाश के भीतर हटाने के लिए चेक के foreach संस्करण, सूचक संस्करण
  • foreachdel_() - पाश के भीतर हटाने के लिए चेक के foreach संस्करण, स्थानीय चर संस्करण

वे सुनिश्चित मेरे लिए काम करते हैं, मुझे आशा है कि वे आपके जीवन को थोड़ा आसान बना देंगे :)

+0

foreach के लिए धन्यवाद लेकिन foreachdel मेरे लिए संकलित नहीं किया था: त्रुटि: 'it_bup' को 'बूल' में परिवर्तित नहीं कर सका – stephenmm

1

इस प्रश्न के दो भाग हैं। आपको किसी भी तरह (1) मैप के मूल्य (चाबियाँ नहीं) पर एक इटरेटर (या बल्कि, एक पुनरावृत्त अनुक्रम) उत्पन्न करने की आवश्यकता है, और (2) बहुत सारे बॉयलरप्लेट के बिना पुनरावृत्ति करने के लिए मैक्रो का उपयोग करें।

सबसे अच्छा समाधान भाग (1) और Boost Foreach भाग (2) के लिए Boost RangeAdaptor का उपयोग करना है। आपको मैक्रो लिखने या खुद को इटरेटर को लागू करने की आवश्यकता नहीं है।

#include <map> 
#include <string> 
#include <boost/range/adaptor/map.hpp> 
#include <boost/foreach.hpp> 

int main() 
{ 
    // Sample data 
    std::map<int, std::string> myMap ; 
    myMap[0] = "Zero" ; 
    myMap[10] = "Ten" ; 
    myMap[20] = "Twenty" ; 

    // Loop over map values 
    BOOST_FOREACH(std::string text, myMap | boost::adaptors::map_values) 
    { 
     std::cout << text << " " ; 
    } 
} 
// Output: 
// Zero Ten Twenty 
0

मैं कार्यान्वित मेरे अपने foreach_valueBoostforeach कोड के आधार पर:

#include <boost/preprocessor/cat.hpp> 
#define MUNZEKONZA_FOREACH_IN_MAP_ID(x) BOOST_PP_CAT(x, __LINE__) 

namespace munzekonza { 
namespace foreach_in_map_private { 
inline bool set_false(bool& b) { 
    b = false; 
    return false; 
} 

} 
} 

#define MUNZEKONZA_FOREACH_VALUE(value, map)         \ 
for(auto MUNZEKONZA_FOREACH_IN_MAP_ID(_foreach_in_map_it) = map.begin();  \ 
     MUNZEKONZA_FOREACH_IN_MAP_ID(_foreach_in_map_it) != map.end();)  \ 
for(bool MUNZEKONZA_FOREACH_IN_MAP_ID(_foreach_in_map_continue) = true;  \ 
     MUNZEKONZA_FOREACH_IN_MAP_ID(_foreach_in_map_continue) &&    \ 
     MUNZEKONZA_FOREACH_IN_MAP_ID(_foreach_in_map_it) != map.end();   \ 
     (MUNZEKONZA_FOREACH_IN_MAP_ID(_foreach_in_map_continue)) ?    \ 
     ((void)++MUNZEKONZA_FOREACH_IN_MAP_ID(_foreach_in_map_it)) :   \ 
     (void)0)                \ 
    if(munzekonza::foreach_in_map_private::set_false(       \ 
      MUNZEKONZA_FOREACH_IN_MAP_ID(_foreach_in_map_continue))) {} else \ 
    for(value = MUNZEKONZA_FOREACH_IN_MAP_ID(_foreach_in_map_it)->second;  \ 
     !MUNZEKONZA_FOREACH_IN_MAP_ID(_foreach_in_map_continue);    \ 
     MUNZEKONZA_FOREACH_IN_MAP_ID(_foreach_in_map_continue) = true)   

उदाहरण के लिए, आप इसे अपने कोड में इस तरह का उपयोग कर सकते हैं:

#define MUNZEKONZA_FOREACH_VALUE foreach_value 

std::map<int, std::string> mymap; 
// populate the map ... 

foreach_value(const std::string& value, mymap) { 
    // do something with value 
} 

// change value 
foreach_value(std::string& value, mymap) { 
    value = "hey"; 
} 
0
#define zforeach(var, container) for(auto var = (container).begin(); var != (container).end(); ++var) 
वहाँ

कोई टाइप नहीं है() ताकि आप इसका उपयोग कर सकें:

decltype((container).begin()) var 
decltype(container)::iterator var 
संबंधित मुद्दे