Основная идея — предоставить для любой последовательности символов интерфейс в виде std::basic_streambuf<>, что позволяет использовать эту последовательность в качестве буфера для std::basic_stream<>.
Реализация наследников std::basic_streambuf<> включает два ключевых момента:
1. Управление памятью буфера.
2. Переопределение унаследованных от std::basic_streambuf<> методов underflow, overflow и sync().
// buffer adapters
template<
// mandatory parameters
class sequence_model
// optional parameters
, class P1 = detail::default_parameter
, class P2 = detail::default_parameter
, class P3 = detail::default_parameter
, class P4 = detail::default_parameter
>
class input_buffer;
template<
// mandatory parameters
class sequence_model
// optional parameters
, class P1 = detail::default_parameter
, class P2 = detail::default_parameter
, class P3 = detail::default_parameter
, class P4 = detail::default_parameter
>
class output_buffer;
Предоставленные адаптеры последовательностей обобщают п.1 до стратегии с интерфейсом std::allocator и упрощают реализацию п.2.
От пользователя требуется лишь реализовать примитивный интерфейс для чтения/записи конкретной последовательности и параметризировать им адаптер. В качестве примера реализации этого интерфейса приведены адаптеры для последовательностей, задаваемых итераторами.
Опциональными параметрами адаптеров являются:
тип символов, по-умолчанию — char
тип свойств символов, по-умолчанию std::char_traits<>
тип аллокатора, по-умолчанию std::allocator<>
размер буфера, по-умолчанию 0x100 символов
Опциональные параметры можно задавать в любом порядке, например:
typedef input_buffer<my_sequence> adapter1;
typedef input_buffer<my_sequence, element_is<wchar_t> > adapter2;
typedef input_buffer<my_sequence, buffer_size_is<0x4000>, allocator_is<my_super_fast_allocator>, element_is<wchar_t> > adapter2;
Единственный недостаток то, что непонятно, где это можно применить...
У меня есть только один пример:
#include <iostream>
#include <algorithm>
#include "sequence_buffer.hpp"
#include "wininet_util.h"
int main()
{
using namespace std;
wininet::internet i(_T("simple app"));
wininet::url_file seq1(i, _T("http://www.rarlab.com/rarnew.htm"));
sequence_buffer::input_buffer<wininet::url_file> buf1(seq1);
copy(istreambuf_iterator<char>(&buf1), istreambuf_iterator<char>(),
ostreambuf_iterator<char>(cout));
return 0;
}
Код самих адаптеров:
////////////////////////////////////////////////////////////////////////////////////////////////
// sequence_buffer.h
#pragma once
#include <algorithm>
#include <iterator>
#include <string>
#include <stdexcept>
#include <streambuf>
#include <memory>
#include <boost/type_traits.hpp>
#include <boost/mpl/void.hpp>
#include <boost/mpl/integral_c.hpp>
#include <boost/mpl/vector.hpp>
#include <boost/mpl/if.hpp>
#include <boost/mpl/find_if.hpp>
////////////////////////////////////////////////////////////////////////////////////////////////
#pragma warning(push)
#pragma warning(disable: 4511; disable: 4512; disable: 4706)
////////////////////////////////////////////////////////////////////////////////////////////////
namespace sequence_buffer
{
////////////////////////////////////////////////////////////////////////////////////////////////
// synopsis
// adaptable sequence concepts
template<class model> class input_sequence_concept;
template<class model> class output_sequence_concept;
// examples of sequence models
template<class iterator> class input_sequence_model;
template<class iterator> class output_sequence_model;
// named optional parameters for buffers
template<class T> struct allocator_is;
template<class T> struct element_is;
template<class T> struct element_traits_is;
template<size_t size> struct buffer_size_is;
namespace detail { struct default_parameter; }
// buffer adapters
template<
// mandatory parameters
class sequence_model
// optional parameters
, class P1 = detail::default_parameter
, class P2 = detail::default_parameter
, class P3 = detail::default_parameter
, class P4 = detail::default_parameter
>
class input_buffer;
template<
// mandatory parameters
class sequence_model
// optional parameters
, class P1 = detail::default_parameter
, class P2 = detail::default_parameter
, class P3 = detail::default_parameter
, class P4 = detail::default_parameter
>
class output_buffer;
////////////////////////////////////////////////////////////////////////////////////////////////
namespace detail
{
////////////////////////////////////////////////////////////////////////////////////////////////
namespace mpl = boost::mpl;
namespace tt = boost; // type traits
struct sequence_model_tag {};
struct allocator_tag {};
struct element_tag {};
struct element_traits_tag {};
struct buffer_size_tag {};
template<class tag, class T>
struct define_parameter
{
typedef tag name;
typedef T type;
};
struct default_parameter_tag {};
struct default_parameter
: detail::define_parameter<detail::default_parameter_tag, detail::mpl::void_> {};
////////////////////////////////////////////////////////////////////////////////////////////////
template<class T>
class optional_parameters;
////////////////////////////////////////////////////////////////////////////////////////////////
template<class derived>
struct superior_of
{
derived& self() { return static_cast<derived&>(*this); }
};
////////////////////////////////////////////////////////////////////////////////////////////////
} // namespace detail
////////////////////////////////////////////////////////////////////////////////////////////////
// sequence concepts
template<class model>
class input_sequence_concept : private detail::superior_of<model>
{
public:
typedef input_sequence_concept concept;
size_t read(void* buffer, size_t size) // byte buffer[size]
{
return self().read_impl(buffer, size);
}
};
template<class model>
class output_sequence_concept : private detail::superior_of<model>
{
public:
typedef output_sequence_concept concept;
void write(const void* buffer, size_t size) // byte buffer[size]
{
return self().write_impl(buffer, size);
}
};
////////////////////////////////////////////////////////////////////////////////////////////////
// sequence models
template<class iterator>
class input_sequence_model : public input_sequence_concept<input_sequence_model>
{
private:
typedef typename std::iterator_traits<iterator>::value_type element;
public:
input_sequence_model(iterator begin, iterator end) // [begin, end)
: begin_(begin)
, end_(end)
{}
size_t read_impl(void* buffer, size_t size)
{
if (size % sizeof(element))
throw std::runtime_error("read_impl() failed.");
size = std::min<size_t>(size / sizeof(element), end_ - begin_);
std::copy(begin_, begin_ + size, static_cast<element*>(buffer));
begin_ += size;
return size * sizeof(element);
}
private:
iterator begin_;
iterator end_;
};
template<class iterator>
class output_sequence_model : public output_sequence_concept<output_sequence_model>
{
private:
typedef typename std::iterator_traits<iterator>::value_type element;
public:
output_sequence_model(iterator begin, iterator end) // [begin, end)
: begin_(begin)
, end_(end)
{}
void write_impl(const void* buffer, size_t size)
{
if (size % sizeof(element) || size / sizeof(element) > size_t(end_ - begin_))
throw std::runtime_error("write_impl() failed.");
size /= sizeof(element);
std::copy(static_cast<const element*>(buffer),
static_cast<const element*>(buffer) + size, begin_);
begin_ += size;
}
private:
iterator begin_;
iterator end_;
};
////////////////////////////////////////////////////////////////////////////////////////////////
// named optional parameters
template<class T>
struct allocator_is
: detail::define_parameter<detail::allocator_tag, T> {};
template<class T>
struct element_is
: detail::define_parameter<detail::element_tag, T> {};
template<class T>
struct element_traits_is
: detail::define_parameter<detail::element_traits_tag, T> {};
template<size_t size>
struct buffer_size_is
: detail::define_parameter<detail::buffer_size_tag, boost::mpl::integral_c<size_t, size> >
{};
////////////////////////////////////////////////////////////////////////////////////////////////
namespace detail
{
////////////////////////////////////////////////////////////////////////////////////////////////
template<
class sequence_concept
, class P1
, class P2
, class P3
, class P4
>
class buffer_base
: public std::basic_streambuf<
typename detail::optional_parameters<buffer_base>::element
, typename detail::optional_parameters<buffer_base>::element_traits
>
, private detail::optional_parameters<buffer_base>::allocator // allow EBO
{
protected:
typedef detail::optional_parameters<buffer_base> parameters;
typedef typename parameters::element element;
typedef typename parameters::element_traits element_traits;
typedef typename parameters::allocator allocator;
static const size_t buffer_size =
parameters::buffer_size::value;
buffer_base(sequence_concept& sequence)
: sequence_(sequence)
{
initialize();
}
~buffer_base()
{
release_buffer();
}
protected:
sequence_concept& get_sequence() { return sequence_; }
void pre_underflow()
{
if (!eback())
{
element* buffer = allocate_buffer();
setg(buffer, buffer + buffer_size, buffer + buffer_size);
}
}
void pre_overflow()
{
if (!pbase())
{
element* buffer = allocate_buffer();
setp(buffer, buffer + buffer_size);
}
}
private:
element* allocate_buffer()
{
return this->allocator::allocate(buffer_size);
}
void release_buffer()
{
this->allocator::deallocate(eback(), buffer_size);
setg(0, 0, 0);
this->allocator::deallocate(pbase(), buffer_size);
setp(0, 0);
}
void initialize()
{
setg(0, 0, 0);
setp(0, 0);
}
sequence_concept& sequence_;
};
////////////////////////////////////////////////////////////////////////////////////////////////
} // namespace detail
////////////////////////////////////////////////////////////////////////////////////////////////
template<
class sequence_model
, class P1
, class P2
, class P3
, class P4
>
class input_buffer : public detail::buffer_base<typename sequence_model::concept, P1, P2, P3, P4>
{
private:
typedef detail::buffer_base<typename sequence_model::concept, P1, P2, P3, P4> base;
typedef typename base::element element;
typedef typename base::element_traits element_traits;
static const size_t buffer_size = base::buffer_size;
typedef typename sequence_model::concept sequence_concept;
public:
input_buffer(sequence_concept& sequence)
: base(sequence)
{}
private:
int_type underflow()
{
base::pre_underflow();
size_t read;
if ((read = get_sequence().read(eback(), buffer_size * sizeof(element)))
&& !(read % sizeof(element)))
{
setg(eback(), eback(), eback() + read / sizeof(element));
return element_traits::to_int_type(*eback());
}
else
{
return element_traits::eof();
}
}
};
////////////////////////////////////////////////////////////////////////////////////////////////
template<
class sequence_model
, class P1
, class P2
, class P3
, class P4
>
class output_buffer : public detail::buffer_base<typename sequence_model::concept, P1, P2, P3, P4>
{
private:
typedef detail::buffer_base<typename sequence_model::concept, P1, P2, P3, P4> base;
typedef typename base::element element;
typedef typename base::element_traits element_traits;
static const size_t buffer_size = base::buffer_size;
typedef typename sequence_model::concept sequence_concept;
public:
output_buffer(sequence_concept& sequence)
: base(sequence)
{}
private:
int sync()
{
overflow(element_traits::eof());
return 0;
}
int_type overflow(int_type c)
{
base::pre_overflow();
get_sequence().write(pbase(), (pptr() - pbase()) * sizeof(element));
setp(pbase(), epptr());
if (!element_traits::eq_int_type(c, element_traits::eof()))
{
element e = element_traits::to_char_type(c);
get_sequence().write(&e, sizeof(element));
}
return !element_traits::eof();
}
};
////////////////////////////////////////////////////////////////////////////////////////////////
namespace detail
{
////////////////////////////////////////////////////////////////////////////////////////////////
template<class tag, class parameters>
class find_parameter
{
private:
struct equal_name
{
template<class T>
struct apply
{
typedef typename tt::is_same<tag, typename T::name>::type type;
};
};
typedef typename mpl::find_if<parameters, equal_name>::type result_iterator;
public:
typedef typename mpl::if_<
typename tt::is_same<
typename mpl::end<parameters>::type,
result_iterator
>::type,
default_parameter,
typename result_iterator::type
>::type type;
};
////////////////////////////////////////////////////////////////////////////////////////////////
template<class sequence_concept, class P1, class P2, class P3, class P4>
class optional_parameters<buffer_base<sequence_concept, P1, P2, P3, P4> >
{
private:
typedef mpl::vector<P1, P2, P3, P4> parameters;
typedef typename find_parameter<
element_tag,
parameters
>::type provided_element;
typedef typename find_parameter<
element_traits_tag,
parameters
>::type provided_element_traits;
typedef typename find_parameter<
allocator_tag,
parameters
>::type provided_allocator;
typedef typename find_parameter<
buffer_size_tag,
parameters
>::type provided_buffer_size;
public:
// default element is char
typedef typename mpl::if_<
typename tt::is_same<
provided_element,
default_parameter
>::type,
char,
typename provided_element::type
>::type element;
// default element_traits is std::char_traits<element>
typedef typename mpl::if_<
typename tt::is_same<
provided_element_traits,
default_parameter
>::type,
std::char_traits<element>,
typename provided_element_traits::type
>::type element_traits;
// default allocator is std::allocator<element>
typedef typename mpl::if_<
typename tt::is_same<
provided_allocator,
default_parameter
>::type,
std::allocator<element>,
typename provided_allocator::type
>::type allocator;
// default buffer_size is 0x100 elements
typedef typename mpl::if_<
typename tt::is_same<
provided_buffer_size,
default_parameter
>::type,
mpl::integral_c<size_t, 0x100>,
typename provided_buffer_size::type
>::type buffer_size;
};
////////////////////////////////////////////////////////////////////////////////////////////////
} // namespace detail
////////////////////////////////////////////////////////////////////////////////////////////////
} // namespace sequence_buffer
////////////////////////////////////////////////////////////////////////////////////////////////
#pragma warning(pop)
////////////////////////////////////////////////////////////////////////////////////////////////
Здравствуйте, MaximE, Вы писали:
ME>Единственный недостаток то, что непонятно, где это можно применить...
ME>У меня есть только один пример:
Еще один пример:
#include <iostream>
#include <algorithm>
#include <vector>
#include "sequence_buffer.hpp"
int _tmain(int argc, _TCHAR* argv[])
{
using namespace std;
namespace sb = sequence_buffer;
vector<char> v1;
// сделаем из вектора буфер вывода
typedef sb::push_back_sequence<vector<char> > seq1;
seq1 s1(v1);
sb::output_buffer<seq1> b1(s1);
// и перенаправим cout в него
basic_streambuf<char>* cout_buf = cout.rdbuf(&b1);
cout << "Redirect this text" << endl;
// сделаем из вектора буфер ввода
typedef sb::stl_input_sequence<vector<char>::const_iterator> seq2;
seq2 s2(v1.begin(), v1.end());
sb::input_buffer<seq2> b2(s2);
// скопируем буфер-вектор в оригинальный буфер cout
copy(istreambuf_iterator<char>(&b2), istreambuf_iterator<char>(),
ostreambuf_iterator<char>(cout_buf));
cout.rdbuf(cout_buf);
return 0;
}