std::basic_streambuf<> адаптеры последовательностей
От: MaximE Великобритания  
Дата: 03.05.03 16:11
Оценка: 12 (1)
Основная идея — предоставить для любой последовательности символов интерфейс в виде 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.

От пользователя требуется лишь реализовать примитивный интерфейс для чтения/записи конкретной последовательности и параметризировать им адаптер. В качестве примера реализации этого интерфейса приведены адаптеры для последовательностей, задаваемых итераторами.

Опциональными параметрами адаптеров являются:

Опциональные параметры можно задавать в любом порядке, например:
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)

////////////////////////////////////////////////////////////////////////////////////////////////
Re: std::basic_streambuf<> адаптеры последовательностей
От: MaximE Великобритания  
Дата: 04.05.03 11:17
Оценка:
Здравствуйте, 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;
}
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.