Created
December 23, 2019 19:15
-
-
Save garettbass/9f0ecc1d92038171d8125284a1e27a00 to your computer and use it in GitHub Desktop.
A simple embedded link and list
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
///usr/bin/env \ | |
[ -n "${PATHEXT}" ] && ext='.exe'; \ | |
bin="$(dirname $0)/$(basename ${0%.*})$ext"; \ | |
c++ -std=c++14 -Werror -o $bin $0 \ | |
&& $bin "$@"; \ | |
status=$?; \ | |
rm $bin; \ | |
exit $status | |
#include <cstdio> | |
#include <new> | |
//------------------------------------------------------------------------------ | |
class link { | |
link* _next; | |
link* _prev; | |
public: // types | |
template <typename T, link T::*Link> | |
class list; | |
public: // structors | |
link() | |
: _next(this) | |
, _prev(this) {} | |
link(link&& src) | |
: _next(src._next == &src ? this : src._next) | |
, _prev(src._prev == &src ? this : src._prev) { | |
new(&src) link(); | |
_next->_prev = this; | |
_prev->_next = this; | |
} | |
link& operator=(link&& src) { | |
if (this != &src) { // do not move into self | |
this->~link(); new(this) link(std::move(src)); | |
} | |
return *this; | |
} | |
~link() { unlink(); } | |
public: // properties | |
bool is_linked() const { return _next != this; } | |
bool is_unlinked() const { return _next == this; } | |
link* next() const { return _next; } | |
link* prev() const { return _prev; } | |
public: // methods | |
void insert_after(link* link) { | |
_next->_prev = _prev; | |
_prev->_next = _next; | |
_next = link->_next; | |
_prev = link; | |
_next->_prev = this; | |
_prev->_next = this; | |
} | |
void insert_before(link* link) { | |
link->insert_after(this); | |
} | |
void unlink() { | |
_next->_prev = _prev; | |
_prev->_next = _next; | |
_next = _prev = this; | |
} | |
}; | |
//------------------------------------------------------------------------------ | |
template <typename T, link T::*Link> | |
class link::list { | |
enum : size_t { LINK_OFFSET = size_t(&(((T*)nullptr)->*Link)) }; | |
link _sentinel; | |
public: // structors | |
list() = default; | |
~list() { clear(); } | |
public: // properties | |
bool empty() const { return _sentinel.is_unlinked(); } | |
size_t size() const { | |
size_t n=0; | |
for(auto a=begin(),z=end();a!=z;++a,++n); | |
return n; | |
} | |
public: // methods | |
void clear() { while (_sentinel.is_linked()) _sentinel.next()->unlink(); } | |
void push_back(T& t) { (t.*Link).insert_after(_sentinel.prev()); } | |
void push_front(T& t) { (t.*Link).insert_before(_sentinel.next()); } | |
public: // iterators | |
template<link* (link::*Next)() const> | |
class iterator { | |
const link* _link; | |
private: | |
friend class list; | |
iterator(const link* link) : _link(link) {} | |
public: | |
friend bool operator==(const iterator& a, const iterator& b) { | |
return a._link == b._link; | |
} | |
friend bool operator!=(const iterator& a, const iterator& b) { | |
return a._link != b._link; | |
} | |
T& operator*() const { return *(T*)(size_t(_link)-LINK_OFFSET); } | |
T& operator->() const { return *(T*)(size_t(_link)-LINK_OFFSET); } | |
iterator& operator++() { _link = (_link->*Next)(); return *this; } | |
}; | |
using foreward_iterator = iterator<&link::next>; | |
using backward_iterator = iterator<&link::prev>; | |
foreward_iterator begin() const { return _sentinel.next(); } | |
foreward_iterator end() const { return &_sentinel; } | |
backward_iterator rbegin() const { return _sentinel.prev(); } | |
backward_iterator rend() const { return &_sentinel; } | |
}; | |
//------------------------------------------------------------------------------ | |
struct node { | |
int id; | |
private: | |
link _link; | |
friend class link::list<node, &node::_link>; | |
public: | |
using list = link::list<node, &node::_link>; | |
node(int id) : id(id) {} | |
node(node&&) = default; | |
node& operator=(node&&) = default; | |
}; | |
//------------------------------------------------------------------------------ | |
int main() { | |
node::list node_list; | |
{ | |
printf("node_list.size(): %zu\n", node_list.size()); | |
node node_array[10] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; | |
puts("node_array:"); | |
for (auto& node : node_array) { | |
printf(" node.id:%i\n", node.id); | |
node_list.push_back(node); | |
} | |
printf("node_list.size(): %zu\n", node_list.size()); | |
puts("node_list:"); | |
for (auto& node : node_list) { | |
printf(" node.id:%i\n", node.id); | |
} | |
puts("node_list (reverse):"); | |
for (auto itr = node_list.rbegin(), end = node_list.rend(); itr != end; ++itr) { | |
auto& node = *itr; | |
printf(" node.id:%i\n", node.id); | |
} | |
} | |
// destructors of node_array empty the list | |
printf("node_list.empty(): %s\n", node_list.empty() ? "true" : "false"); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment