2012-02-02 15 views
15

दलील

मैं C++ में कार्य से बचने के लिए कोड पूरी तरह से कोशिश करने के लिए अस्थायी। यही है, मैं केवल प्रारंभिकरण का उपयोग करता हूं और स्थानीय चर को const के रूप में घोषित करता हूं (यानी हमेशा लूप चर या accumulators को छोड़कर)।बाइंड गैर स्थिरांक संदर्भ

अब, मुझे एक ऐसा मामला मिला है जहां यह काम नहीं करता है। मेरा मानना ​​है कि यह एक सामान्य पद्धति है, लेकिन विशेष रूप से यह निम्न स्थिति में पैदा होती है:

समस्या विवरण

चलो कहते हैं कि मैं एक प्रोग्राम है जो एक स्ट्रिंग में एक इनपुट फ़ाइल की सामग्री को लोड करता चला रहे हैं। आप या तो फ़ाइल नाम (tool filename) या मानक इनपुट स्ट्रीम (cat filename | tool) का उपयोग करके टूल को कॉल कर सकते हैं। अब, मैं स्ट्रिंग कैसे शुरू करूं?

निम्नलिखित काम नहीं करता:

bool const use_stdin = argc == 1; 
std::string const input = slurp(use_stdin ? static_cast<std::istream&>(std::cin) 
              : std::ifstream(argv[1])); 

क्यों यह काम नहीं करता है? क्योंकि slurp के प्रोटोटाइप के रूप में निम्नानुसार ही नज़र आना चाहिए:

std::string slurp(std::istream&); 

है, तर्क मैं गैर - const और एक परिणाम के रूप में मैं यह एक अस्थायी करने के लिए बाध्य नहीं कर सकते। एक अलग चर का उपयोग कर इस के आसपास एक रास्ता प्रतीत नहीं होता है।

बदसूरत वर्कअराउंड

पल में, मैं निम्नलिखित समाधान का उपयोग करें:

std::string input; 
if (use_stdin) 
    input = slurp(std::cin); 
else { 
    std::ifstream in(argv[1]); 
    input = slurp(in); 
} 

लेकिन यह मुझे गलत तरीके से रगड़ रहा है। सबसे पहले यह अधिक कोड (एसएलओसी में) है लेकिन यह (यहां) अधिक तार्किक सशर्त अभिव्यक्ति के बजाय if का उपयोग कर रहा है, और यह घोषणा के बाद असाइनमेंट का उपयोग कर रहा है जिसे मैं टालना चाहता हूं।

प्रारंभिकरण की इस अप्रत्यक्ष शैली से बचने का कोई अच्छा तरीका है? समस्या उन सभी मामलों में सामान्यीकृत की जा सकती है जहां आपको एक अस्थायी वस्तु को म्यूट करने की आवश्यकता है। ऐसे मामलों से निपटने के लिए बीमार तरीके से स्ट्रीम नहीं किए गए हैं (const स्ट्रीम का कोई मतलब नहीं है, और फिर भी एक अस्थायी धारा पर काम करना समझ में आता है)?

+0

क्यों 'static_cast' की आवश्यकता है? –

+0

@ एनएम .: कंपाइलर '?:' के माध्यम से नहीं देख सकता है। ':' के दोनों तरफ एक ही प्रकार का होना चाहिए। –

+0

'slurp' क्या कर रहा है? :) –

उत्तर

14

क्यों बस नहीं अधिभार slurp?

std::string slurp(char const* filename) { 
    std::ifstream in(filename); 
    return slurp(in); 
} 

int main(int argc, char* argv[]) { 
    bool const use_stdin = argc == 1; 
    std::string const input = use_stdin ? slurp(std::cin) : slurp(argv[1]); 
} 

यह सशर्त ऑपरेटर के साथ एक सामान्य समाधान है।

+0

+1 एक उत्कृष्ट समाधान। मैं वर्तमान में पाइथन में इसका बहुत उपयोग कर रहा हूं, लेकिन उत्सुकता से पर्याप्त है, यह सी ++ में ऐसा करने के लिए मेरे पास नहीं हुआ। सादगी के लिए –

+0

+1 और सीधा होने के लिए +1। – vhallac

11

if साथ समाधान मानक समाधान जब argv के साथ काम कर और अधिक या कम है:

if (argc == 1) { 
    process(std::cin); 
} else { 
    for (int i = 1; i != argc; ++ i) { 
     std::ifstream in(argv[i]); 
     if (in.is_open()) { 
      process(in); 
     } else { 
      std::cerr << "cannot open " << argv[i] << std::endl; 
    } 
} 

यह अपने मामले को संभाल नहीं करता है, हालांकि, के बाद से अपने प्राथमिक चिंता का विषय एक स्ट्रिंग प्राप्त के लिए है, फ़ाइल नाम तर्कों को "संसाधित" नहीं करना है।

मेरे अपने कोड में, मैं एक MultiFileInputStream कि मैं लिखा है, जो निर्माता में फ़ाइल नामों की एक सूची लेता है का उपयोग करें, और केवल EOF लौटाता है जब पिछले पढ़ कर दिया गया है यदि सूची खाली है, इसे पढ़ता std::cin

MultiFileInputStream in(
     std::vector<std::string>(argv + 1, argv + argc)); 
std::string const input = slurp(in); 

इस वर्ग यह आम तौर पर उपयोगी है अगर आप अक्सर लिखने यूनिक्स की तरह उपयोगिता कार्यक्रम के रूप में, लेखन के लायक है: यह आपकी समस्या के लिए एक सुंदर और सरल समाधान प्रदान करता है। यह निश्चित रूप से मामूली नहीं है, हालांकि, और यदि यह एक बार की आवश्यकता है तो बहुत काम हो सकता है।

एक अधिक सामान्य समाधान तथ्य पर आधारित है कि आप एक अस्थायी पर एक गैर स्थिरांक सदस्य समारोह, और यह तथ्य कॉल कर सकते हैं std::istream वापसी एक std::istream& — एक गैर स्थिरांक-संदर्भ के सदस्य कार्यों के सबसे कि जो तब एक गैर कॉन्स्ट्रेंस संदर्भ से बंधेगा। तो आप हमेशा की तरह कुछ लिख सकते हैं:

std::string const input = slurp(
      use_stdin 
      ? std::cin.ignore(0) 
      : std::ifstream(argv[1]).ignore(0)); 

मैं इस हैक का एक सा विचार करूँगा, तथापि, और इसे और अधिक सामान्य समस्या यह है कि आप चाहे खुला जांच नहीं कर सकता (द्वारा कहा जाता है std::ifstream के निर्माता काम किया।

आम तौर पर, हालांकि मैं समझता हूँ कि आप क्या हासिल करने की कोशिश कर रहे हैं, मुझे लगता है कि आप पाएंगे कि आईओ लगभग हमेशा एक अपवाद का प्रतिनिधित्व करेंगी। आप एक int नहीं पढ़ सकते हैं बिना इसे पहले परिभाषित किया गया है, और आप पढ़ नहीं सकते हैं पहले std::string परिभाषित किए बिना ine। मैं से सहमत हूं कि यह उतना ही सुरुचिपूर्ण नहीं है जितना हो सकता है, लेकिन फिर, कोड जो सही ढंग से हैंडल त्रुटियों को शायद ही कभी सुरुचिपूर्ण के रूप में सुरुचिपूर्ण हो। (एक समाधान यहाँ std::ifstream से प्राप्त करने के लिए एक अपवाद है, तो खुला काम नहीं किया फेंकने के लिए हो सकता है;। आप सभी की जरूरत होगी एक निर्माता जो is_open() निर्माता शरीर में के लिए जाँच की है)

+0

+1 मुझे 'MultiFileInputStream' समाधान सबसे अच्छा लगता है। यदि धाराएं एपीआई आपकी समस्याओं का समाधान नहीं करती है, तो शीर्ष पर एक शिम जोड़ें। :) – vhallac

+0

स्ट्रीम एपीआई समस्या को हल करता है। आपको केवल कार्यान्वयन का विस्तार करने की आवश्यकता है। ('मल्टीफाइल इनपुटस्ट्रीम' 'std :: istream' से प्राप्त होता है। Iostreams को विस्तार से विस्तारित किया गया था, और मैं उस एप्लिकेशन के बारे में नहीं सोच सकता जहां हमारे पास कम से कम एक कस्टम 'स्ट्रीमबफ' और कस्टम मैनिपुलेटर्स की संख्या नहीं थी।) –

3

सभी एसएसए-स्टाइल भाषाओं में वास्तविक रूप से उपयोग करने योग्य होने के लिए फाई नोड्स की आवश्यकता होती है। आप किसी भी मामले में एक ही समस्या में भाग लेंगे जहां आपको शर्त के मूल्य के आधार पर दो अलग-अलग प्रकारों से निर्माण करने की आवश्यकता है। टर्नरी ऑपरेटर ऐसे मामलों को संभाल नहीं सकता है। बेशक, सी ++ 11 में अन्य चालें हैं, जैसे स्ट्रीम को स्थानांतरित करना या लैम्ब्डा का उपयोग करना, और IOstreams का डिज़ाइन वस्तुतः सटीक एंटीथेसिस है जो आप करने की कोशिश कर रहे हैं, इसलिए मेरी राय में, बस एक अपवाद करना होगा।

std::istream&& is = argc==1? std::move(cin) : std::ifstream(argv[1]); 
std::string const input = slurp(is); 

तथ्य यह है कि नामित rvalue संदर्भ lvalues ​​हैं का लाभ उठाते हुए:

+0

धन्यवाद, मुझे इस सामान्य समस्या का नाम नहीं पता था (जाहिर है मुझे ड्रैगन बुक को फिर से पढ़ना होगा)। Iostreams के लिए एक फाई फ़ंक्शन वास्तव में मुझे चाहिए, और चलना एक उचित समाधान हो सकता है। बहुत बढ़िया, कुछ दिलचस्प सीखा। –

1

एक अन्य विकल्प धारा धारण करने के लिए एक मध्यवर्ती चर हो सकता है।

+0

मैं अपने सिर को लपेट नहीं सकता कि यह कानूनी क्यों है। यह कैसे सुनिश्चित करता है कि 'ifstream' के विनाशक को दायरे के अंत में बुलाया जाता है? –

+0

@ कोनराड: क्या आप अस्थायी बाध्य करते समय संदर्भ-से-आधार नियमों से परिचित हैं? वे यहां आवेदन करते हैं। अस्थायी 'ifstream' ऑब्जेक्ट का जीवनकाल उतना ही विस्तारित होता है जैसे कि यह' std :: istream const 'के लिए बाध्य था, और जब विनाश का दायरा समाप्त हो जाता है तो विनाशक को बुलाया जाता है। यहां एक रावल्यू रेफरी का उपयोग करने का लाभ यह है कि आप ऑब्जेक्ट को संशोधित कर सकते हैं। – Xeo

+0

मैं उससे परिचित हूं। लेकिन अगर यह यहां लागू होता है तो क्या आपके कोड में 'is'' होने पर 'std :: cin' के विनाशक को दो बार बुलाया जाएगा? मेरा मतलब है, आम तौर पर यह नहीं बल्कि सशर्त अभिव्यक्ति का प्रकार क्या है, और यह संकलक के निर्णय को कैसे प्रभावित करता है कि ऑब्जेक्ट के जीवनकाल को 'है' दायरे में बांधना है या नहीं? –

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