#include <memory>
#include <cstddef>
template<typename Value,
typename Alloc = std::allocator<Value>>
struct ListAlloc
{
using base = ListAlloc;
using allocator_type = Alloc;
using allocator_traits = std::allocator_traits<allocator_type>;
#define takeover( N ) using N = typename allocator_traits::N;
takeover(value_type)
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( allocator_type const& alloc = allocator_type{} ):
mAllocator{alloc} {}
// ///////////////////////////////////////////////////////////////////////////////////////////
struct Node
{
Node *prev,
*next;
pointer data;
Node( Node* prev, Node* next, pointer data = nullptr ) noexcept :
prev{prev},
next{next},
data{data} {}
Node() noexcept :
Node{nullptr, nullptr} {} // No increment (delegate ctor)
};
void _alloc_destroy_node( Node* ptr )
{
allocator_traits::destroy( mAllocator, ptr->data );
allocator_traits::deallocate( mAllocator, ptr->data, 1 );
delete ptr;
}
void _alloc_destroy_all_prev( Node* ptr )
{
delete ptr->next; // Ist ptr der letzte Knoten, dann ist ptr->next 0. Sonst wird der nächste das end()-Ãquivalent sein.
Node* old_ptr;
while( ptr )
{
old_ptr = ptr;
ptr = ptr->prev;
_alloc_destroy_node(old_ptr);
}
}
private:
template<typename Func>
std::pair<Node*, Node*> _alloc_create_impl( size_type n, Func constructor )
{
Node* ptr = new Node;
Node* const rval = ptr;
while( n-- )
{
bool constructed = false;
try
{
ptr->data = allocator_traits::allocate( mAllocator, 1 );
constructor( ptr->data );
constructed = true;
ptr->next = new Node{ptr, nullptr};
}
catch(...)
{
if( constructed )
allocator_traits::destroy( mAllocator, ptr->data );
if( ptr->data )
allocator_traits::deallocate( mAllocator, ptr->data, 1 );
ptr = ptr->prev;
_alloc_destroy_all_prev( ptr );
throw;
}
auto old_ptr = ptr;
ptr = ptr->next;
ptr->prev = old_ptr;
}
return {rval, ptr};
}
public:
std::pair<Node*, Node*> _alloc_fill_n( size_type n )
{
return _alloc_create_impl( n, [&](pointer p){ allocator_traits::construct( mAllocator, p ); } );
}
std::pair<Node*, Node*> _alloc_fill_n( size_type n, const_pointer value )
{
return _alloc_create_impl( n, [&](pointer p){ allocator_traits::construct( mAllocator, p, *value ); } );
}
template<typename InputIterator>
std::pair<Node*, Node*> _alloc_copy( size_type n, InputIterator first )
{
return _alloc_create_impl( n, [&]( pointer p ){ allocator_traits::construct( mAllocator, p, *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>
{
using base = typename List::base;
#define takeover(N) using base::N;
takeover(_alloc_copy)
takeover(_alloc_fill_n)
takeover(_alloc_swap)
takeover(_alloc_destroy_all_prev)
takeover(_alloc_destroy_node)
takeover(mAllocator)
typedef typename base::allocator_traits allocator_traits;
public:
takeover( get_allocator )
takeover( max_size )
#undef takeover
#define takeover(N) using typename base::N;
takeover(Node)
takeover(value_type)
takeover(reference)
takeover(const_reference)
takeover(pointer)
takeover(const_pointer)
takeover(difference_type)
takeover(size_type)
takeover(allocator_type)
struct iterator : std::iterator<std::bidirectional_iterator_tag,
value_type,
difference_type,
pointer,
reference>
{
private:
friend class List;
mutable Node* p;
iterator( Node* p ) noexcept :
p{p} {}
public:
iterator() noexcept :
iterator{nullptr} {}
reference operator*() noexcept { return *(p->data); }
pointer operator->() noexcept { return p->data; }
const_reference operator*() const noexcept { return *(p->data); }
const_pointer operator->() const noexcept { return p->data; }
friend bool operator==( iterator lhs, iterator rhs ) noexcept { return lhs.p == rhs.p; }
friend bool operator!=( iterator lhs, iterator rhs ) noexcept { return !(lhs == rhs); }
#define DEFINE_OP( mem, ch, specifier ) \
iterator specifier& operator ch() specifier noexcept \
{ \
p = p->mem; \
return *this; \
}
DEFINE_OP( next, ++, )
DEFINE_OP( prev, --, )
DEFINE_OP( next, ++, const )
DEFINE_OP( prev, --, const )
#define DEFINE_POST_OP( ch, specifier ) \
iterator specifier operator ch(int) specifier noexcept \
{ \
auto rval = *this; \
ch*this; \
return rval; \
}
DEFINE_POST_OP( ++, )
DEFINE_POST_OP( --, )
DEFINE_POST_OP( ++, const )
DEFINE_POST_OP( --, const )
#undef DEFINE_POST_OP
#undef DEFINE_OP
};
typedef iterator const const_iterator;
typedef std::reverse_iterator<iterator> reverse_iterator;
typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
private:
iterator mStart, mEnd;
size_type mSize;
/// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void _assign_to( std::pair<Node*, Node*> pair )
{
mStart = pair.first;
mEnd = 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{} );
}
/**< Löscht alles. Danach sind beide start und end Iteratoren nicht gültig */
void _shutdown()
{
if( !empty() )
{
--mEnd;
_alloc_destroy_all_prev( mEnd.p );
mSize = 0;
}
else
delete mEnd.p;
}
public:
void swap( List& other )
{
_swap<typename allocator_traits::propagate_on_container_swap>(other);
}
explicit List( allocator_type const& alloc = allocator_type{} ):
base{alloc},
mStart{ new Node },
mEnd{ mStart },
mSize{0} {}
explicit List( size_type count,
const_reference value,
allocator_type const& alloc = allocator_type{} ):
base{alloc},
mSize{count}
{
_assign_to( _alloc_fill_n(count, mAllocator.address(value)) );
}
explicit List( size_type count,
allocator_type const& alloc = allocator_type{} ):
base{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 ):
base{ 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 ):
base{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)
{
_shutdown();
}
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()
{
_shutdown();
mStart = new Node;
mEnd = mStart;
}
iterator begin() noexcept { return mStart ; }
iterator end() noexcept { return mEnd ; }
const_iterator begin() const noexcept { return mStart ; }
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 = new Node{it.p->prev, it.p};
if( it == mStart )
mStart = node;
try
{
node->data = allocator_traits::allocate( mAllocator, 1 );
allocator_traits::construct( mAllocator, node->data, std::forward<ValueT>(v)... );
}
catch(...)
{
if(node->data)
allocator_traits::deallocate(mAllocator, node->data, 1);
throw;
}
if( it.p->prev )
it.p->prev->next = node;
it.p->prev = node;
++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;
if( it == mStart )
mStart = rval;
if( it.p->prev )
it.p->prev->next = it.p->next;
if( it.p->next )
it.p->next->prev = it.p->prev;
_alloc_destroy_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");
}