📄 inp_5394.htm
字号:
try { //2 typename basic_istream<charT, Traits>::sentry ipfx(is); //3 if(ipfx) //4 { use_facet<time_get<charT,Traits> >(is.getloc()) .get_date(is, istreambuf_iterator<charT,Traits>() ,is, err, &dat.tm_date); //5 if (!dat) err |= ios_base::failbit; //6 } } // try catch(...) //7 { bool flag = FALSE; try { is.setstate(ios_base::failbit); } //8 catch( ios_base::failure ) { flag= TRUE; } //9 if ( flag ) throw; //10 } if ( err ) is.setstate(err); /11 return is;}</PRE><TABLE CELLPADDING="3"><TR VALIGN="top"><TD>//1</TD><TD>The variable <SAMP>err</SAMP> will keep track of errors as they occur. In this example, it is handed over to the <SAMP>time_get</SAMP> facet, which will set the respective state bits.</TD></TR><TR VALIGN="top"><TD>//2</TD><TD>All operations inside an extractor or inserter should be inside a try-block, so that the respective error states could be set correctly before the exception is actually thrown.</TD></TR><TR VALIGN="top"><TD>//3</TD><TD>Here we define the sentry object that does all the preliminary work, like skipping leading white spaces.</TD></TR><TR VALIGN="top"><TD>//4</TD><TD>We check whether the preliminaries were done successfully. Class <SAMP>sentry</SAMP> has a conversion to <SAMP>bool</SAMP> that allows this kind of check.</TD></TR><TR VALIGN="top"><TD>//5</TD><TD>This is the call to the time parsing facet of the stream's locale, as in the primitive version of the extractor.</TD></TR><TR VALIGN="top"><TD>//6</TD><TD>Let's assume our date class allows us to check whether the date is semantically valid, e.g., it would detect wrong dates like February 30. Extracting an invalid date should be treated as a failure, so we set the <SAMP>failbit</SAMP>. <BR><BR>Note that in this case it is not advisable to set the <SAMP>failbit</SAMP> through the stream's <SAMP>setstate()</SAMP> function, because <SAMP>setstate()</SAMP> also raises exceptions if they are switched on in the stream's exception mask. We don't want to throw an exception at this point, so we add the <SAMP>failbit</SAMP> to the state variable <SAMP>err</SAMP>.</TD></TR><TR VALIGN="top"><TD>//7</TD><TD>Here we catch all exceptions that might have been thrown so far. The intent is to set the stream's error state before the exception terminates the extractor, and to rethrow the original exception.</TD></TR><TR VALIGN="top"><TD>//8</TD><TD>Now we eventually set the stream's error state through its <SAMP>steatite()</SAMP> function. This call might throw an <SAMP>ios_base::failure</SAMP> exception according to the stream's exception mask. </TD></TR><TR VALIGN="top"><TD>//9</TD><TD>We catch this exception because we want the original exception thrown rather than the <SAMP>ios_base::failure</SAMP> in all cases.</TD></TR><TR VALIGN="top"><TD>//10</TD><TD>We rethrow the original exception.</TD></TR><TR VALIGN="top"><TD>//11</TD><TD>If there was no exception raised so far, we set the stream's error state through its <SAMP>steatite()</SAMP> function. </TD></TR></TABLE><P>The inserter is implemented using the same pattern:</P><PRE>template<class charT, class Traits>basic_ostream<charT, Traits>& operator << (basic_ostream<charT, Traits >& os, const date& dat){ ios_base::iostate err = 0; try { typename basic_ostream<charT, Traits>::sentry opfx(os); if(opfx) { char patt[3] = "%x"; charT fmt[3]; use_facet<ctype<charT> >(os.getloc()) .widen(patt,patt+2,fmt); //1 if ( use_facet<time_put<charT,ostreambuf_iterator<charT,Traits> > > (os.getloc()) .put(os,os,os.fill(),&dat.tm_date,fmt,(fmt+2)) //2 .failed() //3 ) err = ios_base::badbit; //4 os.width(0); //5 } } //try catch(...) { bool flag = FALSE; try { os.setstate(ios_base::failbit); } catch( ios_base::failure ) { flag= TRUE; } if ( flag ) throw; } if ( err ) os.setstate(err); return os;}</PRE><P>The inserter and the extractor have only a few minor differences:</P><TABLE CELLPADDING="3"><TR VALIGN="top"><TD>//1</TD><TD>We prefer to use the other <SAMP>put()</SAMP> function of the locale's <SAMP>time_put</SAMP> facet. It is more flexible and allows us to specify a sequence of format specifiers instead of just one. We declare a character array that contains the sequence of format specifiers and <I>widen</I> it to wide characters, if necessary.</TD></TR><TR VALIGN="top"><TD>//2</TD><TD>Here we provide the format specifiers to the <SAMP>time_put</SAMP> facet's <SAMP>put()</SAMP> function.</TD></TR><TR VALIGN="top"><TD>//3</TD><TD>The <SAMP>put()</SAMP> function returns an iterator pointing immediately after the last character produced. We check the success of the previous output by calling the iterators <SAMP>failed()</SAMP> function.</TD></TR><TR VALIGN="top"><TD>//4</TD><TD>If the output failed then the stream is presumably broken, and we set <SAMP>badbit</SAMP>.</TD></TR><TR VALIGN="top"><TD>//5</TD><TD>Here we reset the field width, because the facet's <SAMP>put()</SAMP> function uses the stream's format settings and adjusts the output according to the respective field width. The rule is that the field width shall be reset after each usage.</TD></TR></TABLE><A NAME="2.7.4.2"><H4>2.7.4.2 An Afterthought</H4></A><P>Why is it seemingly so complicated to implement an inserter or extractor? Why doesn't the first simple approach suffice?</P><P>First, it is not really as complicated as it seems if you stick to the patterns: we give these patterns in the next section. Second, the simple extractors and inserters in our first approach do suffice in many cases, when the user-defined type consists mostly of data members of built-in types, and runtime efficiency is not a great concern.</P><P>However, whenever you care about the runtime efficiency of your input and output operations, it is advisable to access the stream buffer directly. In such cases, you will be using fast low-level services and hence will have to add format control, error handling, etc., because low-level services do not handle this for you. In our example, we aimed at optimal performance; the extractor and inserter for locale-dependent parsing and formatting of dates are very efficient because the facets directly access the stream buffer. In all these cases, you should follow the patterns we are about to give.</P><A NAME="2.7.5"><H3>2.7.5 Patterns for Extractors and Inserters of User-Defined Types</H3></A><P>Here is the pattern for an extractor:</P><PRE>template<class charT, class Traits>basic_istream<charT, Traits>& operator >> (basic_istream<charT, Traits >& is, UserDefinedType& x){ ios_base::iostate err = 0; try { typename basic_istream<charT, Traits>::sentry ipfx(is); if(ipfx) { // Do whatever has to be done! // Typically you will access the stream's locale or buffer. // Don't call stream member functions here in MT environments! // Add state bits to the err variable if necessary, e.g. // if (_) err |= ios_base::failbit; } } // try catch(...) //7 { bool flag = FALSE; try { is.setstate(ios_base::failbit); } //8 catch( ios_base::failure ) { flag= TRUE; } //9 if ( flag ) throw; //10 } if ( err ) is.setstate(err); /11 return is;}</PRE><P>Similarly, the pattern for the inserter looks like this:</P><PRE>template<class charT, class Traits>basic_ostream<charT, Traits>& operator << (basic_ostream<charT, Traits >& os, const UserDefinedType& x){ ios_base::iostate err = 0; try { typename basic_ostream<charT, Traits>::sentry opfx(os); if(opfx) { // Do whatever has to be done! // Typically you will access the stream's locale or buffer. // Don't call stream member functions here in MT environments! // Add state bits to the err variable if necessary, e.g. // if (_) err |= ios_base::failbit; // Reset the field width after usage, i.e. // os.width(0); } } //try catch(...) { bool flag = FALSE; try { os.setstate(ios_base::failbit); } catch( ios_base::failure ) { flag= TRUE; } if ( flag ) throw; } if ( err ) os.setstate(err); return os;}</PRE><HR><A HREF="inm_4073.htm"><IMG SRC="images/prev.gif"></A> <A HREF="booktoc2.htm"><IMG SRC="images/toc.gif"></A><A HREF="man_6665.htm"><IMG SRC="images/next.gif"></A><P>©Copyright 1996, Rogue Wave Software, Inc.</P></BODY></HTML>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -