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