1 // (C) Copyright Jonathan Turkanis 2003.
2 // Distributed under the Boost Software License, Version 1.0. (See accompanying
3 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt.)
5 // See http://www.boost.org/libs/iostreams for documentation.
7 #ifndef BOOST_IOSTREAMS_DETAIL_CHAIN_HPP_INCLUDED
8 #define BOOST_IOSTREAMS_DETAIL_CHAIN_HPP_INCLUDED
10 #if defined(_MSC_VER) && (_MSC_VER >= 1020)
14 #include <algorithm> // for_each.
17 #include <functional> // unary_function.
18 #include <iterator> // advance.
20 #include <memory> // allocator, auto_ptr.
22 #include <stdexcept> // logic_error, out_of_range.
23 #include <boost/checked_delete.hpp>
24 #include <boost/config.hpp> // BOOST_MSVC, template friends,
25 #include <boost/detail/workaround.hpp> // BOOST_NESTED_TEMPLATE
26 #include <boost/iostreams/constants.hpp>
27 #include <boost/iostreams/detail/access_control.hpp>
28 #include <boost/iostreams/detail/char_traits.hpp>
29 #include <boost/iostreams/detail/push.hpp>
30 #include <boost/iostreams/detail/streambuf.hpp> // pubsync.
31 #include <boost/iostreams/detail/wrap_unwrap.hpp>
32 #include <boost/iostreams/device/null.hpp>
33 #include <boost/iostreams/positioning.hpp>
34 #include <boost/iostreams/traits.hpp> // is_filter.
35 #include <boost/iostreams/stream_buffer.hpp>
36 #include <boost/next_prior.hpp>
37 #include <boost/shared_ptr.hpp>
38 #include <boost/static_assert.hpp>
39 #include <boost/type_traits/is_convertible.hpp>
40 #include <boost/type.hpp>
41 #if BOOST_WORKAROUND(BOOST_MSVC, < 1310)
42 # include <boost/mpl/int.hpp>
45 // Sometimes type_info objects must be compared by name. Borrowed from
46 // Boost.Python and Boost.Function.
47 #if (defined(__GNUC__) && __GNUC__ >= 3) || \
49 (defined(__sgi) && defined(__host_mips)) || \
50 (defined(linux) && defined(__INTEL_COMPILER) && defined(__ICC)) \
53 # define BOOST_IOSTREAMS_COMPARE_TYPE_ID(X,Y) \
54 (std::strcmp((X).name(),(Y).name()) == 0)
56 # define BOOST_IOSTREAMS_COMPARE_TYPE_ID(X,Y) ((X)==(Y))
60 #define BOOST_IOSTREAMS_COMPONENT_TYPE(chain, index) \
61 chain.component_type( index ) \
64 #if !BOOST_WORKAROUND(BOOST_MSVC, < 1310)
65 # define BOOST_IOSTREAMS_COMPONENT(chain, index, target) \
66 chain.component< target >( index ) \
69 # define BOOST_IOSTREAMS_COMPONENT(chain, index, target) \
70 chain.component( index, ::boost::type< target >() ) \
74 namespace boost { namespace iostreams {
76 //--------------Definition of chain and wchain--------------------------------//
80 template<typename Chain> class chain_client;
83 // Concept name: Chain.
84 // Description: Represents a chain of stream buffers which provides access
85 // to the first buffer in the chain and send notifications when the
86 // streambufs are added to or removed from chain.
87 // Refines: Closable device with mode equal to typename Chain::mode.
88 // Models: chain, converting_chain.
93 // typedef xxx chain_type;
94 // typedef xxx client_type;
96 // bool is_complete() const; // Ready for i/o.
97 // template<typename T>
98 // void push( const T& t, // Adds a stream buffer to
99 // streamsize, // chain, based on t, with
100 // streamsize ); // given buffer and putback
101 // // buffer sizes. Pass -1 to
102 // // request default size.
104 // void register_client(client_type* client); // Associate client.
105 // void notify(); // Notify client.
110 // Description: Represents a chain of filters with an optional device at the
112 // Template parameters:
113 // Self - A class deriving from the current instantiation of this template.
114 // This is an example of the Curiously Recurring Template Pattern.
115 // Ch - The character type.
116 // Tr - The character traits type.
117 // Alloc - The allocator type.
118 // Mode - A mode tag.
120 template<typename Self, typename Ch, typename Tr, typename Alloc, typename Mode>
123 typedef Ch char_type;
124 BOOST_IOSTREAMS_STREAMBUF_TYPEDEFS(Tr)
125 typedef Alloc allocator_type;
131 typedef chain_client<Self> client_type;
132 friend class chain_client<Self>;
134 typedef linked_streambuf<Ch> streambuf_type;
135 typedef std::list<streambuf_type*> list_type;
136 typedef chain_base<Self, Ch, Tr, Alloc, Mode> my_type;
138 chain_base() : pimpl_(new chain_impl) { }
139 chain_base(const chain_base& rhs): pimpl_(rhs.pimpl_) { }
142 //----------Buffer sizing-------------------------------------------------//
144 // Sets the size of the buffer created for the devices to be added to this
145 // chain. Does not affect the size of the buffer for devices already
147 void set_device_buffer_size(int n) { pimpl_->device_buffer_size_ = n; }
149 // Sets the size of the buffer created for the filters to be added
150 // to this chain. Does not affect the size of the buffer for filters already
152 void set_filter_buffer_size(int n) { pimpl_->filter_buffer_size_ = n; }
154 // Sets the size of the putback buffer for filters and devices to be added
155 // to this chain. Does not affect the size of the buffer for filters or
156 // devices already added.
157 void set_pback_size(int n) { pimpl_->pback_size_ = n; }
159 //----------Device interface----------------------------------------------//
161 std::streamsize read(char_type* s, std::streamsize n);
162 std::streamsize write(const char_type* s, std::streamsize n);
163 std::streampos seek(stream_offset off, BOOST_IOS::seekdir way);
165 //----------Additional i/o functions--------------------------------------//
167 // Returns true if this chain is non-empty and its final link
168 // is a source or sink, i.e., if it is ready to perform i/o.
169 bool is_complete() const;
170 bool auto_close() const;
171 void set_auto_close(bool close);
172 bool sync() { return front().BOOST_IOSTREAMS_PUBSYNC() != -1; }
175 //----------Container-like interface--------------------------------------//
177 typedef typename list_type::size_type size_type;
178 streambuf_type& front() { return *list().front(); }
179 BOOST_IOSTREAMS_DEFINE_PUSH(push, mode, char_type, push_impl)
181 bool empty() const { return list().empty(); }
182 size_type size() const { return list().size(); }
185 //----------Direct component access---------------------------------------//
187 const std::type_info& component_type(int n) const
189 if (static_cast<size_type>(n) >= size())
190 throw std::out_of_range("bad chain offset");
191 return (*boost::next(list().begin(), n))->component_type();
194 #if !BOOST_WORKAROUND(BOOST_MSVC, < 1310)
197 const std::type_info& component_type() const { return component_type(N); }
200 T* component(int n) const { return component(n, boost::type<T>()); }
203 template<int N, typename T>
204 T* component() const { return component<T>(N); }
207 #if !BOOST_WORKAROUND(BOOST_MSVC, < 1310)
211 T* component(int n, boost::type<T>) const
213 if (static_cast<size_type>(n) >= size())
214 throw std::out_of_range("bad chain offset");
215 streambuf_type* link = *boost::next(list().begin(), n);
216 if (BOOST_IOSTREAMS_COMPARE_TYPE_ID(link->component_type(), typeid(T)))
217 return static_cast<T*>(link->component_impl());
223 void push_impl(const T& t, int buffer_size = -1, int pback_size = -1)
225 typedef typename iostreams::category_of<T>::type category;
226 typedef typename unwrap_ios<T>::type policy_type;
227 typedef stream_buffer<
229 BOOST_IOSTREAMS_CHAR_TRAITS(char_type),
232 BOOST_STATIC_ASSERT((is_convertible<category, Mode>::value));
234 throw std::logic_error("chain complete");
235 streambuf_type* prev = !empty() ? list().back() : 0;
239 iostreams::optimal_buffer_size(t);
244 std::auto_ptr<facade_type>
245 buf(new facade_type(t, buffer_size, pback_size));
246 list().push_back(buf.get());
248 if (is_device<policy_type>::value)
249 pimpl_->flags_ |= f_complete | f_open;
250 if (prev) prev->set_next(list().back());
254 list_type& list() { return pimpl_->links_; }
255 const list_type& list() const { return pimpl_->links_; }
256 void register_client(client_type* client) { pimpl_->client_ = client; }
257 void notify() { if (pimpl_->client_) pimpl_->client_->notify(); }
259 //----------Nested classes------------------------------------------------//
261 static void close(streambuf_type* b, BOOST_IOS::openmode m)
263 if (m & BOOST_IOS::out)
264 b->BOOST_IOSTREAMS_PUBSYNC();
268 static void set_next(streambuf_type* b, streambuf_type* next)
269 { b->set_next(next); }
271 static void set_auto_close(streambuf_type* b, bool close)
272 { b->set_auto_close(close); }
274 struct closer : public std::unary_function<streambuf_type*, void> {
275 closer(BOOST_IOS::openmode m) : mode_(m) { }
276 void operator() (streambuf_type* b)
280 BOOST_IOS::openmode mode_;
282 friend struct closer;
292 : client_(0), device_buffer_size_(default_device_buffer_size),
293 filter_buffer_size_(default_filter_buffer_size),
294 pback_size_(default_pback_buffer_size),
297 ~chain_impl() { try { close(); reset(); } catch (std::exception&) { } }
300 if ((flags_ & f_open) != 0) {
301 stream_buffer< basic_null_device<Ch, Mode> > null;
302 if ((flags_ & f_complete) == 0) {
303 null.open(basic_null_device<Ch, Mode>());
304 set_next(links_.back(), &null);
306 links_.front()->BOOST_IOSTREAMS_PUBSYNC();
307 if (is_convertible<Mode, input>::value)
308 std::for_each( links_.rbegin(), links_.rend(),
309 closer(BOOST_IOS::in) );
310 if (is_convertible<Mode, output>::value)
311 std::for_each( links_.begin(), links_.end(),
312 closer(BOOST_IOS::out) );
318 typedef typename list_type::iterator iterator;
319 for ( iterator first = links_.begin(),
324 if ( (flags_ & f_complete) == 0 ||
325 (flags_ & f_auto_close) == 0 )
327 set_auto_close(*first, false);
329 streambuf_type* buf = 0;
330 std::swap(buf, *first);
334 flags_ &= ~f_complete;
338 client_type* client_;
339 int device_buffer_size_,
344 friend struct chain_impl;
346 //----------Member data---------------------------------------------------//
349 shared_ptr<chain_impl> pimpl_;
352 } // End namespace detail.
355 // Macro: BOOST_IOSTREAMS_DECL_CHAIN(name, category)
356 // Description: Defines a template derived from chain_base appropriate for a
357 // particular i/o category. The template has the following parameters:
358 // Ch - The character type.
359 // Tr - The character traits type.
360 // Alloc - The allocator type.
362 // name_ - The name of the template to be defined.
363 // category_ - The i/o category of the template to be defined.
365 #define BOOST_IOSTREAMS_DECL_CHAIN(name_, default_char_) \
366 template< typename Mode, typename Ch = default_char_, \
367 typename Tr = BOOST_IOSTREAMS_CHAR_TRAITS(Ch), \
368 typename Alloc = std::allocator<Ch> > \
369 class name_ : public boost::iostreams::detail::chain_base< \
370 name_<Mode, Ch, Tr, Alloc>, \
371 Ch, Tr, Alloc, Mode \
375 struct category : device_tag, Mode { }; \
378 typedef boost::iostreams::detail::chain_base< \
379 name_<Mode, Ch, Tr, Alloc>, \
380 Ch, Tr, Alloc, Mode \
383 typedef Ch char_type; \
384 typedef Tr traits_type; \
385 typedef typename traits_type::int_type int_type; \
386 typedef typename traits_type::off_type off_type; \
388 name_(const name_& rhs) { *this = rhs; } \
389 name_& operator=(const name_& rhs) \
390 { base_type::operator=(rhs); return *this; } \
393 BOOST_IOSTREAMS_DECL_CHAIN(chain, char)
394 BOOST_IOSTREAMS_DECL_CHAIN(wchain, wchar_t)
395 #undef BOOST_IOSTREAMS_DECL_CHAIN
397 //--------------Definition of chain_client------------------------------------//
402 // Template name: chain_client
403 // Description: Class whose instances provide access to an underlying chain
404 // using an interface similar to the chains.
405 // Subclasses: the various stream and stream buffer templates.
407 template<typename Chain>
410 typedef Chain chain_type;
411 typedef typename chain_type::char_type char_type;
412 typedef typename chain_type::traits_type traits_type;
413 typedef typename chain_type::size_type size_type;
414 typedef typename chain_type::mode mode;
416 chain_client(chain_type* chn = 0) : chain_(chn ) { }
417 chain_client(chain_client* client) : chain_(client->chain_) { }
418 virtual ~chain_client() { }
420 const std::type_info& component_type(int n) const
421 { return chain_->component_type(n); }
423 //#if !BOOST_WORKAROUND(BOOST_MSVC, < 1310)
426 // const std::type_info& component_type() const
427 // { return chain_->component_type(N); }
429 // template<typename T>
430 // T* component(int n) const // Tru64 needs boost::type.
431 // { return chain_->component(n, boost::type<T>()); }
434 // template<int N, typename T>
435 // T* component() const // Tru64 needs boost::type.
436 // { return chain_->component(N, boost::type<T>()); }
438 // template<typename T>
439 // T* component(int n, boost::type<T> t) const
440 // { return chain_->component(n, t); }
443 #if !BOOST_WORKAROUND(BOOST_MSVC, < 1310)
446 const std::type_info& component_type() const
447 { return chain_->BOOST_NESTED_TEMPLATE component_type<N>(); }
450 T* component(int n) const
451 { return chain_->BOOST_NESTED_TEMPLATE component<T>(n); }
454 template<int N, typename T>
456 { return chain_->BOOST_NESTED_TEMPLATE component<N, T>(); }
459 T* component(int n, boost::type<T> t) const
460 { return chain_->component(n, t); }
463 bool is_complete() const { return chain_->is_complete(); }
464 bool auto_close() const { return chain_->auto_close(); }
465 void set_auto_close(bool close) { chain_->set_auto_close(close); }
466 bool strict_sync() { return chain_->strict_sync(); }
467 void set_device_buffer_size(std::streamsize n)
468 { chain_->set_device_buffer_size(n); }
469 void set_filter_buffer_size(std::streamsize n)
470 { chain_->set_filter_buffer_size(n); }
471 void set_pback_size(std::streamsize n) { chain_->set_pback_size(n); }
472 BOOST_IOSTREAMS_DEFINE_PUSH(push, mode, char_type, push_impl)
473 void pop() { chain_->pop(); }
474 bool empty() const { return chain_->empty(); }
475 size_type size() { return chain_->size(); }
476 void reset() { chain_->reset(); }
478 // Returns a copy of the underlying chain.
479 chain_type filters() { return *chain_; }
480 chain_type filters() const { return *chain_; }
483 void push_impl(const T& t BOOST_IOSTREAMS_PUSH_PARAMS())
484 { chain_->push(t BOOST_IOSTREAMS_PUSH_ARGS()); }
485 chain_type& ref() { return *chain_; }
486 void set_chain(chain_type* c)
487 { chain_ = c; chain_->register_client(this); }
488 #if !defined(BOOST_NO_MEMBER_TEMPLATE_FRIENDS) && \
489 (!BOOST_WORKAROUND(__BORLANDC__, < 0x600))
490 template<typename S, typename C, typename T, typename A, typename M>
491 friend class chain_base;
495 virtual void notify() { }
500 //--------------Implementation of chain_base----------------------------------//
502 template<typename Self, typename Ch, typename Tr, typename Alloc, typename Mode>
503 inline std::streamsize chain_base<Self, Ch, Tr, Alloc, Mode>::read
504 (char_type* s, std::streamsize n)
505 { return iostreams::read(*list().front(), s, n); }
507 template<typename Self, typename Ch, typename Tr, typename Alloc, typename Mode>
508 inline std::streamsize chain_base<Self, Ch, Tr, Alloc, Mode>::write
509 (const char_type* s, std::streamsize n)
510 { return iostreams::write(*list().front(), s, n); }
512 template<typename Self, typename Ch, typename Tr, typename Alloc, typename Mode>
513 inline std::streampos chain_base<Self, Ch, Tr, Alloc, Mode>::seek
514 (stream_offset off, BOOST_IOS::seekdir way)
515 { return iostreams::seek(*list().front(), off, way); }
517 template<typename Self, typename Ch, typename Tr, typename Alloc, typename Mode>
518 void chain_base<Self, Ch, Tr, Alloc, Mode>::reset()
525 template<typename Self, typename Ch, typename Tr, typename Alloc, typename Mode>
526 bool chain_base<Self, Ch, Tr, Alloc, Mode>::is_complete() const
528 return (pimpl_->flags_ & f_complete) != 0;
531 template<typename Self, typename Ch, typename Tr, typename Alloc, typename Mode>
532 bool chain_base<Self, Ch, Tr, Alloc, Mode>::auto_close() const
534 return (pimpl_->flags_ & f_auto_close) != 0;
537 template<typename Self, typename Ch, typename Tr, typename Alloc, typename Mode>
538 void chain_base<Self, Ch, Tr, Alloc, Mode>::set_auto_close(bool close)
541 (pimpl_->flags_ & ~f_auto_close) |
542 (close ? f_auto_close : 0);
545 template<typename Self, typename Ch, typename Tr, typename Alloc, typename Mode>
546 bool chain_base<Self, Ch, Tr, Alloc, Mode>::strict_sync()
548 typedef typename list_type::iterator iterator;
550 for ( iterator first = list().begin(),
555 bool s = (*first)->strict_sync();
556 result = result && s;
561 template<typename Self, typename Ch, typename Tr, typename Alloc, typename Mode>
562 void chain_base<Self, Ch, Tr, Alloc, Mode>::pop()
567 streambuf_type* buf = 0;
568 std::swap(buf, list().back());
569 buf->set_auto_close(false);
573 pimpl_->flags_ &= ~f_complete;
574 if (auto_close() || list().empty())
575 pimpl_->flags_ &= ~f_open;
578 } // End namespace detail.
580 } } // End namespaces iostreams, boost.
582 #endif // #ifndef BOOST_IOSTREAMS_DETAIL_CHAIN_HPP_INCLUDED