zorba-coders team mailing list archive
-
zorba-coders team
-
Mailing list archive
-
Message #06054
[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