C ++ के IOStreams को किसने आर्किटेक्चर / डिज़ाइन किया है, और क्या इसे आज के मानकों द्वारा अच्छी तरह से डिज़ाइन किया जाएगा? [बन्द है]


127

सबसे पहले, यह लग सकता है कि मैं व्यक्तिपरक राय के लिए पूछ रहा हूं, लेकिन ऐसा नहीं है कि मैं इसके बाद क्या कर रहा हूं। मैं इस विषय पर कुछ अच्छी तरह से जमी हुई दलीलें सुनना पसंद करूँगा।


आधुनिक धाराओं / क्रमांकन ढांचे को कैसे डिजाइन किया जाना चाहिए, इस बारे में कुछ अंतर्दृष्टि प्राप्त करने की उम्मीद में, मैंने हाल ही में खुद को एंजेलिका लैंगर और क्लाउस क्रेफ़ द्वारा लिखी गई किताब स्टैंडर्ड सी ++ आईओएसट्रेस और लोकल्स की एक प्रति मिली है । मुझे लगा कि अगर IOStreams अच्छी तरह से डिज़ाइन नहीं किया गया है, तो यह पहली जगह में C ++ मानक पुस्तकालय में नहीं बना होगा।

इस पुस्तक के विभिन्न भागों को पढ़ने के बाद, मुझे संदेह होने लगा है कि क्या IOStreams की तुलना एसटीएल से कर सकते हैं, समग्र वास्तु-दृष्टि से। अलेक्जेंडर स्टेपानोव (एसटीएल के "आविष्कारक") के साथ इस साक्षात्कार को पढ़ें जैसे कि एसटीएल में गए कुछ डिज़ाइन निर्णयों के बारे में जानने के लिए।

क्या विशेष रूप से मुझे आश्चर्य :

  • यह अज्ञात प्रतीत होता है कि IOStreams के समग्र डिजाइन के लिए कौन जिम्मेदार था (मैं इस बारे में कुछ पृष्ठभूमि जानकारी पढ़ना पसंद करूंगा - क्या मुझे अच्छे संसाधन पता हैं?)

  • एक बार जब आप IOStreams, जैसे की तत्काल सतह के नीचे गड्ढा अगर आप अपने खुद के वर्गों के साथ IOStreams का विस्तार करना चाहते हैं, तो आप काफी गुप्त और भ्रमित सदस्य फ़ंक्शन नाम, जैसे के साथ एक इंटरफेस करने के लिए मिल getloc/ imbue, uflow/ underflow, snextc/ sbumpc/ sgetc/ sgetn, pbase/ pptr/ epptr(और वहाँ शायद इससे भी बदतर उदाहरण)। इससे समग्र डिजाइन और एकल भागों का सह-संचालन कैसे होता है, इसे समझना बहुत कठिन हो जाता है। यहां तक ​​कि मैंने जिस पुस्तक का ऊपर उल्लेख किया है, वह भी उस (IMHO) की बहुत मदद नहीं करती है ।


इस प्रकार मेरा प्रश्न:

यदि आपको आज के सॉफ्टवेयर इंजीनियरिंग मानकों (यदि वास्तव में इन पर कोई सामान्य समझौता है) द्वारा न्याय करना है, तो क्या C ++ के IOStreams को अभी भी अच्छी तरह से डिजाइन किया जाएगा? (मैं अपने सॉफ़्टवेयर डिज़ाइन कौशल को उस चीज़ से नहीं सुधारना चाहूंगा जिसे आमतौर पर पुराना माना जाता है।)


7
हर्ब Sutter की राय दिलचस्प stackoverflow.com/questions/2485963/... :) बहुत बुरा है कि पुरुष की भागीदारी के एसओ के बाद केवल कुछ ही दिन बचे हैं
Johannes Schaub - litb

5
क्या कोई और है जो एसटीएल धाराओं में चिंताओं का मिश्रण देखता है? एक स्ट्रीम आम तौर पर बाइट्स पढ़ने या लिखने के लिए डिज़ाइन की जाती है और कुछ नहीं। एक चीज जो विशिष्ट डेटा प्रकारों को पढ़ या लिख ​​सकती है, वह एक फ़ॉर्मेटर है (जो कि स्वरूपित बाइट्स को पढ़ने / लिखने के लिए एक धारा का उपयोग करने की आवश्यकता नहीं है)। दोनों को एक कक्षा में मिलाने से अपनी धाराओं को लागू करने के लिए और भी अधिक जटिल हो जाता है।
mmmmmmmm

4
@ जल्दी, उन चिंताओं का एक अलगाव है। std::streambufबाइट्स को पढ़ने और लिखने के लिए बेस-क्लास है, और istream/ ostreamमें फॉर्मैट इन और आउटपुट के लिए है, एक पॉइंटर को std::streambufअपने गंतव्य / स्रोत के रूप में ले रहा है ।
जोहान्स शाउब -

1
@litb: लेकिन क्या स्ट्रीम (फॉर्मैटर) द्वारा उपयोग की जाने वाली स्ट्रीमबॉफ़ को स्विच करना संभव है? तो शायद मैं एसटीएल प्रारूपण का उपयोग करना चाहता हूं, लेकिन एक विशिष्ट स्ट्रीमब्यूफ के माध्यम से डेटा लिखना चाहता हूं?
mmmmmmmm

2
@ स्टीव्सन,ostream foo(&somebuffer); foo << "huh"; foo.rdbuf(cout.rdbuf()); foo << "see me!";
जोहान्स

जवाबों:


31

कई बीमार कल्पना विचारों मानक में अपना रास्ता मिल गया: auto_ptr, vector<bool>, valarrayऔर export, बस कुछ ही नाम है। इसलिए मैं गुणवत्ता डिजाइन के संकेत के रूप में आईओएसट्रीम की उपस्थिति को जरूरी नहीं समझूंगा।

IOStreams का एक चेकर इतिहास है। वे वास्तव में एक पुराने स्ट्रीम लाइब्रेरी का एक पुनर्मूल्यांकन हैं, लेकिन ऐसे समय में लेखक थे जब आज के सी ++ मुहावरों में से कई मौजूद नहीं थे, इसलिए डिजाइनरों को दृष्टि का लाभ नहीं था। एक मुद्दा जो केवल समय के साथ स्पष्ट हो गया था कि आभासी कार्यों के प्रचुर उपयोग और यहां तक ​​कि बेहतरीन ग्रैन्युलैरिटी में आंतरिक बफर ऑब्जेक्ट्स को अग्रेषित करने के कारण IOStreams को कुशलतापूर्वक C के stdio के रूप में कार्यान्वित करना लगभग असंभव है, और कुछ असंवेदनशील अजनबियों के लिए भी धन्यवाद। जिस तरह से स्थानों को परिभाषित और कार्यान्वित किया जाता है। मेरी यह याद काफी धुंधली है, मैं मानता हूँ; मुझे याद है कि यह कुछ साल पहले गहन बहस का विषय था, comp.lang.c ++ पर।


3
आपके इनपुट के लिए धन्यवाद। comp.lang.c++.moderatedअगर मुझे कुछ मूल्यवान लगता है, तो मैं अपने प्रश्न के निचले भाग में संग्रह और लिंक ब्राउज़ करूँगा । - इसके अलावा, मैं आपसे असहमत हूं auto_ptr: हर्ब सटर के असाधारण C ++ को पढ़ने के बाद यह RAII पैटर्न को लागू करते समय एक बहुत ही उपयोगी वर्ग की तरह लगता है।
stakx - अब

5
@stakx: फिर भी यह unique_ptrस्पष्ट और अधिक शक्तिशाली शब्दार्थ के साथ पदावनत और अलौकिक हो रहा है ।
अंकलबींस

3
@UncleBens unique_ptrको संदर्भ के संदर्भ में आवश्यकता होती है। तो इस बिंदु auto_ptrपर बहुत शक्तिशाली सूचक है।
अरेटम

7
लेकिन auto_ptrकॉपी / असाइनमेंट शब्दार्थों को खराब कर दिया है जो इसे डेरेफेरिंग कीड़े के लिए एक जगह बनाते हैं ...
मैथ्यू एम।

5
@TokenMacGuy: यह एक वेक्टर नहीं है, और यह बूलों को संग्रहीत नहीं करता है। जो इसे कुछ भ्रामक बनाता है। ;)
जुलफ

40

उन्हें डिजाइन करने के बारे में, मूल पुस्तकालय ब्रेज़ेन स्ट्रॉस्ट्रुप द्वारा बनाया गया था (आश्चर्यजनक रूप से नहीं), और फिर डेव प्रेस्टोटॉप द्वारा इसे फिर से लागू किया गया था। एंड्रयू कोएनिग से मैनिपुलेटर्स के विचार का उपयोग करते हुए, इसे फिर से जैरी श्वार्ज द्वारा Cfront 2.0 के लिए फिर से डिज़ाइन किया गया और फिर से लागू किया गया। पुस्तकालय का मानक संस्करण इस कार्यान्वयन पर आधारित है।

स्रोत "सी ++ का डिजाइन और विकास", खंड 8.3.1।


3
@ नील - अखरोट डिजाइन के बारे में आपकी क्या राय है? आपके अन्य उत्तरों के आधार पर, बहुत से लोग आपकी राय सुनना पसंद करेंगे ...
DVK

1
@ डीवीडीके ने एक अलग उत्तर के रूप में मेरी राय पोस्ट की।

2
बस ब्रेज़ेन स्ट्रॉस्ट्रुप के साथ एक साक्षात्कार का एक प्रतिलेख मिला, जहां उन्होंने IOStreams इतिहास के कुछ बिट्स और टुकड़ों का उल्लेख किया है: www2.research.att.com/~bs/01chinese.html (यह लिंक अभी अस्थायी रूप से टूटा हुआ लगता है, लेकिन आप कोशिश कर सकते हैं Google का पृष्ठ कैश)
stakx - अब

2
अपडेटेड लिंक: stroustrup.com/01chinese.html
फ्रैंकएचबी

28

यदि आपको आज के सॉफ्टवेयर इंजीनियरिंग मानकों (यदि वास्तव में इन पर कोई सामान्य समझौता है) द्वारा न्याय करना है, तो क्या C ++ के IOStreams को अभी भी अच्छी तरह से डिजाइन किया जाएगा? (मैं अपने सॉफ़्टवेयर डिज़ाइन कौशल को उस चीज़ से नहीं सुधारना चाहूंगा जिसे आमतौर पर पुराना माना जाता है।)

मैं कहूंगा कि कोई , कई कारणों से:

गरीब त्रुटि से निपटने

त्रुटि शर्तों को अपवादों के साथ सूचित किया जाना चाहिए, साथ नहीं operator void*

"ज़ॉम्बी ऑब्जेक्ट" एंटी-पैटर्न है जो इन जैसे कीड़े का कारण बनता है ।

स्वरूपण और I / O के बीच खराब अलगाव

यह स्ट्रीम ऑब्जेक्ट्स को अनावश्यक रूप से जटिल बनाता है, क्योंकि उन्हें स्वरूपण के लिए अतिरिक्त राज्य की जानकारी होती है, चाहे आपको इसकी आवश्यकता हो या नहीं।

यह भी कीड़े की तरह लेखन की बाधाओं को बढ़ाता है:

using namespace std; // I'm lazy.
cout << hex << setw(8) << setfill('0') << x << endl;
// Oops!  Forgot to set the stream back to decimal mode.

यदि इसके बजाय, आपने कुछ ऐसा लिखा है:

cout << pad(to_hex(x), 8, '0') << endl;

कोई प्रारूपण-संबंधित स्थिति बिट्स और कोई समस्या नहीं होगी।

ध्यान दें कि जावा, सी #, और पायथन जैसी "आधुनिक" भाषाओं में, सभी वस्तुओं में एक toString/ ToString/ __str__फ़ंक्शन होता है जिसे I / O रूटीन द्वारा बुलाया जाता है। AFAIK, केवल C ++ इसे दूसरे तरीके stringstreamसे स्ट्रिंग में परिवर्तित करने के मानक तरीके के रूप में उपयोग करता है।

I18n के लिए खराब समर्थन

Iostream- आधारित आउटपुट टुकड़ों में स्ट्रिंग शाब्दिक विभाजित करता है।

cout << "My name is " << name << " and I am " << occupation << " from " << hometown << endl;

प्रारूप के तार पूरे वाक्यों को स्ट्रिंग लिटरल में डालते हैं।

printf("My name is %s and I am %s from %s.\n", name, occupation, hometown);

उत्तरार्द्ध दृष्टिकोण जीएनयू गेटटेक्स्ट जैसे अंतर्राष्ट्रीयकरण पुस्तकालयों के लिए अनुकूल करना आसान है, क्योंकि पूरे वाक्यों का उपयोग अनुवादकों के लिए अधिक संदर्भ प्रदान करता है। यदि आपका स्ट्रिंग फॉर्मेटिंग रूटीन री-ऑर्डरिंग (जैसे POSIX $प्रिंटफ पैरामीटर) का समर्थन करता है , तो यह भाषाओं के बीच शब्द क्रम में अंतर को भी बेहतर ढंग से संभालता है।


4
दरअसल, i18n के लिए, प्रतिस्थापन को पदों (% 1,% 2, ..) से पहचाना जाना चाहिए, क्योंकि अनुवाद के लिए पैरामीटर ऑर्डर को बदलना पड़ सकता है। अन्यथा, मैं पूरी तरह से सहमत हूं - +1।
पीटरचेन

4
@peterchen: यह है कि POSIX के लिए $निर्दिष्ट printfकर रहे हैं।
जेम्सडलिन

2
समस्या स्वरूपण स्ट्रिंग्स नहीं है, यह है कि C ++ में गैर-प्रकार के वैरैग हैं।
dan04

5
C ++ 11 के रूप में अब इसमें टाइप्स प्रकार हैं।
Mooing Duck

2
IMHO 'अतिरिक्त राज्य सूचना' सबसे खराब मुद्दा है। cout एक वैश्विक है; इसके लिए फ़्लैगिंग फ़्लैग को संलग्न करना उन झंडों को वैश्विक बनाता है, और जब आप विचार करते हैं कि उनमें से अधिकांश उपयोगों में कुछ पंक्तियों का एक इच्छित दायरा है, तो यह बहुत भयानक है। एक 'फॉर्मेटर' वर्ग के साथ इसे ठीक करना संभव होगा, जो एक धारा में बंधता है लेकिन अपनी खुद की स्थिति रखता है। और,
कॉट के

17

मैं इसे एक अलग उत्तर के रूप में पोस्ट कर रहा हूं क्योंकि यह शुद्ध राय है।

इनपुट और आउटपुट (विशेष रूप से इनपुट) का प्रदर्शन करना एक बहुत ही कठिन समस्या है, इसलिए आश्चर्यजनक रूप से iostreams लाइब्रेरी बॉड्स और उन चीजों से भरी नहीं है जो कि सही हेंडसाइट के साथ बेहतर हो सकती हैं। लेकिन यह मुझे लगता है कि सभी I / O पुस्तकालय, जो भी इस भाषा में हैं। मैंने कभी भी एक प्रोग्रामिंग भाषा का उपयोग नहीं किया है, जहां I / O सिस्टम सौंदर्य की चीज थी जिसने मुझे इसके डिजाइनर के खौफ में खड़ा कर दिया। आईओट्रीम लाइब्रेरी के फायदे हैं, विशेष रूप से CI / O लाइब्रेरी (एक्स्टेंसिबिलिटी, टाइप-सेफ्टी आदि) पर, लेकिन मुझे नहीं लगता कि कोई भी इसे महान OO या जेनेरिक डिज़ाइन के उदाहरण के रूप में धारण कर रहा है।


16

C ++ आईओस्ट्रीम की मेरी राय में समय के साथ काफी सुधार हुआ है, खासकर जब मैंने अपनी स्ट्रीम कक्षाओं को लागू करके वास्तव में उनका विस्तार करना शुरू किया। मैं हास्यास्पद रूप से खराब सदस्य फ़ंक्शन नामों जैसे xsputnया जो भी हो , के बावजूद एक्स्टेंसिबिलिटी और समग्र डिजाइन की सराहना करने लगा । भले ही, मुझे लगता है कि I / O स्ट्रीम C stdio.h पर एक बड़ा सुधार है, जिसमें कोई प्रकार की सुरक्षा नहीं है और प्रमुख सुरक्षा दोषों से ग्रस्त है।

मुझे लगता है कि IO धाराओं के साथ मुख्य समस्या यह है कि वे दो संबंधित लेकिन कुछ हद तक रूढ़िवादी अवधारणाओं का सामना करते हैं: शाब्दिक स्वरूपण और क्रमबद्धता। एक ओर, IO धाराओं को एक मानव-पठनीय, स्वरूपित एक वस्तु के पाठात्मक निरूपण और दूसरी ओर, किसी वस्तु को पोर्टेबल स्वरूप में क्रमबद्ध करने के लिए तैयार किया जाता है। कभी-कभी ये दो लक्ष्य एक और एक ही होते हैं, लेकिन दूसरी बार यह कुछ गंभीर रूप से कष्टप्रद असंगतियों के परिणामस्वरूप होता है। उदाहरण के लिए:

std::stringstream ss;
std::string output_string = "Hello world";
ss << output_string;

...

std::string input_string;
ss >> input_string;
std::cout << input_string;

यहां, इनपुट के रूप में हमें जो मिलता है, वह वह नहीं है जो हम मूल रूप से स्ट्रीम में आउटपुट करते हैं। ऐसा इसलिए है क्योंकि <<ऑपरेटर पूरे स्ट्रिंग को आउटपुट करता है, जबकि >>ऑपरेटर केवल स्ट्रीम से पढ़ेगा जब तक कि यह एक व्हाट्सएप कैरेक्टर का सामना नहीं करता है, क्योंकि स्ट्रीम में कोई लंबाई की जानकारी संग्रहीत नहीं है। भले ही हम "हैलो वर्ल्ड" युक्त एक स्ट्रिंग ऑब्जेक्ट आउटपुट करते हैं, हम केवल "हेल्लो" युक्त स्ट्रिंग ऑब्जेक्ट इनपुट करने जा रहे हैं। इसलिए जब धारा ने एक प्रारूपण सुविधा के रूप में अपने उद्देश्य की सेवा की है, यह ऑब्जेक्ट को ठीक से क्रमबद्ध करने और फिर उसे अनसुना करने में विफल रहा है।

आप कह सकते हैं कि IO धाराएँ क्रमबद्धता की सुविधाओं के लिए डिज़ाइन नहीं की गई थीं, लेकिन यदि ऐसा है, तो वास्तव में इनपुट स्ट्रीम क्या हैं ? इसके अलावा, व्यवहार में I / O धाराओं का उपयोग अक्सर वस्तुओं को अनुक्रमित करने के लिए किया जाता है, क्योंकि कोई अन्य मानक क्रमांकन सुविधाएं नहीं हैं। विचार करें boost::date_timeया boost::numeric::ublas::matrix, जहां यदि आप <<ऑपरेटर के साथ मैट्रिक्स ऑब्जेक्ट का उत्पादन करते हैं, तो >>ऑपरेटर द्वारा उपयोग करने पर आपको उसी सटीक मैट्रिक्स मिलेगा । लेकिन इसे पूरा करने के लिए, बूस्ट डिजाइनरों को आउटपुट में पाठ्य डेटा के रूप में कॉलम की गिनती और पंक्ति गणना की जानकारी संग्रहीत करनी थी, जो वास्तविक मानव-पठनीय प्रदर्शन से समझौता करता है। फिर से, टेक्स्ट स्वरूपण सुविधाओं और क्रमांकन का एक अजीब संयोजन।

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

मेरी राय में, सबसे अच्छा समाधान मानक चरित्र आधारित धाराओं के साथ-साथ बाइट आधारित धाराओं को पेश करना होगा । ये धाराएं बाइनरी डेटा पर काम करेंगी, जिसमें मानव-पठनीय प्रारूपण / प्रदर्शन की कोई चिंता नहीं होगी। इन्हें सी + ऑब्जेक्ट्स को पोर्टेबल बाइट अनुक्रमों में अनुवाद करने के लिए पूरी तरह से क्रमबद्धता / डिसेरिएलाइज़ेशन सुविधाओं के रूप में इस्तेमाल किया जा सकता है।


जवाब के लिए धन्यवाद। मैं इस बारे में अच्छी तरह से गलत हो सकता है, लेकिन अपने अंतिम बिंदु (बाइट-आधारित बनाम चरित्र-आधारित धाराओं) के विषय में, स्ट्रीम बफ़र्स (चरित्र रूपांतरण, परिवहन और बफ़रिंग) के बीच अलगाव के लिए IOStream का (आंशिक?) उत्तर नहीं है। और धाराएँ (स्वरूपण / पार्सिंग)? और क्या आप नई स्ट्रीम क्लासेस नहीं बना सकते हैं, जो केवल (मशीन से पढ़े जाने वाले) क्रमबद्ध और डीसर्विलाइज़िंग के लिए होती हैं और अन्य जो विशिष्ट रूप से (मानव-पठनीय) स्वरूपण और पार्सिंग के लिए तैयार हैं?
stakx -

@stakx, हाँ, और वास्तव में, मैंने ऐसा किया है। यह लगता है की तुलना में थोड़ा अधिक कष्टप्रद है, क्योंकि std::char_traitsएक लेने के लिए विशेष रूप से विशेष नहीं किया जा सकता है unsigned char। हालाँकि, वर्कअराउंड हैं, इसलिए मुझे लगता है कि एक बार फिर से बचाव की संभावना बढ़ गई है। लेकिन मुझे लगता है कि तथ्य यह है कि बाइट-आधारित स्ट्रीम मानक नहीं हैं, लाइब्रेरी की कमजोरी है।
चार्ल्स साल्विया

4
इसके अलावा, द्विआधारी धाराओं को लागू करने के लिए आपको नई स्ट्रीम कक्षाएं और नए बफर कक्षाएं लागू करने की आवश्यकता होती है , क्योंकि स्वरूपण संबंधी चिंताओं को पूरी तरह से अलग नहीं किया जाता है std::streambuf। तो, मूल रूप से केवल एक चीज जो आप बढ़ा रहे हैं वह है std::basic_iosक्लास। तो एक ऐसी रेखा है जहाँ "विस्तार" से "पूरी तरह से लागू होने वाले" क्षेत्र में पार हो जाता है, और C ++ I / O स्ट्रीम सुविधाओं से एक बाइनरी स्ट्रीम बनाने से उस बिंदु पर संपर्क होता है।
चार्ल्स साल्विया

अच्छी तरह से कहा और वास्तव में मुझे क्या संदेह था। और तथ्य यह है कि सी और सी ++ दोनों विशिष्ट बिट चौड़ाई के बारे में गारंटी नहीं देने के लिए महान लंबाई तक जाते हैं और जब मैं / ओ करने की बात आती है तो प्रतिनिधित्व वास्तव में समस्याग्रस्त हो सकते हैं।
stakx - अब

" किसी ऑब्जेक्ट को पोर्टेबल फॉर्मेट में क्रमबद्ध करने के लिए। " नहीं, वे कभी भी इस बात का समर्थन नहीं करते थे कि
क्यूरियस गुगली

11

मुझे हमेशा C ++ IOStreams बीमार-डिज़ाइन मिला: उनका कार्यान्वयन एक नए प्रकार की धारा को ठीक से परिभाषित करना बहुत कठिन बनाता है। वे io सुविधाओं और स्वरूपण सुविधाओं को भी मिलाते हैं (मैनिपुलेटर्स के बारे में सोचें)।

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

काश सी ++ धाराओं के संबंध में उतना ही सरल था ...


मैंने जिस पुस्तक का उल्लेख किया है वह मूल IOStreams आर्किटेक्चर को निम्न प्रकार से समझाती है: एक ट्रांसपोर्ट लेयर (स्ट्रीम बफर क्लास) और एक पार्सिंग / फॉर्मेटिंग लेयर (स्ट्रीम क्लास) है। पूर्व / एक बाइटस्ट्रीम के पात्रों को पढ़ने / लिखने के लिए जिम्मेदार हैं, जबकि उत्तरार्द्ध पात्रों को पार्स करने या मूल्यों को क्रमबद्ध करने के लिए जिम्मेदार हैं। यह पर्याप्त स्पष्ट लगता है, लेकिन मुझे यकीन नहीं है कि अगर ये चिंताएं वास्तव में स्पष्ट रूप से अलग हो जाती हैं, तो जासूसी। जब प्ले में स्थान आते हैं। - मैं नई धाराओं को लागू करने की कठिनाई पर भी आपसे सहमत हूं।
stakx - अब

"मिक्स io फीचर्स एंड फॉर्मेटिंग फीचर्स" <- इसमें क्या गलत है? यह पुस्तकालय के बिंदु की तरह है। नई धाराएँ बनाने के बारे में, आपको एक स्ट्रीम के बजाय एक स्ट्रीमब्यूफ़ बनाना चाहिए और स्ट्रीमब्यू के चारों ओर एक सादे स्ट्रीम का निर्माण करना चाहिए।
बिली ओनली

ऐसा लगता है कि इस सवाल के जवाब ने मुझे कुछ समझा दिया जो मुझे कभी नहीं समझाया गया था: मुझे एक धारा के बजाय एक
स्ट्रीमब्यूफ़

@stakx: यदि स्ट्रीमब्यूफ़ लेयर ने वही किया जो आपने कहा था, यह ठीक होगा। लेकिन चरित्र अनुक्रम और बाइट के बीच रूपांतरण सभी को वास्तविक I / O (फ़ाइल, कंसोल, आदि) के साथ मिलाया जाता है। चरित्र रूपांतरण किए बिना फ़ाइल I / O करने का कोई तरीका नहीं है, जो बहुत दुर्भाग्यपूर्ण है।
बेन वोइग्ट

10

मुझे लगता है कि IOStreams डिजाइन विस्तार और उपयोगिता के मामले में शानदार है।

  1. स्ट्रीम बफ़र्स: बूस्ट पर एक नज़र डालें। पोस्ट एक्सटेंशन: कुछ लाइनों में gzip, टी, कॉपी स्ट्रीम बनाएँ, विशेष फ़िल्टर और इतने पर बनाएँ। इसके बिना यह संभव नहीं होगा।
  2. स्थानीयकरण एकीकरण और स्वरूपण एकीकरण। देखें क्या किया जा सकता है:

    std::cout << as::spellout << 100 << std::endl;

    प्रिंट कर सकते हैं: "एक सौ" या यहां तक ​​कि:

    std::cout << translate("Good morning")  << std::endl;

    स्थानीय निकाय के अनुसार "बोनजोर" या "बेंट्रोन" को प्रिंट कर सकते हैं std::cout!

    इस तरह की चीजें सिर्फ इसलिए की जा सकती हैं क्योंकि iostreams बहुत लचीली होती हैं।

क्या यह बेहतर किया जा सकता है?

बेशक यह हो सकता है! वास्तव में कई चीजें हैं जो बेहतर हो सकती हैं ...

आज यह सही ढंग से प्राप्त करने के लिए काफी दर्दनाक है stream_buffer, अतिरिक्त स्वरूपण जानकारी को स्ट्रीम में जोड़ना गैर-तुच्छ है, लेकिन संभव है।

लेकिन कई साल पहले देख रहा था कि मैं अभी भी पुस्तकालय डिजाइन बहुत अच्छा था कि कई उपहार लाने के बारे में होना चाहिए।

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


5
क्या आप इस बात पर टिप्पणी कर सकते हैं कि बिंदु 2 के लिए आपके उदाहरण केवल किसी चीज़ का उपयोग करने से बेहतर होगा print (spellout(100));और print (translate("Good morning"));यह एक अच्छे विचार की तरह प्रतीत होगा, क्योंकि यह I / O से प्रारूपण और i18n को प्रदर्शित करता है।
शेड्यूलर

3
क्योंकि इसका अनुवाद भाषा के अनुसार किया जा सकता है। यानी french_output << translate("Good morning"):; english_output << translate("Good morning") आपको देगा: "बोंजोर गुड मॉर्निंग"
आर्टीम

3
स्थानीयकरण बहुत कठिन होता है जब आपको एक भाषा में '<< "पाठ" << मान' की आवश्यकता होती है, लेकिन दूसरे में '<< मान << "पाठ" - प्रिंटफ की तुलना में
मार्टिन बेकेट

@ मर्टिन बेकेट मुझे पता है, Boost.Locale लाइब्रेरी पर एक नज़र डालें, क्या होता है कि ऐसे मामले में आप ऐसा करते हैं out << format("text {1}") % valueऔर इसका अनुवाद किया जा सकता है "{1} translated"। तो यह ठीक काम करता है ;-)
Artyom

15
"क्या किया जा सकता है" बहुत प्रासंगिक नहीं है। आप एक प्रोग्रामर हैं, पर्याप्त प्रयास से कुछ भी किया जा सकता है । लेकिन IOStreams, जो किया जा सकता है , उसमें से अधिकांश को प्राप्त करने के लिए भयानक रूप से दर्दनाक बनाता है । और आप आमतौर पर अपनी परेशानी के लिए घटिया प्रदर्शन करते हैं।
जुल्फ

2

(यह जवाब सिर्फ मेरी राय पर आधारित है)

मुझे लगता है कि IOStreams उनके फ़ंक्शन समकक्षों की तुलना में बहुत अधिक जटिल हैं। जब मैं C ++ में लिखता हूं, तब भी मैं "ओल्ड-स्टाइल" I / O के लिए cstdio हेडर का उपयोग करता हूं, जो मुझे अधिक अनुमानित लगता है। एक साइड नोट पर, (हालांकि यह वास्तव में महत्वपूर्ण नहीं है; पूर्ण समय का अंतर नगण्य है) IOStreams कई मौकों पर CI / O की तुलना में धीमा साबित हुआ है।


मुझे लगता है कि आप "कार्यात्मक" के बजाय "फ़ंक्शन" का मतलब है। कार्यात्मक प्रोग्रामिंग उस सामान्य प्रोग्रामिंग को देखते हुए कोड थैट्स को और भी बदतर बनाता है।
क्रिस बेके

उस गलती को इंगित करने के लिए धन्यवाद; मैंने उत्तर को सुधार को प्रतिबिंबित करने के लिए संपादित किया है।
डेलन अजाबानी

5
IOStreams लगभग निश्चित रूप से क्लासिक stdio की तुलना में धीमा होना चाहिए; अगर मुझे एक एक्स्टेंसिबल और आसानी से उपयोग होने वाले I / O स्ट्रीम फ्रेमवर्क को डिजाइन करने का काम दिया गया था, तो मैं शायद स्पीड सेकेंडरी को जज करूंगा, यह देखते हुए कि असली अड़चनें संभवतः फ़ाइल I / O स्पीड या नेटवर्क ट्रैफिक बैंडविड्थ होगी।
stakx - अब

1
मैं मानता हूं कि I / O या नेटवर्क के लिए कम्प्यूटेशनल गति बहुत मायने नहीं रखती है। हालाँकि याद रखें कि संख्यात्मक / स्ट्रिंग रूपांतरण के लिए C ++ उपयोग कर रहा है sstringstream। मुझे लगता है कि गति मायने रखती है, हालांकि यह माध्यमिक है।
Matthieu M.

1
@stakx फ़ाइल I / O और नेटवर्क अड़चनें 'प्रति बाइट' लागतों का एक कार्य है, जो काफी छोटे हैं, और प्रौद्योगिकी सुधार के द्वारा नाटकीय रूप से नीचे चलाए जाते हैं। इसके अलावा, डीएमए को देखते हुए, ये ओवरहेड एक ही मशीन पर अन्य थ्रेड्स से सीपीयू समय को दूर नहीं करते हैं। इसलिए, यदि आप स्वरूपित आउटपुट कर रहे हैं, तो कुशलतापूर्वक बनाम नहीं करने की लागत, आसानी से महत्वपूर्ण हो सकती है (कम से कम, डिस्क या नेटवर्क द्वारा ओवरशैड नहीं किया जाता है; अधिक संभावना है कि यह ऐप में अन्य प्रसंस्करण द्वारा ओवरहैड किया गया है)।
greggo

2

IOStream का उपयोग करने पर मैं हमेशा आश्चर्य में पड़ जाता हूं।

लाइब्रेरी टेक्स्ट ओरिएंटेड लगती है न कि बाइनरी ओरिएंटेड। यह पहला आश्चर्य हो सकता है: द्विआधारी व्यवहार प्राप्त करने के लिए फ़ाइल धाराओं में बाइनरी ध्वज का उपयोग करना पर्याप्त नहीं है। उपर्युक्त उपयोगकर्ता चार्ल्स साल्विया ने इसे सही तरीके से देखा है: IOStreams प्रारूपण पहलुओं (जहां आप सुंदर आउटपुट चाहते हैं, उदाहरण के लिए फ़्लोट्स के लिए सीमित अंक) क्रमबद्ध पहलुओं के साथ) (जहां आप जानकारी हानि नहीं चाहते हैं)। संभवतः इन पहलुओं को अलग करना अच्छा होगा। Boost.Serialization यह आधा करता है। यदि आप चाहते हैं कि आवेषण और चिमटा करने के लिए जो मार्गों के लिए एक क्रमबद्ध समारोह है। पहले से ही आप दोनों पहलुओं के बीच तनाव है।

कई कार्यों ने भी शब्दार्थ को भ्रमित किया है (उदाहरण के लिए, गेटलाइन, उपेक्षा और पढ़ें। कुछ सीमांकक निकालते हैं, कुछ नहीं; कुछ सेट ईओएफ भी)। इसके अलावा जब धारा को लागू करते समय कुछ अजीब फ़ंक्शन नामों का उल्लेख करते हैं (जैसे xsputn, uflow, underflow)। जब wchar_t वेरिएंट का उपयोग करता है तो चीजें और भी खराब हो जाती हैं। वाइस्टस्ट्रीम मल्टीबाइट में अनुवाद करता है जबकि वेस्ट्रिंगस्ट्रीम नहीं करता है। बाइनरी I / O बॉक्स से बाहर wchar_t के साथ काम नहीं करता है: आपके पास कोडेकवेट को अधिलेखित करना है।

C बफ़र्ड I / O (यानी FILE) अपने C ++ समकक्ष के समान शक्तिशाली नहीं है, लेकिन अधिक पारदर्शी है और इसमें बहुत कम काउंटर सहज व्यवहार है।

अभी भी हर बार जब मैं IOStream पर ठोकर खाता हूं, तो मैं एक पतंगे की तरह आग में आकर्षित हो जाता हूं। संभवतः यह एक अच्छी बात होगी यदि कुछ वास्तव में चतुर आदमी समग्र वास्तुकला पर एक अच्छी नज़र रखेगा।


1

मैं प्रश्न के पहले भाग का जवाब देने में मदद नहीं कर सकता (किसने किया?)। लेकिन अन्य पोस्ट में इसका जवाब दिया गया था।

प्रश्न के दूसरे भाग के रूप में (अच्छी तरह से डिजाइन?), मेरा उत्तर एक शानदार "नहीं!" है। यहाँ एक छोटा सा उदाहरण जो मुझे वर्षों से अविश्वास में अपना सिर हिला रहा है:

#include <stdint.h>
#include <iostream>
#include <vector>

// A small attempt in generic programming ;)
template <class _T>
void ShowVector( const char *title, const std::vector<_T> &v)
{
    std::vector<_T>::const_iterator iter;
    std::cout << title << " (" << v.size() << " elements): ";
    for( iter = v.begin(); iter != v.end(); ++iter )
    {
        std::cout << (*iter) << " ";
    }
    std::cout << std::endl;
}
int main( int argc, const char * argv[] )
{
    std::vector<uint8_t> byteVector;
    std::vector<uint16_t> wordVector;
    byteVector.push_back( 42 );
    wordVector.push_back( 42 );
    ShowVector( "Garbled bytes as characters output o.O", byteVector );
    ShowVector( "With words, the numbers show as numbers.", wordVector );
    return 0;
}

ऊपर कोड iostream डिजाइन के कारण बकवास पैदा करता है। मेरी समझ से परे कुछ कारणों से, वे uint8_t बाइट्स को पात्रों के रूप में मानते हैं, जबकि बड़े अभिन्न प्रकारों को संख्याओं की तरह व्यवहार किया जाता है। खराब बैड डिजाइन।

ऐसा कोई तरीका नहीं है जिससे मैं इसे ठीक कर सकूं। प्रकार के बजाय एक फ्लोट या एक डबल हो सकता है ... इसलिए मूर्ख iostream बनाने के लिए 'int' करने के लिए एक कास्ट समझें कि नंबर चार्ट नहीं हैं विषय मदद नहीं करेगा।

मेरे जवाब के लिए एक डाउन-वोट प्राप्त करने के बाद, शायद स्पष्टीकरण के कुछ और शब्द ... IOStream डिज़ाइन त्रुटिपूर्ण है क्योंकि यह प्रोग्रामर को एक आइटम को राज्य का साधन बताने का साधन नहीं देता है। IOStream कार्यान्वयन मनमाने ढंग से निर्णय लेता है (जैसे कि uint8_t को एक चार के रूप में व्यवहार करना, एक बाइट संख्या नहीं)। यह आईओएसट्रीम डिजाइन का एक दोष है, क्योंकि वे अस्वीकार्य हासिल करने की कोशिश करते हैं।

C ++ एक प्रकार को वर्गीकृत करने की अनुमति नहीं देता है - भाषा में सुविधा नहीं है। Is_number_type () या is_character_type () IOStream एक उचित स्वचालित विकल्प बनाने के लिए उपयोग कर सकता है जैसी कोई चीज नहीं है। उस पर ध्यान न देना और एक पुस्तकालय के डिजाइन दोष के साथ अनुमान लगाने की कोशिश करना है।

स्वीकार किया, प्रिंटफ () समान रूप से एक सामान्य "ShowVector ()" कार्यान्वयन में काम करने में विफल रहेगा। लेकिन यह iostream व्यवहार के लिए कोई बहाना नहीं है। लेकिन यह बहुत संभावना है कि प्रिंटफ () मामले में, शोवेक्टर () को इस तरह परिभाषित किया जाएगा:

template <class _T>
void ShowVector( const char *formatString, const char *title, const std::vector<_T> &v );

3
दोष (विशुद्ध रूप से) iostream के साथ नहीं है। जाँचें कि आपके uint8_tलिए क्या एक टाइपराइफ है। क्या यह वास्तव में एक चर है? तो फिर यह एक चार की तरह इलाज के लिए iostreams दोष नहीं है।
मार्टिन बा

और अगर आप यह सुनिश्चित करना चाहते हैं कि आपको जेनेरिक कोड में एक नंबर मिले, तो आप स्ट्रीम इंसर्शन ऑपरेटर के बजाय num_putपहलू का उपयोग कर सकते हैं ।
मार्टिन बा

@Martin Ba आप सही हैं - c / c ++ मानकों ने इसे खुला रखा है कि कितने बाइट्स का "छोटा अहस्ताक्षरित int" है। "अहस्ताक्षरित चार" भाषा का एक आदर्श वाक्य है। यदि आप वास्तव में एक बाइट चाहते हैं, तो आपको एक अहस्ताक्षरित चार का उपयोग करना होगा। C ++ भी टेम्पलेट तर्कों पर प्रतिबंध लगाने की अनुमति नहीं देता है - जैसे कि "केवल नंबर" और इसलिए यदि मैंने ShowVector के कार्यान्वयन को आपके प्रस्तावित num_put समाधान में बदल दिया, तो ShowVector तार के एक वेक्टर को प्रदर्शित नहीं कर सका, है ना? ;)
बिट विकलर

1
@Martin Bla: cppreference का उल्लेख है कि int8_t 8 बिट्स की चौड़ाई के साथ एक हस्ताक्षरित पूर्णांक प्रकार है। मैं लेखक से सहमत हूं कि यह अजीब बात है कि आपको तब कचरा आउटपुट मिलता है, हालांकि यह टाइपस्टैफ़ द्वारा तकनीकी रूप से समझाने योग्य है और iostream में char प्रकारों का अधिभार । यह एक टाइप के बजाय एक __int8 एक सच्चे प्रकार होने से हल किया जा सकता था।
Gast128

ओह, यह वास्तव में तय करने के लिए बहुत आसान है: // std के लिए फिक्सेस :: ostream जो अहस्ताक्षरित / हस्ताक्षरित / चार प्रकारों के लिए समर्थन को तोड़ दिया है // और 8-बिट पूर्णांक प्रिंट करता है जैसे वे वर्ण थे। namespace ostream_fixes {इनलाइन std :: ostream & ऑपरेटर << (std: ostream & os, अहस्ताक्षरित char i) {वापसी os << static_cast <अहस्ताक्षरित int> (i); } इनलाइन std :: ostream & ऑपरेटर << (std :: ostream & os, हस्ताक्षरित char i) {वापसी os << static_cast <हस्ताक्षरित int> (i); }} // नामस्थान ostream_fixes
mcv

1

C ++ iostreams में बहुत सी खामियां हैं, जैसा कि अन्य प्रतिक्रियाओं में नोट किया गया है, लेकिन मैं इसके बचाव में कुछ नोट करना चाहूंगा।

सी ++ वस्तुतः गंभीर उपयोग की भाषाओं के बीच अद्वितीय है जो शुरुआती के लिए चर इनपुट और आउटपुट को सीधा बनाता है। अन्य भाषाओं में, उपयोगकर्ता इनपुट में टाइप क्यूरियन या स्ट्रिंग फ़ॉर्मेटर्स शामिल होते हैं, जबकि C ++ कंपाइलर को सभी काम करता है। वही आउटपुट के लिए काफी हद तक सही है, हालांकि इस संबंध में C ++ उतना विशिष्ट नहीं है। फिर भी, आप कक्षाओं और ऑब्जेक्ट-ओरिएंटेड अवधारणाओं को समझने के बिना C ++ में I / O बहुत अच्छी तरह से स्वरूपित कर सकते हैं, जो कि शैक्षणिक रूप से उपयोगी है, और प्रारूप सिंटैक्स को समझने के बिना। फिर, यदि आप शुरुआती सिखा रहे हैं, तो यह एक बड़ा प्लस है।

शुरुआती लोगों के लिए यह सादगी एक कीमत पर आती है, जो इसे अधिक जटिल परिस्थितियों में I / O से निपटने के लिए सिरदर्द बना सकती है, लेकिन उम्मीद है कि इस बिंदु से प्रोग्रामर ने उनसे निपटने में सक्षम होने के लिए पर्याप्त सीखा है, या कम से कम काफी पुराना हो गया है पीने के लिए।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.