📄 cre_2288.htm
字号:
charT buf[3]; try { \\1 odatstream<charT,Traits>* p = dynamic_cast<odatstream<charT,Traits>*>(&os); \\2 } catch (bad_cast) \\3 { char patt[3] = "%x"; use_facet(os.getloc(), (ctype<charT>*)0).widen(patt,patt+3,buf); } fmt = (p) ? p->fmt_ : buf; \\4 if (use_facet<time_put<charT,ostreambuf_iterator<charT,Traits> > >(os.getloc()) .put(os,os,os.fill(),&dat.tm_date,fmt,fmt+Traits::length(fmt)).failed()) err = ios_base::badbit; 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><TABLE CELLPADDING="3"><TR VALIGN="top"><TD>//1</TD><TD>We will perform a dynamic cast in statement <SAMP>//2</SAMP>. A dynamic cast throws an exception in case of mismatch. Naturally, we do not want to confront our user with <SAMP>bad_cast</SAMP> exceptions because the mismatch does not signify an error condition, but only that the default formatting will be performed. For this reason, we will try to catch the potential <SAMP>bad_cast</SAMP> exception.</TD></TR><TR VALIGN="top"><TD>//2</TD><TD>This is the dynamic cast to find out whether the stream is a date stream or any other kind of output stream.</TD></TR><TR VALIGN="top"><TD>//3</TD><TD>In case of mismatch, we prepare the default date format specification <SAMP>"%x"</SAMP>.</TD></TR><TR VALIGN="top"><TD>//4</TD><TD>If the stream is not of type <SAMP>odatstream</SAMP>, the default format specification prepared in the catch clause is used. Otherwise, the format specification is taken from the private data member <SAMP>fmt_</SAMP>.</TD></TR></TABLE><A NAME="2.12.3.3"><H4>2.12.3.3 The Manipulator</H4></A><P>The date output stream has a member function for setting the format specification. Analogous to the standard stream format functions, we would like to provide a manipulator for setting the format specification. This manipulator will affect only output streams. Therefore, we have to define a manipulator base class for output stream manipulators, <SAMP>osmanip</SAMP>, along with the necessary inserter for this manipulator. We do this in the code below. See <A HREF="man_6665.htm">Section 2.8</A> for a detailed discussion of the technique we are using here:</P><PRE>template <class Ostream, class Arg>class osmanip { public: typedef Ostream ostream_type; typedef Arg argument_type; osmanip(Ostream& (*pf)(Ostream&, Arg), Arg arg) : pf_(pf) , arg_(arg) { ; } protected: Ostream& (*pf_)(Ostream&, Arg); Arg arg_; friend Ostream& operator<< (Ostream& ostr, const osmanip<Ostream,Arg>& manip);}; template <class Ostream, class Arg>Ostream& operator<< (Ostream& ostr,const osmanip<Ostream,Arg>& manip){ (*manip.pf_)(ostr,manip.arg_); return ostr;}</PRE><P>After these preliminaries, we can now implement the <SAMP>setfmt</SAMP> manipulator itself:</P><PRE>template <class charT, class Traits>inline basic_ostream<charT,Traits>&sfmt(basic_ostream<charT,Traits>& ostr, const char* f) \\1{ try { \\2 odatstream<charT,Traits>* p = dynamic_cast<odatstream<charT,Traits>*>(&ostr);}catch (bad_cast) \\3{ return ostr; } p->fmt(f); \\4 return ostr;}template <class charT,class Traits>inline osmanip<basic_ostream<charT,Traits>,const char*>setfmt(const char* fmt){ return osmanip<basic_ostream<charT,Traits>,const char*>(sfmt,fmt); } \\5</PRE><TABLE CELLPADDING="3"><TR VALIGN="top"><TD>//1</TD><TD>The function <SAMP>sfmt()</SAMP> is the function associated with the <SAMP>setfmt</SAMP> manipulator. Its task is to take a format specification and hand it over to the stream. This happens only if the stream is a date output stream; otherwise, nothing is done.</TD></TR><TR VALIGN="top"><TD>//2</TD><TD>We determine the stream's type through a dynamic cast. As it would be rather drastic to let a manipulator call result in an exception thrown, we catch the potential <SAMP>bad_cast</SAMP> exception. </TD></TR><TR VALIGN="top"><TD>//3</TD><TD>In case of mismatch, we don't do anything and simply return.</TD></TR><TR VALIGN="top"><TD>//4</TD><TD>In case the stream actually is a date output stream, we store the format specification by calling the stream's <SAMP>fmt()</SAMP> function.</TD></TR><TR VALIGN="top"><TD>//5</TD><TD>The manipulator itself is a function that creates an output manipulator object.</TD></TR></TABLE><A NAME="2.12.3.4"><H4>2.12.3.4 A Remark on Performance</H4></A><P>The solution suggested in the previous Sections 2.12.3.2 and 2.12.3.3 uses dynamic casts and exception handling to implement the date inserter and the date format manipulator. Although this technique is elegant and makes proper use of the C++ language, it might introduce some loss in runtime performance due to the use of exception handling. This is particularly true as the dynamic cast expression, and the exception it raises, is used as a sort of branching statement. In other words, the "exceptional" case occurs relatively often and is not really an exception. </P><P>If optimal performance is important, you can choose an alternative approach: in the proposed solution that uses dynamic casts, extend the date inserter for arbitrary output streams <SAMP>basic_ostream<charT,Traits>& operator<< (basic_ostream <charT,Traits>&, const date&)</SAMP> so that it formats dates differently, depending on the type of output stream. Alternatively, you can leave the existing date inserter for output streams unchanged and implement an additional date inserter that works for output <I>date</I> streams only; its signature would be <SAMP>odatstream<charT,Traits>& operator<< (odatstream<charT,Traits>&, const date&)</SAMP>. Also, you would have two manipulator functions, one for arbitrary output streams and one for output date streams only, that is, <SAMP>basic_ostream<charT,Traits>& sfmt (basic_ostream<charT,Traits>&, const char*) </SAMP>and <SAMP>odatstream<charT,Traits>& sfmt (odatstream<charT,Traits>&, const char*)</SAMP>. In each of the functions for date streams, you would replace those operations that are specific for output <I>date</I> streams.</P><P>This technique has the drawback of duplicating most of the inserter's code, which in turn might introduce maintenance problems. The advantage is that the runtime performance is likely to be improved.</P><A NAME="2.12.4"><H3>2.12.4 Using iword/pword for RTTI in Derived Streams</H3></A><P>In the previous section, we discussed an example that used runtime-type identification (RTTI) to enable a given input or output operation to adapt its behavior based on the respective stream type's properties.</P><P>Before RTTI was introduced into the C++ language in the form of the new style casts <SAMP>dynamic_cast<></SAMP>, the problem was solved using <SAMP>iword()</SAMP>, <SAMP>pword()</SAMP>, and <SAMP>xalloc()</SAMP> as substitutes for runtime-type identification (RTTI).<A HREF="endnote2.htm#fn43">[43]</A> We describe this old-fashioned technique only briefly because, as the previous example suggests, the use of dynamic casts is clearly preferable over the RTTI substitute. Still, the traditional technique might be useful if your current compiler does not yet support the new-style casts. </P><P>The basic idea of the traditional technique is that the stream class and all functions and classes that need the runtime type information, like the inserter and the manipulator function, agree on two things:</P><UL><LI><P>An index into the arrays for additional storage; in other words, <I>Where</I> do I find the RTTI?, and </P></LI><LI><P>The content or type identification that all concerned parties expect to find there; in other words, <I>What</I> will I find?</P></LI></UL><P>In the sketch below, the derived stream class reserves an index into the additional storage. The index is a static data member of the derived stream class, and identifies all objects of that stream class. The content of that particular slot in the stream's additional storage, which is accessible through <SAMP>pword()</SAMP>, is expected to be the respective stream object's <SAMP>this</SAMP> pointer.</P><P>Here are the modifications to the derived class <SAMP>odatstream</SAMP>:</P><PRE>template <class charT, class Traits=char_traits<charT> >class odatstream : public basic_ostream <charT,Traits>{public: static int xindex() \\1 { static int inited = 0; static int value = 0; if (!inited) { value = xalloc(); inited++; } return value; } odatstream(basic_ostream<charT,Traits>& ostr,const char* fmt = "%x") : basic_ostream<charT,Traits>(ostr.rdbuf()) { pword(xindex()) = this; \\2 fmt_=new charT[strlen(fmt)]; use_facet<ctype<charT> >(ostr.getloc()).widen(fmt, fmt+strlen(fmt), fmt_); } // _ other member, as in the previous section _};</PRE><TABLE CELLPADDING="3"><TR VALIGN="top"><TD>//1</TD><TD>The static function <SAMP>xindex()</SAMP> is concerned with reserving the index into the arrays for additional storage. It also serves as the access function to the index.</TD></TR><TR VALIGN="top"><TD>//2</TD><TD>The reserved slot in the arrays for additional storage is filled with the object's own address.</TD></TR></TABLE><P>Here are the corresponding modifications to the manipulator:</P><PRE>template <class charT, class Traits>inline basic_ostream<charT,Traits>&sfmt(basic_ostream<charT,Traits>& ostr, const char* f){ if (ostr.pword(odatstream<charT,Traits>::xindex()) == &ostr) \\1 ((odatstream<charT,Traits>&)ostr).fmt(f); return ostr;}</PRE><TABLE CELLPADDING="3"><TR VALIGN="top"><TD>//1</TD><TD>The manipulator function checks whether the content of the reserved slot in the stream's storage is the stream's address. If it is, the stream is considered to be a date output stream.</TD></TR></TABLE><P>Note that the technique described in this section is not safe. There is no way to ensure that date output streams and their related functions and classes are the only ones that access the reserved slot in a date output stream's additional storage. In principle, every stream object of any type can access all entries through <SAMP>iword()</SAMP> or <SAMP>pword()</SAMP>. It's up to your programming discipline to restrict access to the desired functions. It is unlikely, however, that all streams will make the same assumptions about the storage's content. Instead of agreeing on each stream object's address as the run-time-type identification, we also could have stored certain integers, pointers to certain strings, etc. Remember, it's the combination of reserved index <I>and</I> assumed content that represents the RTTI substitute.</P></LI></OL><HR><A HREF="str_5412.htm"><IMG SRC="images/prev.gif"></A> <A HREF="booktoc2.htm"><IMG SRC="images/toc.gif"></A><A HREF="def_8655.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 + -