← Back to team overview

zorba-coders team mailing list archive

[Merge] lp:~paul-lucas/zorba/feature-transcode_streambuf-2 into lp:zorba

 

Chris Hillery has proposed merging lp:~paul-lucas/zorba/feature-transcode_streambuf-2 into lp:zorba.

Requested reviews:
  Chris Hillery (ceejatec)

For more details, see:
https://code.launchpad.net/~paul-lucas/zorba/feature-transcode_streambuf-2/+merge/96540
-- 
https://code.launchpad.net/~paul-lucas/zorba/feature-transcode_streambuf-2/+merge/96540
Your team Zorba Coders is subscribed to branch lp:zorba.
=== modified file 'include/zorba/transcode_stream.h'
--- include/zorba/transcode_stream.h	2012-02-07 03:56:23 +0000
+++ include/zorba/transcode_stream.h	2012-03-08 10:40:24 +0000
@@ -60,6 +60,8 @@
  *    }
  *  }
  * \endcode
+ * Alternatively, you may wish to use either \c attach(), \c auto_attach, or
+ * \c transcode_stream instead.
  *
  * While %transcode::streambuf does support seeking, the positions are relative
  * to the original byte stream.
@@ -114,11 +116,131 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
+} // namespace transcode
+
+namespace internal {
+
+ZORBA_DLL_PUBLIC
+zorba::transcode::streambuf*
+alloc_streambuf( char const *charset, std::streambuf *orig );
+
+ZORBA_DLL_PUBLIC
+void dealloc_streambuf( zorba::transcode::streambuf* );
+
+ZORBA_DLL_PUBLIC
+int get_streambuf_index();
+
+ZORBA_DLL_PUBLIC
+void stream_callback( std::ios_base::event, std::ios_base&, int index );
+
+} // namespace internal
+
+namespace transcode {
+
+///////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Attaches a transcode::streambuf to a stream.  Unlike using a
+ * transcode::streambuf directly, this function will create the streambuf,
+ * attach it to the stream, and manage it for the lifetime of the stream
+ * automatically.
+ *
+ * @param ios The stream to attach the transcode::streambuf to.  If the stream
+ * already has a transcode::streambuf attached to it, this function does
+ * nothing.
+ * @param charset The name of the character encoding to convert from/to.
+ */
+template<typename charT,typename Traits> inline
+void attach( std::basic_ios<charT,Traits> &ios, char const *charset ) {
+  int const index = internal::get_streambuf_index();
+  void *&pword = ios.pword( index );
+  if ( !pword ) {
+    streambuf *const buf = internal::alloc_streambuf( charset, ios.rdbuf() );
+    ios.rdbuf( buf );
+    pword = buf;
+    ios.register_callback( internal::stream_callback, index );
+  }
+}
+
+/**
+ * Detaches a previously attached transcode::streambuf from a stream.  The
+ * streambuf is destroyed and the stream's original streambuf is restored.
+ *
+ * @param ios The stream to detach the transcode::streambuf from.  If the
+ * stream doesn't have a transcode::streambuf attached to it, this function
+ * does nothing.
+ */
+template<typename charT,typename Traits> inline
+void detach( std::basic_ios<charT,Traits> &ios ) {
+  int const index = internal::get_streambuf_index();
+  if ( streambuf *const buf = static_cast<streambuf*>( ios.pword( index ) ) ) {
+    ios.pword( index ) = 0;
+    ios.rdbuf( buf->orig_streambuf() );
+    internal::dealloc_streambuf( buf );
+  }
+}
+
+/**
+ * Checks whether the given stream has a transcode::streambuf attached.
+ *
+ * @param ios The stream to check.
+ * @return \c true only if a transcode::streambuf is attached.
+ */
+template<typename charT,typename Traits> inline
+bool is_attached( std::basic_ios<charT,Traits> &ios ) {
+  return !!ios.pword( internal::get_streambuf_index() );
+}
+
+/**
+ * A %transcode::auto_attach is a class that attaches a transcode::streambuf to
+ * a stream and automatically detaches it when the %auto_attach object is
+ * destroyed.
+ * \code
+ *  void f( ostream &os ) {
+ *    transcode::auto_attach<ostream> const raii( os, "ISO-8859-1" );
+ *    // ...
+ *  }
+ * \endcode
+ * A %transcode::auto_attach is useful for streams not created by you.
+ *
+ * @see http://en.wikipedia.org/wiki/Resource_Acquisition_Is_Initialization
+ */
+template<class StreamType>
+class auto_attach {
+public:
+  /**
+   * Constructs an %auto_attach object calling attach() on the given stream.
+   *
+   * @param stream The stream to attach the transcode::streambuf to.  If the
+   * stream already has a transcode::streambuf attached to it, this contructor
+   * does nothing.
+   * @param charset The name of the character encoding to convert from/to.
+   */
+  auto_attach( StreamType &stream, char const *charset ) : stream_( stream ) {
+    attach( stream, charset );
+  }
+
+  /**
+   * Destroys this %auto_attach object calling detach() on the previously
+   * attached stream.
+   */
+  ~auto_attach() {
+    detach( stream_ );
+  }
+
+private:
+  StreamType &stream_;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
 /**
  * A %transcode::stream is used to wrap a C++ standard I/O stream with a
  * transcode::streambuf so that transcoding and the management of the streambuf
  * happens automatically.
  *
+ * A %transcode::stream is useful for streams created by you.
+ *
  * @tparam StreamType The I/O stream class type to wrap. It must be a concrete
  * stream class.
  */

=== modified file 'src/api/transcode_streambuf.cpp'
--- src/api/transcode_streambuf.cpp	2012-02-28 20:45:43 +0000
+++ src/api/transcode_streambuf.cpp	2012-03-08 10:40:24 +0000
@@ -99,5 +99,53 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 } // namespace transcode
+
+///////////////////////////////////////////////////////////////////////////////
+
+namespace internal {
+
+// Both new & delete are done here inside Zorba rather than in the header to
+// guarantee that they're cross-DLL-boundary safe on Windows.
+
+zorba::transcode::streambuf*
+alloc_streambuf( char const *charset, std::streambuf *orig ) {
+  return new zorba::transcode::streambuf( charset, orig );
+}
+
+void dealloc_streambuf( zorba::transcode::streambuf *buf ) {
+  delete buf;
+}
+
+int get_streambuf_index() {
+  //
+  // This function is out-of-line because it has a static constant within it.
+  // It has a static constant within it to guarantee (1) initialization before
+  // use and (2) initialization happens exactly once.
+  //
+  // See: "Standard C++ IOStreams and Locales: Advanced Programmer's Guide and
+  // Reference," Angelika Langer and Klaus Kreft, Addison-Wesley, 2000, section
+  // 3.3.1.1: "Initializing and Maintaining the iword/pword Index."
+  //
+  // See: "The C++ Programming Language," Bjarne Stroustrup, Addison-Wesley,
+  // 2000, section 10.4.8: "Local Static Store."
+  //
+  static int const index = ios_base::xalloc();
+  return index;
+}
+
+void stream_callback( ios_base::event e, ios_base &ios, int index ) {
+  //
+  // See: "Standard C++ IOStreams and Locales: Advanced Programmer's Guide and
+  // Reference," Angelika Langer and Klaus Kreft, Addison-Wesley, 2000, section
+  // 3.3.1.4: "Using Stream Callbacks for Memory Management."
+  //
+  if ( e == ios_base::erase_event )
+    delete static_cast<streambuf*>( ios.pword( index ) );
+}
+
+} // namespace internal
+
+///////////////////////////////////////////////////////////////////////////////
+
 } // namespace zorba
 /* vim:set et sw=2 ts=2: */

=== modified file 'src/unit_tests/test_icu_streambuf.cpp'
--- src/unit_tests/test_icu_streambuf.cpp	2012-02-28 20:45:43 +0000
+++ src/unit_tests/test_icu_streambuf.cpp	2012-03-08 10:40:24 +0000
@@ -130,6 +130,18 @@
   return ext_str == expected_ext_str;
 }
 
+static bool test_attach( test const *t ) {
+  ostringstream oss;
+  transcode::auto_attach<ostringstream> const raii( oss, t->ext_charset );
+
+  for ( char const *c = t->utf8_str; *c; ++c )
+    oss.put( *c );
+  string const ext_str( oss.str() );
+
+  string const expected_ext_str( make_ext_str( t ) );
+  return ext_str == expected_ext_str;
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 
 namespace zorba {
@@ -142,6 +154,7 @@
     ASSERT_TRUE_AND_NO_EXCEPTION( test_no, test_read( t ) );
     ASSERT_TRUE_AND_NO_EXCEPTION( test_no, test_insertion( t ) );
     ASSERT_TRUE_AND_NO_EXCEPTION( test_no, test_put( t ) );
+    ASSERT_TRUE_AND_NO_EXCEPTION( test_no, test_attach( t ) );
   }
   cout << failures << " test(s) failed\n";
   return failures ? 1 : 0;


Follow ups