📄 str_5412.htm
字号:
<HTML><HEAD><TITLE>2.11 Stream Storage for Private Use: iword, pword, and xalloc</TITLE></HEAD><BODY><A HREF="ug2.htm"><IMG SRC="images/banner.gif"></A><BR><A HREF="syn_4687.htm"><IMG SRC="images/prev.gif"></A><A HREF="booktoc2.htm"><IMG SRC="images/toc.gif"></A><A HREF="cre_2288.htm"><IMG SRC="images/next.gif"></A><BR><STRONG>Click on the banner to return to the user guide home page.</STRONG><H2>2.11 Stream Storage for Private Use: iword, pword, and xalloc</H2><P>As we have seen in previous sections, a stream carries a number of data items: an error state, a format state, a locale, an exception mask, information about tied streams, and a stream buffer, to mention a few. Sometimes it is useful and necessary to store <I>additional</I> data in a stream.</P><A NAME="2.11.1"><H3>2.11.1 An Example: Storing a Date Format String </H3></A><P>Consider the inserter and extractor we defined for a date class in <A HREF="inp_5394.htm">Section 2.7</A>. The input and output operations were internationalized and relayed the task of date formatting and parsing to the stream's locale. Here, however, the rules for formatting and parsing were fixed, making them much more restricted than the features available in the standard C library, for example. </P><P>In the standard C library, you can specify format strings, similar to those for <SAMP>prinft()</SAMP> and <SAMP>scanf()</SAMP>, that describe the rules for parsing and formatting dates.<A HREF="endnote2.htm#fn38">[38]</A> For example, the format string <SAMP>"%A, %B %d, %Y"</SAMP> stands for the rule that a date must consist of the name of the weekday, the name of the month, the day of the month, and the year--as in Friday, July 12, 1996. </P><P>Now imagine you want to improve the input and output operations for the date class by allowing specification of such format strings. How can you do this? Other format information is stored in the stream's format state; consequently, you may want to store the format string for dates somewhere in the stream as well. And indeed, you can.</P><P>Streams have an array for private use. An array element is of a <SAMP>union</SAMP> type that allows access as a <SAMP>long</SAMP> or as a pointer to <SAMP>void</SAMP>. The array is of unspecified size, and new memory is allocated as needed. In principle, you can think of it as infinitely long.</FN><P>You can use this array to store in a stream whatever additional information you might need. In our example, we would want to store the format string.</P><P>The array can be accessed by two functions: <SAMP>iword()</SAMP> and <SAMP>pword()</SAMP>. Both functions take an index to an array element and return a reference to the respective element. The function <SAMP>iword()</SAMP> returns a reference to <SAMP>long</SAMP>; the function <SAMP>pword()</SAMP> allows access to the array element as a pointer to <SAMP>void</SAMP>.</P><P>Indices into the array are maintained by the <SAMP>xalloc()</SAMP> function, a static function in class <SAMP>ios_base</SAMP> that returns the next free index into the array.</P><A NAME="2.11.2"><H3>2.11.2 Another Look at the Date Format String</H3></A><P>We would like to store the format string for dates in the iostream storage through <SAMP>iword()</SAMP> and <SAMP>pword()</SAMP>. In this way, the input and output operations for <SAMP>date</SAMP> objects can access the format string for parsing and formatting. Format parameters are often set with manipulators (see <A HREF="for_5394.htm#2.3.3.2">Section 2.3.3.2</A>), so we should add a manipulator that sets the date format string. It could be used like this:</P><PRE>date today;ofstream ostr;// _ostr << setfmt("%D") << today;</PRE><P>Here is a suggested implementation for such a manipulator:</P><PRE>class setfmt : public smanip<const char*>{ public: setfmt(const char* fmt) : smanip<const char*>(setfmt_,fmt) {} private: static const int datfmtIdx ; \\1 static ios_base& setfmt_(ios_base& str, const char* fmt) { str.pword(datfmtIdx) = (void*) fmt; \\2 return str; } template<class charT, class Traits> friend basic_ostream<charT, Traits> & \\3 operator << ( basic_ostream<charT, Traits >& os, const date& dat);}; const int setfmt::datfmtIdx = ios_base::xalloc(); \\4</PRE><P>The technique applied to implement the manipulator is described in detail in Example 2 of <A HREF="man_6665.htm#2.8.2.3">Section 2.8.2.3</A>, so we won't repeat it here. But regarding this manipulator and the private use of iostream storage , there are other interesting details:</P><TABLE CELLPADDING="3"><TR VALIGN="top"><TD>//1</TD><TD>The manipulator class owns the index of the element in the iostream storage where we want to store the format string. It is initialized in <SAMP>\\4 </SAMP>by a call to <SAMP>xalloc()</SAMP>.</TD></TR><TR VALIGN="top"><TD>//2</TD><TD>The manipulator accesses the array <SAMP>pword()</SAMP> using the index <SAMP>datfmtIdx</SAMP>, and stores the pointer to the date format string. Note that the reference returned by <SAMP>pword()</SAMP> is only used for <I>storing</I> the pointer to the date format string. Generally, one should never store a reference returned by <SAMP>iword()</SAMP> or <SAMP>pword()</SAMP> in order to access the stored data through this reference later on. This is because these references can become invalid once the array is reallocated or copied. (See the <I>Class Reference</I> for more details.)</ANNOTFN></TD></TR><TR VALIGN="top"><TD>//3</TD><TD>The inserter for <SAMP>date</SAMP> objects needs to access the index into the array of pointers, so that it can read the format string and use it. Therefore, the inserter has to be declared as a friend. In principle, the extractor would have to be a friend, too; however, the standard C++ locale falls short of supporting the use of format strings like the ones used by the standard C function <SAMP>strptime()</SAMP>. Hence, the implementation of a date extractor that supports date format strings would be a lot more complicated than the implementation for the inserter, which can use the stream's locale. We have omitted the extractor for the sake of brevity.</TD></TR></TABLE><P>The inserter for <SAMP>date</SAMP> objects given below is almost identical to the one we described in <A HREF="inp_5394.htm#2.7.3">Section 2.7.3</A>:</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; char* patt = 0; int len = 0; charT* fmt = 0; try { typename basic_ostream<charT, Traits>::sentry opfx(os); if(opfx) { patt = (char*) os.pword(setfmt.datfmtIdx); \\1 len = strlen(patt); fmt = new charT[len]; use_facet<ctype<charT> >(os.getloc()). widen(patt, patt+len, fmt); if (use_facet<time_put<charT ,ostreambuf_iterator<charT,Traits> > > (os.getloc()) .put(os,os,os.fill(),&dat.tm_date,fmt,fmt+len) \\2 .failed() ) err = ios_base::badbit; os.width(0); } } //try catch(...) { delete [] fmt; bool flag = FALSE; try { os.setstate(ios_base::failbit); } catch( ios_base::failure ) { flag= TRUE; } if ( flag ) throw; } delete [] fmt; if ( err ) os.setstate(err); return os;}</PRE><P>The only change from the previous inserter is that the format string here is read from the iostream storage (in statement <SAMP>//1</SAMP>) instead of being the fixed string <SAMP>"%x"</SAMP>. The format string is then provided to the locale's time formatting facet (in statement <SAMP>//2</SAMP>).</P><A NAME="2.11.3"><H3>2.11.3 Caveat</H3></A><P>Note that the solution suggested here has a pitfall.</P><P>The manipulator takes the format specification and stores it. The inserter retrieves it and uses it. In such a situation, the question arises: Who owns the format string? In other words, who is responsible for creating and deleting it and hence controlling its lifetime? Neither the manipulator nor the inserter can own it because they share it.</P><P>We solved the problem by requiring the format specification to be created and deleted by the iostream user. Only a pointer to the format string is handed over to the manipulator, and only this pointer is stored through <SAMP>pword()</SAMP>. Also, we do not copy the format string because it would not be clear who--the manipulator or the inserter--is responsible for deleting the copy. Hence the iostream user has to monitor the format string's lifetime, and ensure that the format string is valid for as long as it is accessed by the inserter.</P><P>This introduces a subtle lifetime problem in cases where the date format is a variable instead of a constant: the stream might be a static stream and hence live longer that the date format variable. This is a problem you will always deal with when storing a pointer or reference to additional data instead of copying the data.</P><P>However, this subtle problem does not impose an undue burden on the user of our <SAMP>setfmt</SAMP> manipulator. If a static character sequence is provided, as in:</P><PRE>cout << <SAMP>setfmt</SAMP>("%A, %B %d, %Y") << today;</PRE><P>the <SAMP>setfmt</SAMP> manipulator can be used safely, even with static streams like <SAMP>cout</SAMP>.</P><HR><A HREF="syn_4687.htm"><IMG SRC="images/prev.gif"></A> <A HREF="booktoc2.htm"><IMG SRC="images/toc.gif"></A><A HREF="cre_2288.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 + -