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

Proposed by Chris Hillery
Status: Superseded
Proposed branch: lp:~paul-lucas/zorba/feature-transcode_streambuf-2
Merge into: lp:zorba
Diff against target: 233 lines (+183/-0)
3 files modified
include/zorba/transcode_stream.h (+122/-0)
src/api/transcode_streambuf.cpp (+48/-0)
src/unit_tests/test_icu_streambuf.cpp (+13/-0)
To merge this branch: bzr merge lp:~paul-lucas/zorba/feature-transcode_streambuf-2
Reviewer Review Type Date Requested Status
Chris Hillery Approve
Matthias Brantner Pending
Review via email: mp+96540@code.launchpad.net

This proposal has been superseded by a proposal from 2012-03-29.

Commit message

Adds new "attachable" transcode::streambufs which handle memory-management automatically.

To post a comment you must log in.
Revision history for this message
Chris Hillery (ceejatec) :
review: Approve
10713. By Paul J. Lucas

Fixed typo.

10714. By Paul J. Lucas

Merge from trunk.

Unmerged revisions

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'include/zorba/transcode_stream.h'
2--- include/zorba/transcode_stream.h 2012-02-07 03:56:23 +0000
3+++ include/zorba/transcode_stream.h 2012-03-29 03:26:21 +0000
4@@ -60,6 +60,8 @@
5 * }
6 * }
7 * \endcode
8+ * Alternatively, you may wish to use either \c attach(), \c auto_attach, or
9+ * \c transcode::stream instead.
10 *
11 * While %transcode::streambuf does support seeking, the positions are relative
12 * to the original byte stream.
13@@ -114,11 +116,131 @@
14
15 ///////////////////////////////////////////////////////////////////////////////
16
17+} // namespace transcode
18+
19+namespace internal {
20+
21+ZORBA_DLL_PUBLIC
22+zorba::transcode::streambuf*
23+alloc_streambuf( char const *charset, std::streambuf *orig );
24+
25+ZORBA_DLL_PUBLIC
26+void dealloc_streambuf( zorba::transcode::streambuf* );
27+
28+ZORBA_DLL_PUBLIC
29+int get_streambuf_index();
30+
31+ZORBA_DLL_PUBLIC
32+void stream_callback( std::ios_base::event, std::ios_base&, int index );
33+
34+} // namespace internal
35+
36+namespace transcode {
37+
38+///////////////////////////////////////////////////////////////////////////////
39+
40+/**
41+ * Attaches a transcode::streambuf to a stream. Unlike using a
42+ * transcode::streambuf directly, this function will create the streambuf,
43+ * attach it to the stream, and manage it for the lifetime of the stream
44+ * automatically.
45+ *
46+ * @param ios The stream to attach the transcode::streambuf to. If the stream
47+ * already has a transcode::streambuf attached to it, this function does
48+ * nothing.
49+ * @param charset The name of the character encoding to convert from/to.
50+ */
51+template<typename charT,typename Traits> inline
52+void attach( std::basic_ios<charT,Traits> &ios, char const *charset ) {
53+ int const index = internal::get_streambuf_index();
54+ void *&pword = ios.pword( index );
55+ if ( !pword ) {
56+ streambuf *const buf = internal::alloc_streambuf( charset, ios.rdbuf() );
57+ ios.rdbuf( buf );
58+ pword = buf;
59+ ios.register_callback( internal::stream_callback, index );
60+ }
61+}
62+
63+/**
64+ * Detaches a previously attached transcode::streambuf from a stream. The
65+ * streambuf is destroyed and the stream's original streambuf is restored.
66+ *
67+ * @param ios The stream to detach the transcode::streambuf from. If the
68+ * stream doesn't have a transcode::streambuf attached to it, this function
69+ * does nothing.
70+ */
71+template<typename charT,typename Traits> inline
72+void detach( std::basic_ios<charT,Traits> &ios ) {
73+ int const index = internal::get_streambuf_index();
74+ if ( streambuf *const buf = static_cast<streambuf*>( ios.pword( index ) ) ) {
75+ ios.pword( index ) = 0;
76+ ios.rdbuf( buf->orig_streambuf() );
77+ internal::dealloc_streambuf( buf );
78+ }
79+}
80+
81+/**
82+ * Checks whether the given stream has a transcode::streambuf attached.
83+ *
84+ * @param ios The stream to check.
85+ * @return \c true only if a transcode::streambuf is attached.
86+ */
87+template<typename charT,typename Traits> inline
88+bool is_attached( std::basic_ios<charT,Traits> &ios ) {
89+ return !!ios.pword( internal::get_streambuf_index() );
90+}
91+
92+/**
93+ * A %transcode::auto_attach is a class that attaches a transcode::streambuf to
94+ * a stream and automatically detaches it when the %auto_attach object is
95+ * destroyed.
96+ * \code
97+ * void f( ostream &os ) {
98+ * transcode::auto_attach<ostream> const raii( os, "ISO-8859-1" );
99+ * // ...
100+ * }
101+ * \endcode
102+ * A %transcode::auto_attach is useful for streams not created by you.
103+ *
104+ * @see http://en.wikipedia.org/wiki/Resource_Acquisition_Is_Initialization
105+ */
106+template<class StreamType>
107+class auto_attach {
108+public:
109+ /**
110+ * Constructs an %auto_attach object calling attach() on the given stream.
111+ *
112+ * @param stream The stream to attach the transcode::streambuf to. If the
113+ * stream already has a transcode::streambuf attached to it, this contructor
114+ * does nothing.
115+ * @param charset The name of the character encoding to convert from/to.
116+ */
117+ auto_attach( StreamType &stream, char const *charset ) : stream_( stream ) {
118+ attach( stream, charset );
119+ }
120+
121+ /**
122+ * Destroys this %auto_attach object calling detach() on the previously
123+ * attached stream.
124+ */
125+ ~auto_attach() {
126+ detach( stream_ );
127+ }
128+
129+private:
130+ StreamType &stream_;
131+};
132+
133+///////////////////////////////////////////////////////////////////////////////
134+
135 /**
136 * A %transcode::stream is used to wrap a C++ standard I/O stream with a
137 * transcode::streambuf so that transcoding and the management of the streambuf
138 * happens automatically.
139 *
140+ * A %transcode::stream is useful for streams created by you.
141+ *
142 * @tparam StreamType The I/O stream class type to wrap. It must be a concrete
143 * stream class.
144 */
145
146=== modified file 'src/api/transcode_streambuf.cpp'
147--- src/api/transcode_streambuf.cpp 2012-03-28 05:19:57 +0000
148+++ src/api/transcode_streambuf.cpp 2012-03-29 03:26:21 +0000
149@@ -99,5 +99,53 @@
150 ///////////////////////////////////////////////////////////////////////////////
151
152 } // namespace transcode
153+
154+///////////////////////////////////////////////////////////////////////////////
155+
156+namespace internal {
157+
158+// Both new & delete are done here inside Zorba rather than in the header to
159+// guarantee that they're cross-DLL-boundary safe on Windows.
160+
161+zorba::transcode::streambuf*
162+alloc_streambuf( char const *charset, std::streambuf *orig ) {
163+ return new zorba::transcode::streambuf( charset, orig );
164+}
165+
166+void dealloc_streambuf( zorba::transcode::streambuf *buf ) {
167+ delete buf;
168+}
169+
170+int get_streambuf_index() {
171+ //
172+ // This function is out-of-line because it has a static constant within it.
173+ // It has a static constant within it to guarantee (1) initialization before
174+ // use and (2) initialization happens exactly once.
175+ //
176+ // See: "Standard C++ IOStreams and Locales: Advanced Programmer's Guide and
177+ // Reference," Angelika Langer and Klaus Kreft, Addison-Wesley, 2000, section
178+ // 3.3.1.1: "Initializing and Maintaining the iword/pword Index."
179+ //
180+ // See: "The C++ Programming Language," Bjarne Stroustrup, Addison-Wesley,
181+ // 2000, section 10.4.8: "Local Static Store."
182+ //
183+ static int const index = ios_base::xalloc();
184+ return index;
185+}
186+
187+void stream_callback( ios_base::event e, ios_base &ios, int index ) {
188+ //
189+ // See: "Standard C++ IOStreams and Locales: Advanced Programmer's Guide and
190+ // Reference," Angelika Langer and Klaus Kreft, Addison-Wesley, 2000, section
191+ // 3.3.1.4: "Using Stream Callbacks for Memory Management."
192+ //
193+ if ( e == ios_base::erase_event )
194+ delete static_cast<streambuf*>( ios.pword( index ) );
195+}
196+
197+} // namespace internal
198+
199+///////////////////////////////////////////////////////////////////////////////
200+
201 } // namespace zorba
202 /* vim:set et sw=2 ts=2: */
203
204=== modified file 'src/unit_tests/test_icu_streambuf.cpp'
205--- src/unit_tests/test_icu_streambuf.cpp 2012-03-28 05:19:57 +0000
206+++ src/unit_tests/test_icu_streambuf.cpp 2012-03-29 03:26:21 +0000
207@@ -130,6 +130,18 @@
208 return ext_str == expected_ext_str;
209 }
210
211+static bool test_attach( test const *t ) {
212+ ostringstream oss;
213+ transcode::auto_attach<ostringstream> const raii( oss, t->ext_charset );
214+
215+ for ( char const *c = t->utf8_str; *c; ++c )
216+ oss.put( *c );
217+ string const ext_str( oss.str() );
218+
219+ string const expected_ext_str( make_ext_str( t ) );
220+ return ext_str == expected_ext_str;
221+}
222+
223 ///////////////////////////////////////////////////////////////////////////////
224
225 namespace zorba {
226@@ -142,6 +154,7 @@
227 ASSERT_TRUE_AND_NO_EXCEPTION( test_no, test_read( t ) );
228 ASSERT_TRUE_AND_NO_EXCEPTION( test_no, test_insertion( t ) );
229 ASSERT_TRUE_AND_NO_EXCEPTION( test_no, test_put( t ) );
230+ ASSERT_TRUE_AND_NO_EXCEPTION( test_no, test_attach( t ) );
231 }
232 cout << failures << " test(s) failed\n";
233 return failures ? 1 : 0;

Subscribers

People subscribed via source and target branches