#include <memory>
#include <cstddef>
template<typename Value,
typename Alloc = std::allocator<Value>>
struct ListAlloc
{
using value_type = Value;
// ///////////////////////////////////////////////////////////////////////////////////////////
struct NodeBase
{
NodeBase *prev,
*next;
NodeBase( NodeBase* prev, NodeBase* next ) noexcept :
prev{prev},
next{next} {}
NodeBase() noexcept :
NodeBase{nullptr, nullptr} {}
} mutable mStart, mEnd;
struct Node : NodeBase
{
value_type data;
template<typename... Args>
Node( NodeBase* prev, NodeBase* next, Args&&... args ) noexcept :
NodeBase{prev, next},
data{std::forward<Args>(args)...} {}
Node() noexcept = default;
};
using allocator_type = typename Alloc::template rebind<Node>::other;
using allocator_traits = std::allocator_traits<allocator_type>;
/// ////////////////////////////
#define takeover( N ) using N = typename allocator_traits::N;
typedef value_type& reference;
typedef value_type const& const_reference;
takeover(pointer)
takeover(const_pointer)
takeover(difference_type)
takeover(size_type)
#undef takeover
/// /////////////////////////////
allocator_type mAllocator;
ListAlloc( NodeBase const& start, NodeBase const& end, allocator_type const& alloc = allocator_type{} ):
mStart{start}, mEnd{end},
mAllocator{alloc} {}
template<typename... Args>
Node* _alloc_make_node( NodeBase* prev, NodeBase* next, Args&&... args )
{
Node* node = allocator_traits::allocate( mAllocator, 1 );
try
{
allocator_traits::construct( mAllocator, node, prev, next, std::forward<Args>(args)... );
}
catch(...)
{
allocator_traits::deallocate( mAllocator, node, 1 );
throw;
}
return node;
}
template<typename... Args>
Node* _alloc_make_node_notify( NodeBase* prev, NodeBase* next, Args&&... args )
{
Node* node = _alloc_make_node( prev, next, std::forward<Args>(args)... );
if( prev )
prev->next = node;
if( next )
next->prev = node;
return node;
}
void _alloc_destroy_node( Node* ptr )
{
allocator_traits::destroy( mAllocator, ptr );
allocator_traits::deallocate( mAllocator, ptr, 1 );
}
void _alloc_destroy_all_prev( NodeBase* end, NodeBase* ptr )
{
NodeBase* old_ptr;
while( ptr != end )
{
old_ptr = ptr;
ptr = ptr->prev;
_alloc_destroy_node( static_cast<Node*>(old_ptr) );
}
}
private:
template<typename... Func>
std::pair<Node*, Node*> _alloc_create_impl( size_type n, Func... func )
{
if( n == 0 )
return { nullptr, nullptr };
Node* ptr = _alloc_make_node_notify( ptr, nullptr, func()... );
Node* const rval = ptr;
while( --n ) try
{
ptr = _alloc_make_node_notify( ptr, nullptr, func()... );
}
catch(...)
{
ptr = ptr->prev;
_alloc_destroy_all_prev( ptr );
throw;
}
return {rval, ptr};
}
public:
std::pair<Node*, Node*> _alloc_fill_n( size_type n )
{
return _alloc_create_impl( n );
}
std::pair<Node*, Node*> _alloc_fill_n( size_type n, const_reference value )
{
return _alloc_create_impl( n, [&]() -> const_reference { return value; } );
}
template<typename InputIterator>
std::pair<Node*, Node*> _alloc_copy( size_type n, InputIterator first )
{
return _alloc_create_impl( n, [&]() -> const_reference { return *first++; } );
}
// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void _alloc_swap( allocator_type& lhs, allocator_type& rhs, std::true_type )
{ std::swap(lhs, rhs); }
void _alloc_swap( allocator_type&, allocator_type&, std::false_type )
{}
allocator_type get_allocator() const { return mAllocator; }
size_type max_size() const { return allocator_traits::max_size(mAllocator); }
};
#include <type_traits>
/// Geborgt von camper - verzeih'
#define TAG_CHECK(It, ItTag) \
,typename std::enable_if<std::is_convertible< \
typename std::conditional<std::is_class<It>::value || std::is_pointer<It>::value, std::iterator_traits<It>, std::false_type>::type::iterator_category, \
ItTag>::value, int>::type = 0
#include <iterator>
template<typename Value,
typename Alloc = std::allocator<Value>>
class List : ListAlloc<Value, Alloc>
{
#define takeover(N) using ListAlloc<Value,Alloc>::N;
takeover(_alloc_copy)
takeover(_alloc_make_node_notify)
takeover(_alloc_make_node)
takeover(_alloc_fill_n)
takeover(_alloc_swap)
takeover(_alloc_destroy_all_prev)
takeover(_alloc_destroy_node)
takeover(mAllocator)
takeover(mStart)
takeover(mEnd)
typedef typename List::allocator_traits allocator_traits;
public:
takeover( get_allocator )
takeover( max_size )
#undef takeover
#define takeover(N) using typename ListAlloc<Value,Alloc>::N;
takeover(NodeBase)
private:
takeover(Node)
public:
takeover(value_type)
takeover(reference)
takeover(const_reference)
takeover(pointer)
takeover(const_pointer)
takeover(difference_type)
takeover(size_type)
takeover(allocator_type)
#define DEFINE_OP( class, mem, ch ) \
class& operator ch() noexcept \
{ \
p = p->mem; \
return *this; \
}
#define DEFINE_POST_OP( class, ch ) \
class operator ch(int) noexcept \
{ \
auto rval = *this; \
ch*this; \
return rval; \
}
struct iterator : std::iterator<std::bidirectional_iterator_tag,
value_type,
difference_type,
pointer,
reference>
{
private:
friend class List;
friend class const_iterator;
NodeBase* p;
iterator( NodeBase* p ) noexcept :
p{p} {}
public:
iterator() noexcept :
iterator{nullptr} {}
reference operator*() noexcept { return static_cast<Node*>(p)->data; }
pointer operator->() noexcept { return &static_cast<Node*>(p)->data; }
DEFINE_OP( iterator, next, ++ )
DEFINE_OP( iterator, prev, -- )
DEFINE_POST_OP( iterator, ++ )
DEFINE_POST_OP( iterator, -- )
friend bool operator==( iterator lhs, iterator rhs ) noexcept { return lhs.p == rhs.p; }
friend bool operator!=( iterator lhs, iterator rhs ) noexcept { return !(lhs == rhs); }
};
struct const_iterator : std::iterator<std::bidirectional_iterator_tag,
value_type const,
difference_type,
const_pointer,
const_reference>
{
private:
friend class List;
mutable NodeBase* p;
const_iterator( NodeBase* p ) noexcept :
p{p} {}
public:
const_iterator() noexcept :
const_iterator{nullptr} {}
const_iterator( iterator it ) noexcept :
p{it.p} {}
const_reference operator*() noexcept { return static_cast<Node*>(p)->data; }
const_pointer operator->() noexcept { return &static_cast<Node*>(p)->data; }
DEFINE_OP( const_iterator, next, ++ )
DEFINE_OP( const_iterator, prev, -- )
DEFINE_POST_OP( const_iterator, ++ )
DEFINE_POST_OP( const_iterator, -- )
friend bool operator==( const_iterator lhs, const_iterator rhs ) noexcept { return lhs.p == rhs.p; }
friend bool operator!=( const_iterator lhs, const_iterator rhs ) noexcept { return !(lhs == rhs); }
};
#undef DEFINE_POST_OP
#undef DEFINE_OP
typedef std::reverse_iterator<iterator> reverse_iterator;
typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
private:
size_type mSize;
/// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void _assign_to( std::pair<Node*, Node*> pair )
{
mStart.next = pair.first;
mEnd.prev = pair.second;
}
template<typename scenario_bool>
void _swap( List& other )
{
std::swap( mStart, other.mStart );
std::swap( mEnd, other.mEnd );
std::swap( mSize, other.mSize );
_alloc_swap( mAllocator, other.mAllocator, scenario_bool{} );
}
public:
void swap( List& other )
{
_swap<typename allocator_traits::propagate_on_container_swap>(other);
}
explicit List( allocator_type const& alloc = allocator_type{} ):
ListAlloc<Value, Alloc>{NodeBase{ nullptr, &mEnd }, // Start-NodeBase
NodeBase{ &mStart, nullptr }, // End-NodeBase
alloc},
mSize{0} {}
explicit List( size_type count,
const_reference value,
allocator_type const& alloc = allocator_type{} ):
ListAlloc<Value, Alloc>{alloc},
mSize{count}
{
_assign_to( _alloc_fill_n(count, mAllocator.address(value)) );
}
explicit List( size_type count,
allocator_type const& alloc = allocator_type{} ):
ListAlloc<Value, Alloc>{alloc},
mSize{count}
{
_assign_to( _alloc_fill_n(count) );
}
template<typename InputIterator
TAG_CHECK(InputIterator, std::input_iterator_tag)>
explicit List( InputIterator first,
InputIterator last,
allocator_type const& alloc = allocator_type{} ):
List{alloc}
{
assign(first, last);
}
List( std::initializer_list<value_type> ilist,
allocator_type const& alloc = allocator_type{} ):
List(std::begin(ilist), std::end(ilist), alloc) {}
List( List const& l ):
ListAlloc<Value, Alloc>{ allocator_traits::select_on_container_copy_construction(l.mAllocator) },
mSize{l.mSize}
{ _assign_to( _alloc_copy( mSize, l.cbegin() ) ); }
List( List const& l, allocator_type const& alloc ):
ListAlloc<Value, Alloc>{alloc},
mSize{l.mSize}
{ _assign_to( _alloc_copy( mSize, l.cbegin() ) ); }
List& operator=( List const& l )
{
clear();
if( allocator_traits::propagate_on_container_copy_assignment::value )
mAllocator = l.mAllocator;
_assign_to( _alloc_copy( l.mSize, l.cbegin() ) );
mSize = l.mSize;
return *this;
}
List( List&& l ):
List{}
{ _swap<std::false_type>(l); }
List( List&& l, allocator_type const& alloc ):
List{alloc}
{ _swap<std::false_type>(l); }
List& operator=( List&& l )
{
clear();
_swap<allocator_traits::propagate_on_container_move_assignment>(l);
}
~List() // noexcept (ist schon default)
{
clear();
}
void assign( size_type count, const_reference value )
{
clear();
mSize = count;
_assign_to( _alloc_fill_n(count, mAllocator.address(value)) );
}
template<typename InputIterator>
void assign( InputIterator first, InputIterator last )
{
clear();
insert( cbegin(), first, last );
}
void assign( std::initializer_list<value_type> ilist )
{
assign(std::begin(ilist), std::end(ilist));
}
size_type size() const { return mSize; }
bool empty() const { return size() == 0; }
void clear()
{
_alloc_destroy_all_prev( &mStart, mEnd.prev );
mSize = 0;
}
iterator begin() noexcept { return mStart.next ; }
iterator end() noexcept { return &mEnd ; }
const_iterator begin() const noexcept { return mStart.next ; }
const_iterator end() const noexcept { return &mEnd ; }
const_iterator cbegin() const noexcept { return begin(); }
const_iterator cend() const noexcept { return end() ; }
reverse_iterator rbegin() { return reverse_iterator{begin()}; }
reverse_iterator rend() { return reverse_iterator{end() }; }
const_reverse_iterator rbegin() const { return const_reverse_iterator{cbegin()}; }
const_reverse_iterator rend() const { return const_reverse_iterator{cend() }; }
const_reverse_iterator crbegin() const { return rbegin(); }
const_reverse_iterator crend() const { return rend() ; }
reference front() noexcept { return *begin(); }
reference back() noexcept { return *--iterator{end()}; }
const_reference front() const noexcept { return *cbegin(); }
const_reference back() const noexcept { return *--const_iterator{cend()}; }
template<typename... ValueT>
iterator emplace( const_iterator it, ValueT&&... v )
{
auto node = _alloc_make_node_notify( it.p->prev, it.p, std::forward<ValueT>(v)... );
++mSize;
return node;
}
iterator insert( const_iterator it, const_reference value )
{ return emplace(it, value); }
iterator insert( const_iterator it, value_type&& value )
{ return emplace(it, value); }
iterator insert( const_iterator it, size_type count, const_reference value )
{
if( count == 0 )
return it;
auto rval = insert( it, value );
while( --count )
insert( it, value );
return rval;
}
template<typename InputIterator
TAG_CHECK(InputIterator, std::input_iterator_tag)>
iterator insert( const_iterator it, InputIterator first, InputIterator last )
{
if( first == last )
return it;
auto rval = insert( it, *first );
while( ++first != last )
insert( it, *first );
return rval;
}
iterator insert( const_iterator it, std::initializer_list<value_type> ilist )
{
insert( it, std::begin(ilist), std::end(ilist) );
}
template<typename... Args>
iterator emplace_back( Args&&... args )
{
return emplace( cend(), std::forward<Args>(args)... );
}
template<typename... Args>
iterator emplace_front( Args&&... args )
{
return emplace( cbegin(), std::forward<Args>(args)... );
}
void push_back( const_reference v )
{ insert( cend(), v ); }
void push_back( value_type&& v )
{ insert( cend(), v ); }
void push_front( const_reference v )
{ insert( cbegin(), v ); }
void push_front( value_type&& v )
{ insert( cbegin(), v ); }
iterator erase( const_iterator it )
{
auto rval = it.p->next;
it.p->prev->next = it.p->next;
it.p->next->prev = it.p->prev;
_alloc_destroy_node( static_cast<Node*>(it.p) );
--mSize;
return rval;
}
iterator erase( const_iterator first, const_iterator last )
{
while( first != last )
erase( first++ );
return last;
}
void pop_back()
{ erase( --end() ); }
void pop_front()
{ erase( begin() ); }
private:
template<typename ... Args>
void _resize_impl( size_type new_size, Args&&... args )
{
if( new_size > size() )
for( size_type n = size(); n != new_size; ++n )
emplace_back( std::forward<Args>(args)... );
else if( new_size < size() )
erase( std::next(cbegin(), new_size), cend() );
}
public:
void resize( size_type new_size, const_reference value )
{ _resize_impl( new_size, value ); }
void resize( size_type new_size )
{ _resize_impl( new_size ); }
};
#include <iostream>
#include <iterator>
#include <cassert>
#include <string>
int main()
{
#define check(s) assert(std::string(l.begin(), l.end()) == s)
List<char> l;
l.push_back('m'); check("m");
l.push_back('z'); check("mz");
l.push_front('a'); check("amz");
l.erase(l.begin());check("mz");
l.insert(l.begin(), 'a');check("amz");
l.insert(l.end(), '!'); check("amz!");
l.pop_front(); check("mz!");
l.pop_back(); check("mz");
List<char>::iterator i=l.begin(); i++;
i = l.insert(i, '-'); check("m-z");
i = l.erase(i); check("mz");
l.erase(i); check("m");
l.push_front('a'); check("am");
l.push_back('z'); check("amz");
i=l.end(); i--; i--;
i=l.erase(i); check("az");
}