Chris@16: // (C) Copyright 2008 CodeRage, LLC (turkanis at coderage dot com) Chris@16: // (C) Copyright 2003-2007 Jonathan Turkanis Chris@16: // Distributed under the Boost Software License, Version 1.0. (See accompanying Chris@16: // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt.) Chris@16: Chris@16: // See http://www.boost.org/libs/iostreams for documentation. Chris@16: Chris@16: #ifndef BOOST_IOSTREAMS_DETAIL_CHAIN_HPP_INCLUDED Chris@16: #define BOOST_IOSTREAMS_DETAIL_CHAIN_HPP_INCLUDED Chris@16: Chris@16: #if defined(_MSC_VER) && (_MSC_VER >= 1020) Chris@16: # pragma once Chris@16: #endif Chris@16: Chris@16: #include Chris@16: #include Chris@16: #include // unary_function. Chris@16: #include // advance. Chris@16: #include Chris@16: #include // allocator, auto_ptr. Chris@16: #include Chris@16: #include // logic_error, out_of_range. Chris@16: #include Chris@16: #include // BOOST_MSVC, template friends, Chris@16: #include // BOOST_NESTED_TEMPLATE Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include // pubsync. Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include // is_filter. Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include // VC6.5 requires this Chris@16: #if BOOST_WORKAROUND(BOOST_MSVC, < 1310) // #include order Chris@16: # include Chris@16: #endif Chris@16: Chris@16: // Sometimes type_info objects must be compared by name. Borrowed from Chris@16: // Boost.Python and Boost.Function. Chris@16: #if (defined(__GNUC__) && __GNUC__ >= 3) || \ Chris@16: defined(_AIX) || \ Chris@16: (defined(__sgi) && defined(__host_mips)) || \ Chris@16: (defined(linux) && defined(__INTEL_COMPILER) && defined(__ICC)) \ Chris@16: /**/ Chris@16: # include Chris@16: # define BOOST_IOSTREAMS_COMPARE_TYPE_ID(X,Y) \ Chris@16: (std::strcmp((X).name(),(Y).name()) == 0) Chris@16: #else Chris@16: # define BOOST_IOSTREAMS_COMPARE_TYPE_ID(X,Y) ((X)==(Y)) Chris@16: #endif Chris@16: Chris@16: // Deprecated Chris@16: #define BOOST_IOSTREAMS_COMPONENT_TYPE(chain, index) \ Chris@16: chain.component_type( index ) \ Chris@16: /**/ Chris@16: Chris@16: #if !BOOST_WORKAROUND(BOOST_MSVC, < 1310) Chris@16: # define BOOST_IOSTREAMS_COMPONENT(chain, index, target) \ Chris@16: chain.component< target >( index ) \ Chris@16: /**/ Chris@16: #else Chris@16: # define BOOST_IOSTREAMS_COMPONENT(chain, index, target) \ Chris@16: chain.component( index, ::boost::type< target >() ) \ Chris@16: /**/ Chris@16: #endif Chris@16: Chris@16: namespace boost { namespace iostreams { Chris@16: Chris@16: //--------------Definition of chain and wchain--------------------------------// Chris@16: Chris@16: namespace detail { Chris@16: Chris@16: template class chain_client; Chris@16: Chris@16: // Chris@16: // Concept name: Chain. Chris@16: // Description: Represents a chain of stream buffers which provides access Chris@16: // to the first buffer in the chain and sends notifications when the Chris@16: // streambufs are added to or removed from chain. Chris@16: // Refines: Closable device with mode equal to typename Chain::mode. Chris@16: // Models: chain, converting_chain. Chris@16: // Example: Chris@16: // Chris@16: // class chain { Chris@16: // public: Chris@16: // typedef xxx chain_type; Chris@16: // typedef xxx client_type; Chris@16: // typedef xxx mode; Chris@16: // bool is_complete() const; // Ready for i/o. Chris@16: // template Chris@16: // void push( const T& t, // Adds a stream buffer to Chris@16: // streamsize, // chain, based on t, with Chris@16: // streamsize ); // given buffer and putback Chris@16: // // buffer sizes. Pass -1 to Chris@16: // // request default size. Chris@16: // protected: Chris@16: // void register_client(client_type* client); // Associate client. Chris@16: // void notify(); // Notify client. Chris@16: // }; Chris@16: // Chris@16: Chris@16: // Chris@16: // Description: Represents a chain of filters with an optional device at the Chris@16: // end. Chris@16: // Template parameters: Chris@16: // Self - A class deriving from the current instantiation of this template. Chris@16: // This is an example of the Curiously Recurring Template Pattern. Chris@16: // Ch - The character type. Chris@16: // Tr - The character traits type. Chris@16: // Alloc - The allocator type. Chris@16: // Mode - A mode tag. Chris@16: // Chris@16: template Chris@16: class chain_base { Chris@16: public: Chris@16: typedef Ch char_type; Chris@16: BOOST_IOSTREAMS_STREAMBUF_TYPEDEFS(Tr) Chris@16: typedef Alloc allocator_type; Chris@16: typedef Mode mode; Chris@16: struct category Chris@16: : Mode, Chris@16: device_tag Chris@16: { }; Chris@16: typedef chain_client client_type; Chris@16: friend class chain_client; Chris@16: private: Chris@16: typedef linked_streambuf streambuf_type; Chris@16: typedef std::list list_type; Chris@16: typedef chain_base my_type; Chris@16: protected: Chris@16: chain_base() : pimpl_(new chain_impl) { } Chris@16: chain_base(const chain_base& rhs): pimpl_(rhs.pimpl_) { } Chris@16: public: Chris@16: Chris@16: // dual_use is a pseudo-mode to facilitate filter writing, Chris@16: // not a genuine mode. Chris@16: BOOST_STATIC_ASSERT((!is_convertible::value)); Chris@16: Chris@16: //----------Buffer sizing-------------------------------------------------// Chris@16: Chris@16: // Sets the size of the buffer created for the devices to be added to this Chris@16: // chain. Does not affect the size of the buffer for devices already Chris@16: // added. Chris@16: void set_device_buffer_size(std::streamsize n) Chris@16: { pimpl_->device_buffer_size_ = n; } Chris@16: Chris@16: // Sets the size of the buffer created for the filters to be added Chris@16: // to this chain. Does not affect the size of the buffer for filters already Chris@16: // added. Chris@16: void set_filter_buffer_size(std::streamsize n) Chris@16: { pimpl_->filter_buffer_size_ = n; } Chris@16: Chris@16: // Sets the size of the putback buffer for filters and devices to be added Chris@16: // to this chain. Does not affect the size of the buffer for filters or Chris@16: // devices already added. Chris@16: void set_pback_size(std::streamsize n) Chris@16: { pimpl_->pback_size_ = n; } Chris@16: Chris@16: //----------Device interface----------------------------------------------// Chris@16: Chris@16: std::streamsize read(char_type* s, std::streamsize n); Chris@16: std::streamsize write(const char_type* s, std::streamsize n); Chris@16: std::streampos seek(stream_offset off, BOOST_IOS::seekdir way); Chris@16: Chris@16: //----------Direct component access---------------------------------------// Chris@16: Chris@16: const std::type_info& component_type(int n) const Chris@16: { Chris@16: if (static_cast(n) >= size()) Chris@16: boost::throw_exception(std::out_of_range("bad chain offset")); Chris@16: return (*boost::next(list().begin(), n))->component_type(); Chris@16: } Chris@16: Chris@16: #if !BOOST_WORKAROUND(BOOST_MSVC, < 1310) Chris@16: // Deprecated. Chris@16: template Chris@16: const std::type_info& component_type() const { return component_type(N); } Chris@16: Chris@16: template Chris@16: T* component(int n) const { return component(n, boost::type()); } Chris@16: Chris@16: // Deprecated. Chris@16: template Chris@16: T* component() const { return component(N); } Chris@16: #endif Chris@16: Chris@16: #if !BOOST_WORKAROUND(BOOST_MSVC, < 1310) Chris@16: private: Chris@16: #endif Chris@16: template Chris@16: T* component(int n, boost::type) const Chris@16: { Chris@16: if (static_cast(n) >= size()) Chris@16: boost::throw_exception(std::out_of_range("bad chain offset")); Chris@16: streambuf_type* link = *boost::next(list().begin(), n); Chris@16: if (BOOST_IOSTREAMS_COMPARE_TYPE_ID(link->component_type(), typeid(T))) Chris@16: return static_cast(link->component_impl()); Chris@16: else Chris@16: return 0; Chris@16: } Chris@16: public: Chris@16: Chris@16: //----------Container-like interface--------------------------------------// Chris@16: Chris@16: typedef typename list_type::size_type size_type; Chris@16: streambuf_type& front() { return *list().front(); } Chris@16: BOOST_IOSTREAMS_DEFINE_PUSH(push, mode, char_type, push_impl) Chris@16: void pop(); Chris@16: bool empty() const { return list().empty(); } Chris@16: size_type size() const { return list().size(); } Chris@16: void reset(); Chris@16: Chris@16: //----------Additional i/o functions--------------------------------------// Chris@16: Chris@16: // Returns true if this chain is non-empty and its final link Chris@16: // is a source or sink, i.e., if it is ready to perform i/o. Chris@16: bool is_complete() const; Chris@16: bool auto_close() const; Chris@16: void set_auto_close(bool close); Chris@16: bool sync() { return front().BOOST_IOSTREAMS_PUBSYNC() != -1; } Chris@16: bool strict_sync(); Chris@16: private: Chris@16: template Chris@16: void push_impl(const T& t, std::streamsize buffer_size = -1, Chris@16: std::streamsize pback_size = -1) Chris@16: { Chris@16: typedef typename iostreams::category_of::type category; Chris@16: typedef typename unwrap_ios::type component_type; Chris@16: typedef stream_buffer< Chris@16: component_type, Chris@16: BOOST_IOSTREAMS_CHAR_TRAITS(char_type), Chris@16: Alloc, Mode Chris@16: > streambuf_t; Chris@16: typedef typename list_type::iterator iterator; Chris@16: BOOST_STATIC_ASSERT((is_convertible::value)); Chris@16: if (is_complete()) Chris@16: boost::throw_exception(std::logic_error("chain complete")); Chris@16: streambuf_type* prev = !empty() ? list().back() : 0; Chris@16: buffer_size = Chris@16: buffer_size != -1 ? Chris@16: buffer_size : Chris@16: iostreams::optimal_buffer_size(t); Chris@16: pback_size = Chris@16: pback_size != -1 ? Chris@16: pback_size : Chris@16: pimpl_->pback_size_; Chris@16: std::auto_ptr Chris@16: buf(new streambuf_t(t, buffer_size, pback_size)); Chris@16: list().push_back(buf.get()); Chris@16: buf.release(); Chris@16: if (is_device::value) { Chris@16: pimpl_->flags_ |= f_complete | f_open; Chris@16: for ( iterator first = list().begin(), Chris@16: last = list().end(); Chris@16: first != last; Chris@16: ++first ) Chris@16: { Chris@16: (*first)->set_needs_close(); Chris@16: } Chris@16: } Chris@16: if (prev) prev->set_next(list().back()); Chris@16: notify(); Chris@16: } Chris@16: Chris@16: list_type& list() { return pimpl_->links_; } Chris@16: const list_type& list() const { return pimpl_->links_; } Chris@16: void register_client(client_type* client) { pimpl_->client_ = client; } Chris@16: void notify() { if (pimpl_->client_) pimpl_->client_->notify(); } Chris@16: Chris@16: //----------Nested classes------------------------------------------------// Chris@16: Chris@16: static void close(streambuf_type* b, BOOST_IOS::openmode m) Chris@16: { Chris@16: if (m == BOOST_IOS::out && is_convertible::value) Chris@16: b->BOOST_IOSTREAMS_PUBSYNC(); Chris@16: b->close(m); Chris@16: } Chris@16: Chris@16: static void set_next(streambuf_type* b, streambuf_type* next) Chris@16: { b->set_next(next); } Chris@16: Chris@16: static void set_auto_close(streambuf_type* b, bool close) Chris@16: { b->set_auto_close(close); } Chris@16: Chris@16: struct closer : public std::unary_function { Chris@16: closer(BOOST_IOS::openmode m) : mode_(m) { } Chris@16: void operator() (streambuf_type* b) Chris@16: { Chris@16: close(b, mode_); Chris@16: } Chris@16: BOOST_IOS::openmode mode_; Chris@16: }; Chris@16: friend struct closer; Chris@16: Chris@16: enum flags { Chris@16: f_complete = 1, Chris@16: f_open = 2, Chris@16: f_auto_close = 4 Chris@16: }; Chris@16: Chris@16: struct chain_impl { Chris@16: chain_impl() Chris@16: : client_(0), device_buffer_size_(default_device_buffer_size), Chris@16: filter_buffer_size_(default_filter_buffer_size), Chris@16: pback_size_(default_pback_buffer_size), Chris@16: flags_(f_auto_close) Chris@16: { } Chris@16: ~chain_impl() Chris@16: { Chris@16: try { close(); } catch (...) { } Chris@16: try { reset(); } catch (...) { } Chris@16: } Chris@16: void close() Chris@16: { Chris@16: if ((flags_ & f_open) != 0) { Chris@16: flags_ &= ~f_open; Chris@16: stream_buffer< basic_null_device > null; Chris@16: if ((flags_ & f_complete) == 0) { Chris@16: null.open(basic_null_device()); Chris@16: set_next(links_.back(), &null); Chris@16: } Chris@16: links_.front()->BOOST_IOSTREAMS_PUBSYNC(); Chris@16: try { Chris@16: boost::iostreams::detail::execute_foreach( Chris@16: links_.rbegin(), links_.rend(), Chris@16: closer(BOOST_IOS::in) Chris@16: ); Chris@16: } catch (...) { Chris@16: try { Chris@16: boost::iostreams::detail::execute_foreach( Chris@16: links_.begin(), links_.end(), Chris@16: closer(BOOST_IOS::out) Chris@16: ); Chris@16: } catch (...) { } Chris@16: throw; Chris@16: } Chris@16: boost::iostreams::detail::execute_foreach( Chris@16: links_.begin(), links_.end(), Chris@16: closer(BOOST_IOS::out) Chris@16: ); Chris@16: } Chris@16: } Chris@16: void reset() Chris@16: { Chris@16: typedef typename list_type::iterator iterator; Chris@16: for ( iterator first = links_.begin(), Chris@16: last = links_.end(); Chris@16: first != last; Chris@16: ++first ) Chris@16: { Chris@16: if ( (flags_ & f_complete) == 0 || Chris@16: (flags_ & f_auto_close) == 0 ) Chris@16: { Chris@16: set_auto_close(*first, false); Chris@16: } Chris@16: streambuf_type* buf = 0; Chris@16: std::swap(buf, *first); Chris@16: delete buf; Chris@16: } Chris@16: links_.clear(); Chris@16: flags_ &= ~f_complete; Chris@16: flags_ &= ~f_open; Chris@16: } Chris@16: list_type links_; Chris@16: client_type* client_; Chris@16: std::streamsize device_buffer_size_, Chris@16: filter_buffer_size_, Chris@16: pback_size_; Chris@16: int flags_; Chris@16: }; Chris@16: friend struct chain_impl; Chris@16: Chris@16: //----------Member data---------------------------------------------------// Chris@16: Chris@16: private: Chris@16: shared_ptr pimpl_; Chris@16: }; Chris@16: Chris@16: } // End namespace detail. Chris@16: Chris@16: // Chris@16: // Macro: BOOST_IOSTREAMS_DECL_CHAIN(name, category) Chris@16: // Description: Defines a template derived from chain_base appropriate for a Chris@16: // particular i/o category. The template has the following parameters: Chris@16: // Ch - The character type. Chris@16: // Tr - The character traits type. Chris@16: // Alloc - The allocator type. Chris@16: // Macro parameters: Chris@16: // name_ - The name of the template to be defined. Chris@16: // category_ - The i/o category of the template to be defined. Chris@16: // Chris@16: #define BOOST_IOSTREAMS_DECL_CHAIN(name_, default_char_) \ Chris@16: template< typename Mode, typename Ch = default_char_, \ Chris@16: typename Tr = BOOST_IOSTREAMS_CHAR_TRAITS(Ch), \ Chris@16: typename Alloc = std::allocator > \ Chris@16: class name_ : public boost::iostreams::detail::chain_base< \ Chris@16: name_, \ Chris@16: Ch, Tr, Alloc, Mode \ Chris@16: > \ Chris@16: { \ Chris@16: public: \ Chris@16: struct category : device_tag, Mode { }; \ Chris@16: typedef Mode mode; \ Chris@16: private: \ Chris@16: typedef boost::iostreams::detail::chain_base< \ Chris@16: name_, \ Chris@16: Ch, Tr, Alloc, Mode \ Chris@16: > base_type; \ Chris@16: public: \ Chris@16: typedef Ch char_type; \ Chris@16: typedef Tr traits_type; \ Chris@16: typedef typename traits_type::int_type int_type; \ Chris@16: typedef typename traits_type::off_type off_type; \ Chris@16: name_() { } \ Chris@16: name_(const name_& rhs) : base_type(rhs) { } \ Chris@16: name_& operator=(const name_& rhs) \ Chris@16: { base_type::operator=(rhs); return *this; } \ Chris@16: }; \ Chris@16: /**/ Chris@16: BOOST_IOSTREAMS_DECL_CHAIN(chain, char) Chris@16: BOOST_IOSTREAMS_DECL_CHAIN(wchain, wchar_t) Chris@16: #undef BOOST_IOSTREAMS_DECL_CHAIN Chris@16: Chris@16: //--------------Definition of chain_client------------------------------------// Chris@16: Chris@16: namespace detail { Chris@16: Chris@16: // Chris@16: // Template name: chain_client Chris@16: // Description: Class whose instances provide access to an underlying chain Chris@16: // using an interface similar to the chains. Chris@16: // Subclasses: the various stream and stream buffer templates. Chris@16: // Chris@16: template Chris@16: class chain_client { Chris@16: public: Chris@16: typedef Chain chain_type; Chris@16: typedef typename chain_type::char_type char_type; Chris@16: typedef typename chain_type::traits_type traits_type; Chris@16: typedef typename chain_type::size_type size_type; Chris@16: typedef typename chain_type::mode mode; Chris@16: Chris@16: chain_client(chain_type* chn = 0) : chain_(chn ) { } Chris@16: chain_client(chain_client* client) : chain_(client->chain_) { } Chris@16: virtual ~chain_client() { } Chris@16: Chris@16: const std::type_info& component_type(int n) const Chris@16: { return chain_->component_type(n); } Chris@16: Chris@16: #if !BOOST_WORKAROUND(BOOST_MSVC, < 1310) Chris@16: // Deprecated. Chris@16: template Chris@16: const std::type_info& component_type() const Chris@16: { return chain_->BOOST_NESTED_TEMPLATE component_type(); } Chris@16: Chris@16: template Chris@16: T* component(int n) const Chris@16: { return chain_->BOOST_NESTED_TEMPLATE component(n); } Chris@16: Chris@16: // Deprecated. Chris@16: template Chris@16: T* component() const Chris@16: { return chain_->BOOST_NESTED_TEMPLATE component(); } Chris@16: #else Chris@16: template Chris@16: T* component(int n, boost::type t) const Chris@16: { return chain_->component(n, t); } Chris@16: #endif Chris@16: Chris@16: bool is_complete() const { return chain_->is_complete(); } Chris@16: bool auto_close() const { return chain_->auto_close(); } Chris@16: void set_auto_close(bool close) { chain_->set_auto_close(close); } Chris@16: bool strict_sync() { return chain_->strict_sync(); } Chris@16: void set_device_buffer_size(std::streamsize n) Chris@16: { chain_->set_device_buffer_size(n); } Chris@16: void set_filter_buffer_size(std::streamsize n) Chris@16: { chain_->set_filter_buffer_size(n); } Chris@16: void set_pback_size(std::streamsize n) { chain_->set_pback_size(n); } Chris@16: BOOST_IOSTREAMS_DEFINE_PUSH(push, mode, char_type, push_impl) Chris@16: void pop() { chain_->pop(); } Chris@16: bool empty() const { return chain_->empty(); } Chris@16: size_type size() { return chain_->size(); } Chris@16: void reset() { chain_->reset(); } Chris@16: Chris@16: // Returns a copy of the underlying chain. Chris@16: chain_type filters() { return *chain_; } Chris@16: chain_type filters() const { return *chain_; } Chris@16: protected: Chris@16: template Chris@16: void push_impl(const T& t BOOST_IOSTREAMS_PUSH_PARAMS()) Chris@16: { chain_->push(t BOOST_IOSTREAMS_PUSH_ARGS()); } Chris@16: chain_type& ref() { return *chain_; } Chris@16: void set_chain(chain_type* c) Chris@16: { chain_ = c; chain_->register_client(this); } Chris@16: #if !defined(BOOST_NO_MEMBER_TEMPLATE_FRIENDS) && \ Chris@16: (!BOOST_WORKAROUND(__BORLANDC__, < 0x600)) Chris@16: template Chris@16: friend class chain_base; Chris@16: #else Chris@16: public: Chris@16: #endif Chris@16: virtual void notify() { } Chris@16: private: Chris@16: chain_type* chain_; Chris@16: }; Chris@16: Chris@16: //--------------Implementation of chain_base----------------------------------// Chris@16: Chris@16: template Chris@16: inline std::streamsize chain_base::read Chris@16: (char_type* s, std::streamsize n) Chris@16: { return iostreams::read(*list().front(), s, n); } Chris@16: Chris@16: template Chris@16: inline std::streamsize chain_base::write Chris@16: (const char_type* s, std::streamsize n) Chris@16: { return iostreams::write(*list().front(), s, n); } Chris@16: Chris@16: template Chris@16: inline std::streampos chain_base::seek Chris@16: (stream_offset off, BOOST_IOS::seekdir way) Chris@16: { return iostreams::seek(*list().front(), off, way); } Chris@16: Chris@16: template Chris@16: void chain_base::reset() Chris@16: { Chris@16: using namespace std; Chris@16: pimpl_->close(); Chris@16: pimpl_->reset(); Chris@16: } Chris@16: Chris@16: template Chris@16: bool chain_base::is_complete() const Chris@16: { Chris@16: return (pimpl_->flags_ & f_complete) != 0; Chris@16: } Chris@16: Chris@16: template Chris@16: bool chain_base::auto_close() const Chris@16: { Chris@16: return (pimpl_->flags_ & f_auto_close) != 0; Chris@16: } Chris@16: Chris@16: template Chris@16: void chain_base::set_auto_close(bool close) Chris@16: { Chris@16: pimpl_->flags_ = Chris@16: (pimpl_->flags_ & ~f_auto_close) | Chris@16: (close ? f_auto_close : 0); Chris@16: } Chris@16: Chris@16: template Chris@16: bool chain_base::strict_sync() Chris@16: { Chris@16: typedef typename list_type::iterator iterator; Chris@16: bool result = true; Chris@16: for ( iterator first = list().begin(), Chris@16: last = list().end(); Chris@16: first != last; Chris@16: ++first ) Chris@16: { Chris@16: bool s = (*first)->strict_sync(); Chris@16: result = result && s; Chris@16: } Chris@16: return result; Chris@16: } Chris@16: Chris@16: template Chris@16: void chain_base::pop() Chris@16: { Chris@16: BOOST_ASSERT(!empty()); Chris@16: if (auto_close()) Chris@16: pimpl_->close(); Chris@16: streambuf_type* buf = 0; Chris@16: std::swap(buf, list().back()); Chris@16: buf->set_auto_close(false); Chris@16: buf->set_next(0); Chris@16: delete buf; Chris@16: list().pop_back(); Chris@16: pimpl_->flags_ &= ~f_complete; Chris@16: if (auto_close() || list().empty()) Chris@16: pimpl_->flags_ &= ~f_open; Chris@16: } Chris@16: Chris@16: } // End namespace detail. Chris@16: Chris@16: } } // End namespaces iostreams, boost. Chris@16: Chris@16: #endif // #ifndef BOOST_IOSTREAMS_DETAIL_CHAIN_HPP_INCLUDED