简体   繁体   English

C++ 简单的循环缓冲区队列

[英]C++ Simple circular-buffer Queue

I have a implemented a queue using an array and treating it as a circular buffer.我使用数组实现了一个队列并将其视为循环缓冲区。 _tail points to the next element to read and _head points to the next element to write into: _tail指向下一个要读取的元素, _head指向下一个要写入的元素:

template<int SIZE>
class Q {

bool push(const int item){
    if(false == isFull()){
        _q[_head] = item;
        _head = ++_head % SIZE;
        return true;
    }

    return false;
}

bool pop(int& toReturn){
    if(false == isEmpty()){
        toReturn = _q[_tail];
        _tail = ++_tail % SIZE;  
        return true;
    }

    return false;
}

private:

    std::array<int, SIZE> _q;
    std::atomic<int> _head;
    std::atomic<int> _tail;
};

However, I have a bug in my isFull() and isEmpty() logic, identified by the following questions:但是,我的isFull()isEmpty()逻辑中有一个错误,由以下问题确定:

When the queue is initially empty, both _tail and _head will point to _q[0] .当队列最初为空时, _tail_head都将指向_q[0]

When I have written _q[0] , _q[1] , _q[2] and q[3] again _tail and _head will point to the same, so clearly I cannot use _tail == _head to determine full/empty.当我再次写入_q[0]_q[1]_q[2]q[3]时, _tail_head将指向相同的内容,所以很明显我不能使用_tail == _head来确定满/空。

What is the simplest way to implement the isEmpty() and isFull() logic?实现isEmpty()isFull()逻辑的最简单方法是什么? I wasn't sure whether I should write a "BLANK" enum after a queue item has been read?我不确定在读取队列项后是否应该编写“空白”枚举?

Declare a data member (counter) in the class that counts the number of items in the queue. 在类中声明一个数据成员(计数器),用于计算队列中的项目数。 Increment it in (push) and decrement it in (pop). 递增(推)并递减(弹出)。 When it is (0), the queue is empty 当它为(0)时,队列为空

The logic is relatively straightforward, if you are willing to reduce the effective size of your queue by one element: 如果您愿意将队列的有效大小减少一个元素,那么逻辑相对简单:

  • When head and tail are the same, the queue is empty. headtail相同时,队列为空。
  • When incrementing tail by one, with wrap-around, produces head , the queue is full. tail递增1时,使用环绕生成head ,队列已满。

This makes it impossible to insert N-th element, which serves as a "sentinel". 这使得不可能插入作为“哨兵”的第N个元素。 Hence, for an N-element queue you need to allocate an array of N+1 elements, and use (SIZE+1) in all your expressions dealing with wrap-around. 因此,对于N元素队列,您需要分配N + 1个元素的数组,并在处理环绕的所有表达式中使用(SIZE+1)

std::array<int,SIZE+1> _q;

Implementation note: 实施说明:

These two lines 这两行

_head = ++_head % SIZE;
_tail = ++_tail % SIZE;  

have undefined behavior, because the compiler has flexibility at applying side effects from incrementing _head and _tail either before or after the assignment is over. 具有未定义的行为,因为编译器可以灵活地应用在赋值结束之前或之后递增_head_tail副作用。 If the compiler chooses to apply side effects after the assignment, the wrap-around effect will not happen. 如果编译器选择在赋值后应用副作用,则不会发生环绕效果。

Since you do not need compound assignment at all, the fix is easy: 由于您根本不需要复合赋值,因此修复很简单:

_head = (_head+1) % SIZE;
_tail = (_tail+1) % SIZE;  

How about changing implementation a bit like 如何改变实现有点像

Change your head's definition from " _head points to the next element to write into: " to 将头部的定义从“ _head points to the next element to write into: ”改为
" _head points to latest element added " _head points to latest element added

Then isFull() can be 然后isFull()可以

bool isFull()
{
    if((_head + 1) % SIZE == _tail)
        return true;

    return false;
}

and isEmpty could be 并且isEmpty可以

bool isEmpty()
{
    if(_head == _tail)
        return true;

    return false;
}

pop() will be same but change push() and don't forget to initialize _head and _tail to -1 pop()将相同但更改push()并且不要忘记将_head_tail初始化为-1

bool push(const int item){
    if(false == isFull()){
        if(_head == -1)    //If adding the first element,
            _tail = 0;     // tail will point to it as it was inititiallty 0
        _head = ++_head % SIZE;
         _q[_head] = item;
        return true;
    }

    return false;
}

Since you use modulus to calculate the index of your array, perhaps keeping a running count for each read and write would simplify the logic. 由于您使用模数来计算数组的索引,因此保留每次读取和写入的运行计数可以简化逻辑。

Change the implementation like this ... 改变这样的实现......

bool push(const int item){
    if(false == isFull()){
        _q[_head % SIZE] = item;
        ++_head;
        return true;
    }

    return false;
}

bool pop(int& toReturn){
    if(false == isEmpty()){
        toReturn = _q[_tail % SIZE];
        ++_tail;  
        return true;
    }

    return false;
}

And use these ... 并使用这些......

bool isFull()
{
    return _head - _tail == SIZE;
}

bool isEmpty()
{
    if (_head == _tail)
    {
        _head = _tail = 0;
        return true;
    }

    return false;
}

If you need a full-featured ring-buffer, try this:如果你需要一个全功能的环形缓冲区,试试这个:

#pragma once
#include <vector>
#include <stdexcept>
#include <new>
#include <utility>
#include <cstring>
#include <cassert>

#if defined(_MSC_VER)
    #pragma warning(push)
    #pragma warning(disable: 26495) // uninitialized member t at TU::TU()
#endif
#if defined(__llvm__)
    #pragma clang diagnostic push
    #pragma clang diagnostic ignored "-Wdangling-else"
#endif

template<typename T, typename Alloc = std::allocator<T>>
class ring_buffer
{
    union TU
    {
        T t;
        TU()  {}
        ~TU() {}
    };
    using vec_t = std::vector<TU, typename std::allocator_traits<Alloc>::template rebind_alloc<TU>>;
    using it_t  = typename vec_t::iterator;
public:
    struct iterator;
    struct const_iterator
    {
                        const_iterator();
                        const_iterator( const_iterator const &it );
        const_iterator &operator +=( std::ptrdiff_t offset );
        const_iterator &operator -=( std::ptrdiff_t offset );
        const_iterator &operator  =( const_iterator const &it );
        bool            operator ==( const_iterator const &it ) const;
        bool            operator !=( const_iterator const &it ) const;
        bool            operator  <( const_iterator const &it ) const;
        bool            operator <=( const_iterator const &it ) const;
        bool            operator  >( const_iterator const &it ) const;
        bool            operator >=( const_iterator const &it ) const;
        const_iterator  operator  +( std::ptrdiff_t offset ) const;
        const_iterator  operator  -( std::ptrdiff_t offset ) const;
        T const        &operator  *() const;
        const_iterator &operator ++();
        const_iterator &operator --();
        const_iterator  operator ++( int );
        const_iterator  operator --( int );
        T const        &operator []( std::ptrdiff_t offset ) const;
        T const        &at( std::ptrdiff_t offset ) const;
    private:
        friend class ring_buffer;
                        const_iterator( ring_buffer &ring, it_t it );
        std::ptrdiff_t  idx() const;
        template<bool THROW = false>
        it_t         offset_it( std::ptrdiff_t offset ) const;
        ring_buffer *m_ring;
        it_t         m_it;
    };
    struct iterator : public const_iterator
    {
                 iterator();
                 iterator( iterator const &it );
        iterator &operator +=( std::ptrdiff_t offset );
        iterator &operator -=( std::ptrdiff_t offset );
        iterator &operator  =( iterator const &it );
        iterator  operator  +( std::ptrdiff_t offset ) const;
        iterator  operator  -( std::ptrdiff_t offset ) const;
        T        &operator  *() const;
        iterator &operator ++();
        iterator &operator --();
        iterator  operator ++( int );
        iterator  operator --( int );
        T        &operator []( std::ptrdiff_t offset ) const;
        T        &at(          std::ptrdiff_t offset ) const;
    private:
                  iterator( ring_buffer &ring, it_t it );
        explicit  iterator( const_iterator const &it );
        friend class ring_buffer;
    };
                    ring_buffer( std::size_t cap );
                    ring_buffer( ring_buffer const &other );
                    ~ring_buffer();
    T              &operator []( std::size_t offset );
    T              &at( std::size_t offset );
    T              &front() const;
    iterator        begin() const;
    iterator        end() const;
    const_iterator  cbegin() const;
    const_iterator  cend() const;
    void            pop_front();
    template<typename ... Args>
    T              &emplace_back( Args &&...args );
    T              &push_back( T const &other );
    T              &push_back( T &&other );
    bool            full() const;
    std::size_t     size() const;
    void            capacity( std::size_t newCap );
private:
    vec_t  m_ring;
    it_t   m_read, m_write;
    size_t m_size, m_capacity;
};

template<typename T, typename Alloc>
inline
std::ptrdiff_t ring_buffer<T, Alloc>::const_iterator::idx() const
{
    if( m_it >= m_ring->m_read )
    {
        assert(   m_ring->m_read  <= m_ring->m_write && m_it <= m_ring->m_write
               || m_ring->m_write <  m_ring->m_read   & m_it >= m_ring->m_ring.begin() && m_it <= m_ring->m_write);
        return m_it - m_ring->m_read;
    }
    else
    {
        assert(m_it >= m_ring->m_ring.begin() && m_it <= m_ring->m_write);
        return (m_it - m_ring->m_ring.begin()) + (m_ring->m_ring.end() - m_ring->m_read);
    }
}

template<typename T, typename Alloc>
template<bool THROW>
inline
typename ring_buffer<T, Alloc>::it_t ring_buffer<T, Alloc>::const_iterator::offset_it( std::ptrdiff_t offset ) const
{
    using namespace std;
    ptrdiff_t rel;
    static
    char const outOfRangeStr[] = "ring-buffer access out of range";
    if( m_ring->m_read <= m_ring->m_write )
    {
        rel = m_it - m_ring->m_read + offset;
        if constexpr( !THROW )
            assert(rel >= 0 && rel <= m_ring->m_write - m_ring->m_read);
        else
            if( rel < 0 || rel > m_ring->m_write - m_ring->m_read )
                throw out_of_range( outOfRangeStr );
        return m_it + offset;
    }
    if( offset >= 0 )
    {
        if( m_it >= m_ring->m_read )
        {
            rel = m_it - m_ring->m_read + offset;
            if( rel < m_ring->m_ring.end() - m_ring->m_read )
                return m_it + rel;
            rel -= m_ring->m_ring.end() - m_ring->m_read;
            if constexpr( !THROW )
                assert(rel <= m_ring->m_write - m_ring->m_ring.begin());
            else
                if( rel > m_ring->m_write - m_ring->m_ring.begin() )
                    throw out_of_range( outOfRangeStr );
            return m_ring->m_ring.begin() + rel;
        }
        if constexpr( !THROW )
            assert(m_it - m_ring->m_ring.begin() + offset <= m_ring->m_write - m_ring->m_ring.begin());
        else
            if( m_it - m_ring->m_ring.begin() + offset > m_ring->m_write - m_ring->m_ring.begin() )
                throw out_of_range( outOfRangeStr );
        return m_it + offset;
    }
    if( m_it >= m_ring->m_read )
    {
        rel = m_it - m_ring->m_read + offset;
        if constexpr( !THROW )
            assert(rel >= 0);
        else
            if( rel < 0 )
                throw out_of_range( outOfRangeStr );
        return m_it + offset;
    }
    rel = m_it - m_ring->m_ring.begin() + offset;
    if( rel >= 0 )
        return m_it + rel;
    rel += m_it - m_ring->m_ring.begin();
    if constexpr( !THROW )
        assert(m_ring->m_ring.end() - m_ring->m_read >= -rel);
    else
        if( m_ring->m_ring.end() - m_ring->m_read < -rel )
            throw out_of_range( outOfRangeStr );
    return m_ring->m_ring.end() + rel;
}

template<typename T, typename Alloc>
inline
ring_buffer<T, Alloc>::const_iterator::const_iterator()
#if !defined(NDEBUG)
    : m_ring( nullptr )
#endif
{
}

template<typename T, typename Alloc>
inline
ring_buffer<T, Alloc>::const_iterator::const_iterator( const_iterator const &it ) :
    m_ring( it.m_ring ),
    m_it( it.m_it )
{
}

template<typename T, typename Alloc>
inline
ring_buffer<T, Alloc>::const_iterator::const_iterator( ring_buffer &ring, it_t it ) :
    m_ring( &ring ),
    m_it( it )
{
}

template<typename T, typename Alloc>
inline
typename ring_buffer<T, Alloc>::const_iterator &ring_buffer<T, Alloc>::const_iterator::operator +=( std::ptrdiff_t offset )
{
    m_it = offset_it( offset );
    return *this;
}

template<typename T, typename Alloc>
inline
typename ring_buffer<T, Alloc>::const_iterator &ring_buffer<T, Alloc>::const_iterator::operator -=( std::ptrdiff_t offset )
{
    m_it = offset_it( -offset );
    return *this;
}

template<typename T, typename Alloc>
inline
typename ring_buffer<T, Alloc>::const_iterator &ring_buffer<T, Alloc>::const_iterator::operator =( const_iterator const &it )
{
    m_ring = it.m_ring;
    m_it   = it.m_it;
    return *this;
}

template<typename T, typename Alloc>
inline
bool ring_buffer<T, Alloc>::const_iterator::operator ==( const_iterator const &it ) const
{
    assert(m_ring == it.m_ring);
    return m_it == it.m_it;
}

template<typename T, typename Alloc>
inline
bool ring_buffer<T, Alloc>::const_iterator::operator !=( const_iterator const &it ) const
{
    assert(m_ring == it.m_ring);
    return m_it != it.m_it;
}

template<typename T, typename Alloc>
inline
bool ring_buffer<T, Alloc>::const_iterator::operator  <( const_iterator const &it ) const
{
    assert(m_ring == it.m_ring);
    return idx() < it.idx();
}

template<typename T, typename Alloc>
inline
bool ring_buffer<T, Alloc>::const_iterator::operator <=( const_iterator const &it ) const
{
    assert(m_ring == it.m_ring);
    return idx() <= it.idx();
}

template<typename T, typename Alloc>
inline
bool ring_buffer<T, Alloc>::const_iterator::operator >( const_iterator const &it ) const
{
    assert(m_ring == it.m_ring);
    return idx() > it.idx();
}

template<typename T, typename Alloc>
inline
bool ring_buffer<T, Alloc>::const_iterator::operator >=( const_iterator const &it ) const
{
    assert(m_ring == it.m_ring);
    return idx() >= it.idx();
}

template<typename T, typename Alloc>
inline
typename ring_buffer<T, Alloc>::const_iterator ring_buffer<T, Alloc>::const_iterator::operator +( std::ptrdiff_t offset ) const
{
    return const_iterator( *m_ring, offset_it( offset ) );
}

template<typename T, typename Alloc>
inline
typename ring_buffer<T, Alloc>::const_iterator ring_buffer<T, Alloc>::const_iterator::operator -( std::ptrdiff_t offset ) const
{
    return const_iterator( *m_ring, offset_it( -offset ) );
}

template<typename T, typename Alloc>
inline
T const &ring_buffer<T, Alloc>::const_iterator::operator *() const
{
    assert(idx() < (std::ptrdiff_t)m_ring->m_size);
    return m_it->t;
}

template<typename T, typename Alloc>
inline
typename ring_buffer<T, Alloc>::const_iterator &ring_buffer<T, Alloc>::const_iterator::operator ++()
{
    m_it = offset_it( 1 );
    return *this;
}

template<typename T, typename Alloc>
inline
typename ring_buffer<T, Alloc>::const_iterator &ring_buffer<T, Alloc>::const_iterator::operator --()
{
    m_it = offset_it( -1 );
    return *this;
}

template<typename T, typename Alloc>
inline
typename ring_buffer<T, Alloc>::const_iterator ring_buffer<T, Alloc>::const_iterator::operator ++( int )
{
    const_iterator ret( *this );
    m_it = offset_it( 1 );
    return ret;
}

template<typename T, typename Alloc>
inline
typename ring_buffer<T, Alloc>::const_iterator ring_buffer<T, Alloc>::const_iterator::operator --( int )
{
    const_iterator ret( *this );
    m_it = offset_it( -1 );
    return ret;
}

template<typename T, typename Alloc>
inline
T const &ring_buffer<T, Alloc>::const_iterator::operator []( std::ptrdiff_t offset ) const
{
    assert(offset_it( offset ) != m_ring->m_write);
    return offset_it( offset )->t;
}

template<typename T, typename Alloc>
inline
T const &ring_buffer<T, Alloc>::const_iterator::at( std::ptrdiff_t offset ) const
{
    assert(offset_it( offset ) != m_ring->m_write);
    return offset_it<true>( offset )->t;
}

template<typename T, typename Alloc>
inline
ring_buffer<T, Alloc>::iterator::iterator() :
    const_iterator()
{
}

template<typename T, typename Alloc>
inline
ring_buffer<T, Alloc>::iterator::iterator( iterator const &it ) :
    const_iterator( it )
{
}

template<typename T, typename Alloc>
inline
ring_buffer<T, Alloc>::iterator::iterator( ring_buffer &ring, it_t it ) :
    const_iterator( ring, it )
{
}

template<typename T, typename Alloc>
inline
ring_buffer<T, Alloc>::iterator::iterator( const_iterator const &it ) :
    const_iterator( it )
{
}

template<typename T, typename Alloc>
inline
typename ring_buffer<T, Alloc>::iterator &ring_buffer<T, Alloc>::iterator::operator +=( std::ptrdiff_t offset )
{
    return (iterator &)const_iterator::operator +=( offset );
}

template<typename T, typename Alloc>
inline
typename ring_buffer<T, Alloc>::iterator &ring_buffer<T, Alloc>::iterator::operator -=( std::ptrdiff_t offset )
{
    return (iterator &)const_iterator::operator -=( -offset );
}

template<typename T, typename Alloc>
inline
typename ring_buffer<T, Alloc>::iterator &ring_buffer<T, Alloc>::iterator::operator =( iterator const &it )
{
    return (iterator &)const_iterator::operator =( it );
}

template<typename T, typename Alloc>
inline
typename ring_buffer<T, Alloc>::iterator ring_buffer<T, Alloc>::iterator::operator +( std::ptrdiff_t offset ) const
{
    return (iterator)const_iterator::operator +( offset );
}

template<typename T, typename Alloc>
inline
typename ring_buffer<T, Alloc>::iterator ring_buffer<T, Alloc>::iterator::operator -( std::ptrdiff_t offset ) const
{
    return (iterator)const_iterator::operator -( offset );
}

template<typename T, typename Alloc>
inline
T &ring_buffer<T, Alloc>::iterator::operator *() const
{
    return (T &)const_iterator::operator *();
}

template<typename T, typename Alloc>
inline
typename ring_buffer<T, Alloc>::iterator &ring_buffer<T, Alloc>::iterator::operator ++()
{
    return (iterator &)const_iterator::operator ++();
}

template<typename T, typename Alloc>
inline
typename ring_buffer<T, Alloc>::iterator &ring_buffer<T, Alloc>::iterator::operator --()
{
    return (iterator &)const_iterator::operator --();
}

template<typename T, typename Alloc>
inline
typename ring_buffer<T, Alloc>::iterator ring_buffer<T, Alloc>::iterator::operator ++( int )
{
    return (iterator)const_iterator::operator ++( 1 );
}

template<typename T, typename Alloc>
inline
typename ring_buffer<T, Alloc>::iterator ring_buffer<T, Alloc>::iterator::operator --( int )
{
    return (iterator)const_iterator::operator --( 1 );
}

template<typename T, typename Alloc>
inline
T &ring_buffer<T, Alloc>::iterator::operator []( std::ptrdiff_t offset ) const
{
    return (T &)const_iterator::operator []( offset );
}

template<typename T, typename Alloc>
inline
T &ring_buffer<T, Alloc>::iterator::at( std::ptrdiff_t offset ) const
{
    return (T &)const_iterator::at( offset );
}

template<typename T, typename Alloc>
ring_buffer<T, Alloc>::ring_buffer( std::size_t cap ) :
    m_ring( cap + 1 ),
    m_read( m_ring.begin() ),
    m_write( m_ring.begin() ),
    m_size( 0 ),
    m_capacity( cap )
{
}

template<typename T, typename Alloc>
ring_buffer<T, Alloc>::ring_buffer( ring_buffer const &other ) :
    m_ring( other.m_capacity + 1 ),
    m_size( other.m_size ),
    m_capacity( other.m_capacity ),
    m_read( m_ring.begin() ),
    m_write( m_ring.begin() + m_size )
{
    it_t write = m_read;
    try
    {
        for( TU const &tu : other.m_ring )
            new( (void *)&*write++ )T( tu.t );
    }
    // if an exception occurs, destroy the objects which we've copied so far
    catch( ... )
    {
        for( it_t begin = m_read; write > begin; (--write)->t.~T() );
        throw;
    }
}

template<typename T, typename Alloc>
ring_buffer<T, Alloc>::~ring_buffer()
{
    auto destrRange = []( it_t destr, it_t end )
    {
        for( ; destr != end; destr++->t.~T() );
    };
    if( m_read <= m_write )
    {
        destrRange( m_read, m_write );
        return;
    }
    destrRange( m_read,  m_ring.end() );
    destrRange( m_ring.begin(), m_write );
}

template<typename T, typename Alloc>
inline
T &ring_buffer<T, Alloc>::operator []( std::size_t offset )
{
    using namespace std;
    assert(offset < m_size);
    if( m_write >= m_read )
    {
        assert((size_t)(m_ring.end() - m_ring.begin()) > offset);
        return m_read[offset].t;
    }
    if( offset < (size_t)(m_ring.end() - m_read) )
        return m_read[offset].t;
    offset -= m_ring.end() - m_read;
    assert((size_t)(m_write - m_ring.begin()) > offset);
    return m_ring[offset].t;
}

template<typename T, typename Alloc>
inline
T &ring_buffer<T, Alloc>::at( std::size_t offset )
{
    using namespace std;
    if( m_size == 0 )
        throw logic_error( "ring-buffer empty" );
    if( m_write >= m_read )
        if( offset < (size_t)(m_write - m_read) )
            return m_read[offset].t;
        else
            throw out_of_range( "ring-buffer index too large" );
    if( offset < (size_t)(m_ring.end() - m_read) )
        return m_read[offset].t;
    if( (offset -= m_ring.end() - m_read) >= (size_t)(m_write - m_ring.begin()) )
        throw out_of_range( "ring-buffer index too large" );
    return m_ring[offset].t;
}

template<typename T, typename Alloc>
inline
T &ring_buffer<T, Alloc>::front() const
{
    assert(m_size);
    return m_read->t;
}

template<typename T, typename Alloc>
inline
typename ring_buffer<T, Alloc>::iterator ring_buffer<T, Alloc>::begin() const
{
    return iterator( *(ring_buffer *)this, m_read );
}

template<typename T, typename Alloc>
inline
typename ring_buffer<T, Alloc>::iterator ring_buffer<T, Alloc>::end() const
{
    return iterator( *(ring_buffer *)this, m_write );
}

template<typename T, typename Alloc>
inline
typename ring_buffer<T, Alloc>::const_iterator ring_buffer<T, Alloc>::cbegin() const
{
    return const_iterator( *(ring_buffer *)this, m_read );
}

template<typename T, typename Alloc>
inline
typename ring_buffer<T, Alloc>::const_iterator ring_buffer<T, Alloc>::cend() const
{
    return const_iterator( *(ring_buffer *)this, m_write );
}

template<typename T, typename Alloc>
inline
void ring_buffer<T, Alloc>::pop_front()
{
    assert(m_size);
    m_read->t.~T();
    if( ++m_read == m_ring.end() )
        m_read = m_ring.begin();
    --m_size;
}

template<typename T, typename Alloc>
template<typename ... Args>
T &ring_buffer<T, Alloc>::emplace_back( Args &&...args )
{
    using namespace std;
    assert(m_capacity);
    if( m_size == m_capacity )
        pop_front();
    T &t = m_write->t;
    new( (void *)&t )T( forward<Args>( args ) ... );
    if( ++m_write == m_ring.end() )
        m_write = m_ring.begin();
    ++m_size;
    return t;
}

template<typename T, typename Alloc>
T &ring_buffer<T, Alloc>::push_back( T const &other )
{
    using namespace std;
    assert(m_capacity);
    if( m_size == m_capacity )
        pop_front();
    T &t = m_write->t;
    new( (void *)&t )T( other );
    if( ++m_write == m_ring.end() )
        m_write = m_ring.begin();
    ++m_size;
    return t;
}

template<typename T, typename Alloc>
T &ring_buffer<T, Alloc>::push_back( T &&other )
{
    using namespace std;
    assert(m_capacity);
    if( m_size == m_capacity )
        pop_front();
    T &t = m_write->t;
    new( (void *)&t )T( move( other ) );
    if( ++m_write == m_ring.end() )
        m_write = m_ring.begin();
    ++m_size;
    return t;
}

template<typename T, typename Alloc>
inline
std::size_t ring_buffer<T, Alloc>::size() const
{
    return m_size;
}

template<typename T, typename Alloc>
inline
bool ring_buffer<T, Alloc>::full() const
{
    return m_size == m_capacity;
}

template<typename T, typename Alloc>
void ring_buffer<T, Alloc>::capacity( std::size_t newCap )
{
    using namespace std;
    if( newCap < m_size )
        throw invalid_argument( "new ring-buffer size too small" );
    vec_t newRing( newCap + 1 );
    it_t  to = newRing.begin();
    auto moveRange = [&to]( it_t from, it_t fromEnd )
    {
        for( ; from != fromEnd; ++from, ++to )
            new( (void *)&*to )T( move( from->t ) );
    };
    auto destrRange = []( it_t destr, it_t end )
    {
        for( ; destr != end; (--destr)->t.~T() );
    };
    try
    {
        // destroy after copying / moving completely because whe might copy only if there isn't
        // move-constructor and have an exception; so the whole thing becomes transactional
        if( m_read <= m_write )
            moveRange(  m_read,         m_write        ),
            destrRange( m_write,        m_read         );
        else
            moveRange(  m_read,         m_ring.end()   ),
            moveRange(  m_ring.begin(), m_write        ),
            destrRange( m_write,        m_ring.begin() ),
            destrRange( m_ring.end(),   m_read         );
    }
    // if there's an exception, the objects haven't been moved but copied;
    // so we can destroy the objects in the destination-vector created so far
    catch( ... )
    {
        destrRange( to, newRing.begin() );
        throw;
    }
    swap( m_ring, newRing );
    m_read     = m_ring.begin();
    m_write    = to;
    m_capacity = newCap;
}

template<typename T, typename Alloc>
void swap( ring_buffer<T, Alloc> &a, ring_buffer<T, Alloc> &b )
{
    swap( a.m_ring,     b.m_ring     );
    swap( a.m_read,     b.m_read     );
    swap( a.m_write,    b.m_write    );
    swap( a.m_capacity, b.m_capacity );
    swap( a.m_size,     b.m_size     );
}

#if defined(_MSC_VER)
    #pragma warning(pop)
#endif
#if defined(__llvm__)
    #pragma clang diagnostic pop
#endif

There's a lot disinformation circling about the circular buffer.有很多关于循环缓冲区的虚假信息。 First of all, it's a deque.首先,它是一个双端队列。 Whenever you see 2 pointers or indices, you might be dealing with a deque and you see head and tail and read and write pointers frequently in relation to the circular buffer.每当您看到 2 个指针或索引时,您可能正在处理一个双端队列,并且您会经常看到与循环缓冲区相关的头和尾以及读写指针。 But it's all wrong, the circular buffer is a deque and as such is related to the doubly linked list, which can also serve as a deque.但这都是错误的,循环缓冲区是一个双端队列,因此与双向链表相关,它也可以作为双端队列。 So, what you should think about are the first and last nodes, not head and tail, just like with a doubly linked list.所以,你应该考虑的是第一个和最后一个节点,而不是头和尾,就像双向链表一样。 Then you need to decide, whether first == last means, that the buffer is empty.然后你需要决定, first == last是否意味着缓冲区为空。 If so, you can implement the buffer using 2 pointers (indices), but if not, then 3. The most important thing is to check out the literature about deques as different people tend to implement their circular buffers differently (head and tail frequently switch roles, for example) and deques are usually dealt with more academically, unambiguously.如果是这样,你可以使用 2 个指针(索引)来实现缓冲区,但如果不是,那么 3。最重要的是查看关于双端队列的文献,因为不同的人倾向于以不同的方式实现他们的循环缓冲区(头部和尾部经常切换角色,例如)和双端队列通常在学术上更明确地处理。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM