1 | // (C) Copyright 2008 CodeRage, LLC (turkanis at coderage dot com) |
2 | // (C) Copyright 2005-2007 Jonathan Turkanis |
3 | // Distributed under the Boost Software License, Version 1.0. (See accompanying |
4 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt.) |
5 | |
6 | // See http://www.boost.org/libs/iostreams for documentation. |
7 | |
8 | // Note: bidirectional streams are not supported. |
9 | |
10 | #ifndef BOOST_IOSTREAMS_COMPOSE_HPP_INCLUDED |
11 | #define BOOST_IOSTREAMS_COMPOSE_HPP_INCLUDED |
12 | |
13 | #if defined(_MSC_VER) && (_MSC_VER >= 1020) |
14 | # pragma once |
15 | #endif |
16 | |
17 | #include <algorithm> // min. |
18 | #include <utility> // pair. |
19 | #include <boost/config.hpp> // DEDUCED_TYPENAME. |
20 | #include <boost/iostreams/categories.hpp> |
21 | #include <boost/iostreams/detail/adapter/direct_adapter.hpp> |
22 | #include <boost/iostreams/detail/call_traits.hpp> |
23 | #include <boost/iostreams/detail/enable_if_stream.hpp> |
24 | #include <boost/iostreams/detail/execute.hpp> |
25 | #include <boost/iostreams/detail/functional.hpp> |
26 | #include <boost/iostreams/operations.hpp> |
27 | #include <boost/iostreams/traits.hpp> // mode_of, is_direct. |
28 | #include <boost/mpl/if.hpp> |
29 | #include <boost/ref.hpp> |
30 | #include <boost/static_assert.hpp> |
31 | #include <boost/type_traits/is_convertible.hpp> |
32 | |
33 | // Must come last. |
34 | #include <boost/iostreams/detail/config/disable_warnings.hpp> // MSVC. |
35 | |
36 | namespace boost { namespace iostreams { |
37 | |
38 | namespace detail { |
39 | |
40 | template< typename First, |
41 | typename Second, |
42 | typename FirstMode = |
43 | BOOST_DEDUCED_TYPENAME mode_of<First>::type, |
44 | typename SecondMode = |
45 | BOOST_DEDUCED_TYPENAME mode_of<Second>::type > |
46 | struct composite_mode |
47 | : select< |
48 | is_convertible<SecondMode, FirstMode>, FirstMode, |
49 | is_convertible<FirstMode, SecondMode>, SecondMode, |
50 | is_convertible<SecondMode, input>, input, |
51 | else_, output |
52 | > |
53 | { }; |
54 | |
55 | // |
56 | // Template name: composite_device. |
57 | // Description: Provides a Device view of a Filter, Device pair. |
58 | // Template parameters: |
59 | // Filter - A model of Filter. |
60 | // Device - An indirect model of Device. |
61 | // |
62 | template< typename Filter, |
63 | typename Device, |
64 | typename Mode = |
65 | BOOST_DEDUCED_TYPENAME composite_mode<Filter, Device>::type > |
66 | class composite_device { |
67 | private: |
68 | typedef typename detail::param_type<Device>::type param_type; |
69 | typedef typename mode_of<Filter>::type filter_mode; |
70 | typedef typename mode_of<Device>::type device_mode; |
71 | typedef typename |
72 | iostreams::select< // Disambiguation for Tru64. |
73 | is_direct<Device>, direct_adapter<Device>, |
74 | is_std_io<Device>, Device&, |
75 | else_, Device |
76 | >::type value_type; |
77 | BOOST_STATIC_ASSERT(is_filter<Filter>::value); |
78 | BOOST_STATIC_ASSERT(is_device<Device>::value); |
79 | public: |
80 | typedef typename char_type_of<Filter>::type char_type; |
81 | struct category |
82 | : Mode, |
83 | device_tag, |
84 | closable_tag, |
85 | flushable_tag, |
86 | localizable_tag, |
87 | optimally_buffered_tag |
88 | { }; |
89 | composite_device(const Filter& flt, param_type dev); |
90 | std::streamsize read(char_type* s, std::streamsize n); |
91 | std::streamsize write(const char_type* s, std::streamsize n); |
92 | std::streampos seek( stream_offset off, BOOST_IOS::seekdir way, |
93 | BOOST_IOS::openmode which = |
94 | BOOST_IOS::in | BOOST_IOS::out ); |
95 | |
96 | void close(); |
97 | void close(BOOST_IOS::openmode which); |
98 | bool flush(); |
99 | std::streamsize optimal_buffer_size() const; |
100 | |
101 | template<typename Locale> // Avoid dependency on <locale> |
102 | void imbue(const Locale& loc) |
103 | { |
104 | iostreams::imbue(filter_, loc); |
105 | iostreams::imbue(device_, loc); |
106 | } |
107 | |
108 | Filter& first() { return filter_; } |
109 | Device& second() { return device_; } |
110 | private: |
111 | Filter filter_; |
112 | value_type device_; |
113 | }; |
114 | |
115 | // |
116 | // Template name: composite_device. |
117 | // Description: Provides a Device view of a Filter, Device pair. |
118 | // Template parameters: |
119 | // Filter - A model of Filter. |
120 | // Device - An indirect model of Device. |
121 | // |
122 | template< typename Filter1, |
123 | typename Filter2, |
124 | typename Mode = |
125 | BOOST_DEDUCED_TYPENAME composite_mode<Filter1, Filter2>::type > |
126 | class composite_filter { |
127 | private: |
128 | typedef reference_wrapper<Filter2> filter_ref; |
129 | typedef typename mode_of<Filter1>::type first_mode; |
130 | typedef typename mode_of<Filter2>::type second_mode; |
131 | |
132 | // A dual-use filter cannot be composed with a read-write filter |
133 | BOOST_STATIC_ASSERT( |
134 | !(is_convertible<first_mode, dual_use>::value) || |
135 | !(is_convertible<second_mode, input>::value) || |
136 | !(is_convertible<second_mode, output>::value) || |
137 | (is_convertible<second_mode, dual_use>::value) |
138 | ); |
139 | BOOST_STATIC_ASSERT( |
140 | !(is_convertible<second_mode, dual_use>::value) || |
141 | !(is_convertible<first_mode, input>::value) || |
142 | !(is_convertible<first_mode, output>::value) || |
143 | (is_convertible<first_mode, dual_use>::value) |
144 | ); |
145 | BOOST_STATIC_ASSERT(is_filter<Filter1>::value); |
146 | BOOST_STATIC_ASSERT(is_filter<Filter2>::value); |
147 | public: |
148 | typedef typename char_type_of<Filter1>::type char_type; |
149 | struct category |
150 | : Mode, |
151 | filter_tag, |
152 | multichar_tag, |
153 | closable_tag, |
154 | flushable_tag, |
155 | localizable_tag, |
156 | optimally_buffered_tag |
157 | { }; |
158 | composite_filter(const Filter1& filter1, const Filter2& filter2) |
159 | : filter1_(filter1), filter2_(filter2) |
160 | { } |
161 | |
162 | template<typename Source> |
163 | std::streamsize read(Source& src, char_type* s, std::streamsize n) |
164 | { |
165 | composite_device<filter_ref, Source> cmp(boost::ref(filter2_), src); |
166 | return iostreams::read(filter1_, cmp, s, n); |
167 | } |
168 | |
169 | template<typename Sink> |
170 | std::streamsize write(Sink& snk, const char_type* s, std::streamsize n) |
171 | { |
172 | composite_device<filter_ref, Sink> cmp(boost::ref(filter2_), snk); |
173 | return iostreams::write(filter1_, cmp, s, n); |
174 | } |
175 | |
176 | template<typename Device> |
177 | std::streampos seek( Device& dev, stream_offset off, BOOST_IOS::seekdir way, |
178 | BOOST_IOS::openmode which = |
179 | BOOST_IOS::in | BOOST_IOS::out ) |
180 | { |
181 | composite_device<filter_ref, Device> cmp(boost::ref(filter2_), dev); |
182 | return iostreams::seek(filter1_, cmp, off, way, which); |
183 | } |
184 | |
185 | template<typename Device> |
186 | void close(Device& dev) |
187 | { |
188 | BOOST_STATIC_ASSERT((!is_convertible<category, two_sequence>::value)); |
189 | BOOST_STATIC_ASSERT((!is_convertible<category, dual_use>::value)); |
190 | |
191 | // Create a new device by composing the second filter2_ with dev. |
192 | composite_device<filter_ref, Device> cmp(boost::ref(filter2_), dev); |
193 | |
194 | // Close input sequences in reverse order and output sequences in |
195 | // forward order |
196 | if (!is_convertible<first_mode, dual_use>::value) { |
197 | detail::execute_all( |
198 | detail::call_close(filter2_, dev, BOOST_IOS::in), |
199 | detail::call_close(filter1_, cmp, BOOST_IOS::in), |
200 | detail::call_close(filter1_, cmp, BOOST_IOS::out), |
201 | detail::call_close(filter2_, dev, BOOST_IOS::out) |
202 | ); |
203 | } else if (is_convertible<second_mode, input>::value) { |
204 | detail::execute_all( |
205 | detail::call_close(filter2_, dev, BOOST_IOS::in), |
206 | detail::call_close(filter1_, cmp, BOOST_IOS::in) |
207 | ); |
208 | } else { |
209 | detail::execute_all( |
210 | detail::call_close(filter1_, cmp, BOOST_IOS::out), |
211 | detail::call_close(filter2_, dev, BOOST_IOS::out) |
212 | ); |
213 | } |
214 | } |
215 | |
216 | template<typename Device> |
217 | void close(Device& dev, BOOST_IOS::openmode which) |
218 | { |
219 | BOOST_STATIC_ASSERT( |
220 | (is_convertible<category, two_sequence>::value) || |
221 | (is_convertible<category, dual_use>::value) |
222 | ); |
223 | |
224 | // Create a new device by composing the second filter2_ with dev. |
225 | composite_device<filter_ref, Device> cmp(boost::ref(filter2_), dev); |
226 | |
227 | // Close input sequences in reverse order |
228 | if ( which == BOOST_IOS::in && |
229 | ( !is_convertible<first_mode, dual_use>::value || |
230 | is_convertible<second_mode, input>::value ) ) |
231 | { |
232 | detail::execute_all( |
233 | detail::call_close(filter2_, dev, BOOST_IOS::in), |
234 | detail::call_close(filter1_, cmp, BOOST_IOS::in) |
235 | ); |
236 | } |
237 | |
238 | // Close output sequences in forward order |
239 | if ( which == BOOST_IOS::out && |
240 | ( !is_convertible<first_mode, dual_use>::value || |
241 | is_convertible<second_mode, output>::value ) ) |
242 | { |
243 | detail::execute_all( |
244 | detail::call_close(filter1_, cmp, BOOST_IOS::out), |
245 | detail::call_close(filter2_, dev, BOOST_IOS::out) |
246 | ); |
247 | } |
248 | } |
249 | |
250 | template<typename Device> |
251 | bool flush(Device& dev) |
252 | { |
253 | composite_device<Filter2, Device> cmp(filter2_, dev); |
254 | return iostreams::flush(filter1_, cmp); |
255 | } |
256 | |
257 | std::streamsize optimal_buffer_size() const |
258 | { |
259 | std::streamsize first = iostreams::optimal_buffer_size(filter1_); |
260 | std::streamsize second = iostreams::optimal_buffer_size(filter2_); |
261 | return first < second ? second : first; |
262 | } |
263 | |
264 | template<typename Locale> // Avoid dependency on <locale> |
265 | void imbue(const Locale& loc) |
266 | { // To do: consider using RAII. |
267 | iostreams::imbue(filter1_, loc); |
268 | iostreams::imbue(filter2_, loc); |
269 | } |
270 | |
271 | Filter1& first() { return filter1_; } |
272 | Filter2& second() { return filter2_; } |
273 | private: |
274 | Filter1 filter1_; |
275 | Filter2 filter2_; |
276 | }; |
277 | |
278 | template<typename Filter, typename FilterOrDevice> |
279 | struct composite_traits |
280 | : mpl::if_< |
281 | is_device<FilterOrDevice>, |
282 | composite_device<Filter, FilterOrDevice>, |
283 | composite_filter<Filter, FilterOrDevice> |
284 | > |
285 | { }; |
286 | |
287 | } // End namespace detail. |
288 | |
289 | template<typename Filter, typename FilterOrDevice> |
290 | struct composite : detail::composite_traits<Filter, FilterOrDevice>::type { |
291 | typedef typename detail::param_type<FilterOrDevice>::type param_type; |
292 | typedef typename detail::composite_traits<Filter, FilterOrDevice>::type base; |
293 | composite(const Filter& flt, param_type dev) |
294 | : base(flt, dev) |
295 | { } |
296 | }; |
297 | |
298 | //--------------Implementation of compose-------------------------------------// |
299 | |
300 | // Note: The following workarounds are patterned after resolve.hpp. It has not |
301 | // yet been confirmed that they are necessary. |
302 | |
303 | #ifndef BOOST_IOSTREAMS_BROKEN_OVERLOAD_RESOLUTION //-------------------------// |
304 | # ifndef BOOST_IOSTREAMS_NO_STREAM_TEMPLATES //-------------------------------// |
305 | |
306 | template<typename Filter, typename FilterOrDevice> |
307 | composite<Filter, FilterOrDevice> |
308 | compose( const Filter& filter, const FilterOrDevice& fod |
309 | BOOST_IOSTREAMS_DISABLE_IF_STREAM(FilterOrDevice) ) |
310 | { return composite<Filter, FilterOrDevice>(filter, fod); } |
311 | |
312 | template<typename Filter, typename Ch, typename Tr> |
313 | composite< Filter, std::basic_streambuf<Ch, Tr> > |
314 | compose(const Filter& filter, std::basic_streambuf<Ch, Tr>& sb) |
315 | { return composite< Filter, std::basic_streambuf<Ch, Tr> >(filter, sb); } |
316 | |
317 | template<typename Filter, typename Ch, typename Tr> |
318 | composite< Filter, std::basic_istream<Ch, Tr> > |
319 | compose(const Filter& filter, std::basic_istream<Ch, Tr>& is) |
320 | { return composite< Filter, std::basic_istream<Ch, Tr> >(filter, is); } |
321 | |
322 | template<typename Filter, typename Ch, typename Tr> |
323 | composite< Filter, std::basic_ostream<Ch, Tr> > |
324 | compose(const Filter& filter, std::basic_ostream<Ch, Tr>& os) |
325 | { return composite< Filter, std::basic_ostream<Ch, Tr> >(filter, os); } |
326 | |
327 | template<typename Filter, typename Ch, typename Tr> |
328 | composite< Filter, std::basic_iostream<Ch, Tr> > |
329 | compose(const Filter& filter, std::basic_iostream<Ch, Tr>& io) |
330 | { return composite< Filter, std::basic_iostream<Ch, Tr> >(filter, io); } |
331 | |
332 | # else // # ifndef BOOST_IOSTREAMS_NO_STREAM_TEMPLATES //---------------------// |
333 | |
334 | template<typename Filter, typename FilterOrDevice> |
335 | composite<Filter, FilterOrDevice> |
336 | compose( const Filter& filter, const FilterOrDevice& fod |
337 | BOOST_IOSTREAMS_DISABLE_IF_STREAM(FilterOrDevice) ) |
338 | { return composite<Filter, FilterOrDevice>(filter, fod); } |
339 | |
340 | template<typename Filter> |
341 | composite<Filter, std::streambuf> |
342 | compose(const Filter& filter, std::streambuf& sb) |
343 | { return composite<Filter, std::streambuf>(filter, sb); } |
344 | |
345 | template<typename Filter> |
346 | composite<Filter, std::istream> |
347 | compose(const Filter& filter, std::istream& is) |
348 | { return composite<Filter, std::istream>(filter, is); } |
349 | |
350 | template<typename Filter> |
351 | composite<Filter, std::ostream> |
352 | compose(const Filter& filter, std::ostream& os) |
353 | { return composite<Filter, std::ostream>(filter, os); } |
354 | |
355 | template<typename Filter> |
356 | composite<Filter, std::iostream> |
357 | compose(const Filter& filter, std::iostream& io) |
358 | { return composite<Filter, std::iostream>(filter, io); } |
359 | |
360 | # endif // # ifndef BOOST_IOSTREAMS_NO_STREAM_TEMPLATES //--------------------// |
361 | #else // #ifndef BOOST_IOSTREAMS_BROKEN_OVERLOAD_RESOLUTION //----------------// |
362 | |
363 | template<typename Filter, typename Stream> |
364 | composite<Filter, Stream> |
365 | compose(const Filter& flt, const Stream& strm, mpl::true_) |
366 | { // Bad overload resolution. |
367 | return composite<Filter, Stream>(flt, const_cast<Stream&>(strm)); |
368 | } |
369 | |
370 | template<typename Filter, typename FilterOrDevice> |
371 | composite<Filter, FilterOrDevice> |
372 | compose(const Filter& flt, const FilterOrDevice& fod, mpl::false_) |
373 | { return composite<Filter, FilterOrDevice>(flt, fod); } |
374 | |
375 | template<typename Filter, typename FilterOrDevice> |
376 | composite<Filter, FilterOrDevice> |
377 | compose( const Filter& flt, const FilterOrDevice& fod |
378 | BOOST_IOSTREAMS_DISABLE_IF_STREAM(T) ) |
379 | { return compose(flt, fod, is_std_io<FilterOrDevice>()); } |
380 | |
381 | # if !BOOST_WORKAROUND(__BORLANDC__, < 0x600) && \ |
382 | !BOOST_WORKAROUND(BOOST_MSVC, <= 1300) && \ |
383 | !defined(__GNUC__) // ---------------------------------------------------// |
384 | |
385 | template<typename Filter, typename FilterOrDevice> |
386 | composite<Filter, FilterOrDevice> |
387 | compose (const Filter& filter, FilterOrDevice& fod) |
388 | { return composite<Filter, FilterOrDevice>(filter, fod); } |
389 | |
390 | # endif // Borland 5.x, VC6-7.0 or GCC 2.9x //--------------------------------// |
391 | #endif // #ifndef BOOST_IOSTREAMS_BROKEN_OVERLOAD_RESOLUTION //---------------// |
392 | |
393 | //----------------------------------------------------------------------------// |
394 | |
395 | namespace detail { |
396 | |
397 | //--------------Implementation of composite_device---------------------------// |
398 | |
399 | template<typename Filter, typename Device, typename Mode> |
400 | composite_device<Filter, Device, Mode>::composite_device |
401 | (const Filter& flt, param_type dev) |
402 | : filter_(flt), device_(dev) |
403 | { } |
404 | |
405 | template<typename Filter, typename Device, typename Mode> |
406 | inline std::streamsize composite_device<Filter, Device, Mode>::read |
407 | (char_type* s, std::streamsize n) |
408 | { return iostreams::read(filter_, device_, s, n); } |
409 | |
410 | template<typename Filter, typename Device, typename Mode> |
411 | inline std::streamsize composite_device<Filter, Device, Mode>::write |
412 | (const char_type* s, std::streamsize n) |
413 | { return iostreams::write(filter_, device_, s, n); } |
414 | |
415 | template<typename Filter, typename Device, typename Mode> |
416 | std::streampos composite_device<Filter, Device, Mode>::seek |
417 | (stream_offset off, BOOST_IOS::seekdir way, BOOST_IOS::openmode which) |
418 | { return iostreams::seek(filter_, device_, off, way, which); } |
419 | |
420 | template<typename Filter, typename Device, typename Mode> |
421 | void composite_device<Filter, Device, Mode>::close() |
422 | { |
423 | BOOST_STATIC_ASSERT((!is_convertible<Mode, two_sequence>::value)); |
424 | BOOST_STATIC_ASSERT( |
425 | !(is_convertible<filter_mode, dual_use>::value) || |
426 | !(is_convertible<device_mode, input>::value) || |
427 | !(is_convertible<device_mode, output>::value) |
428 | ); |
429 | |
430 | // Close input sequences in reverse order and output sequences |
431 | // in forward order |
432 | if (!is_convertible<filter_mode, dual_use>::value) { |
433 | detail::execute_all( |
434 | detail::call_close(device_, BOOST_IOS::in), |
435 | detail::call_close(filter_, device_, BOOST_IOS::in), |
436 | detail::call_close(filter_, device_, BOOST_IOS::out), |
437 | detail::call_close(device_, BOOST_IOS::out) |
438 | ); |
439 | } else if (is_convertible<device_mode, input>::value) { |
440 | detail::execute_all( |
441 | detail::call_close(device_, BOOST_IOS::in), |
442 | detail::call_close(filter_, device_, BOOST_IOS::in) |
443 | ); |
444 | } else { |
445 | detail::execute_all( |
446 | detail::call_close(filter_, device_, BOOST_IOS::out), |
447 | detail::call_close(device_, BOOST_IOS::out) |
448 | ); |
449 | } |
450 | } |
451 | |
452 | template<typename Filter, typename Device, typename Mode> |
453 | void composite_device<Filter, Device, Mode>::close(BOOST_IOS::openmode which) |
454 | { |
455 | BOOST_STATIC_ASSERT((is_convertible<Mode, two_sequence>::value)); |
456 | BOOST_STATIC_ASSERT(!(is_convertible<filter_mode, dual_use>::value)); |
457 | |
458 | // Close input sequences in reverse order |
459 | if (which == BOOST_IOS::in) { |
460 | detail::execute_all( |
461 | detail::call_close(device_, BOOST_IOS::in), |
462 | detail::call_close(filter_, device_, BOOST_IOS::in) |
463 | ); |
464 | } |
465 | |
466 | // Close output sequences in forward order |
467 | if (which == BOOST_IOS::out) { |
468 | detail::execute_all( |
469 | detail::call_close(filter_, device_, BOOST_IOS::out), |
470 | detail::call_close(device_, BOOST_IOS::out) |
471 | ); |
472 | } |
473 | } |
474 | |
475 | template<typename Filter, typename Device, typename Mode> |
476 | bool composite_device<Filter, Device, Mode>::flush() |
477 | { |
478 | bool r1 = iostreams::flush(filter_, device_); |
479 | bool r2 = iostreams::flush(device_); |
480 | return r1 && r2; |
481 | } |
482 | |
483 | template<typename Filter, typename Device, typename Mode> |
484 | std::streamsize |
485 | composite_device<Filter, Device, Mode>::optimal_buffer_size() const |
486 | { return iostreams::optimal_buffer_size(device_); } |
487 | |
488 | } // End namespace detail. |
489 | |
490 | } } // End namespaces iostreams, boost. |
491 | |
492 | #include <boost/iostreams/detail/config/enable_warnings.hpp> |
493 | |
494 | #endif // #ifndef BOOST_IOSTREAMS_COMPOSE_HPP_INCLUDED |
495 | |