8#ifndef Sawyer_TreeVertex_H
9#define Sawyer_TreeVertex_H
11#include <Sawyer/Assert.h>
12#include <Sawyer/Exception.h>
14#include <boost/lexical_cast.hpp>
15#include <boost/range/adaptor/reversed.hpp>
16#include <boost/signals2/signal.hpp>
21#ifdef SAWYER_HAVE_BOOST_SERIALIZATION
22#include <boost/serialization/shared_ptr.hpp>
23#include <boost/serialization/vector.hpp>
26#ifdef SAWYER_HAVE_CEREAL
27#include <cereal/types/memory.hpp>
28#include <cereal/types/vector.hpp>
125class Vertex:
public std::enable_shared_from_this<Vertex<B>> {
138 template<
class T>
class Edge;
175 :
Exception(
"insertion of vertex would cause a cycle in the tree",
vertex) {}
237 explicit operator bool()
const {
238 return parent_ !=
nullptr;
243 template<
class T>
friend class Edge;
252 enum class Link { NO, YES };
266 virtual ~EdgeBase() {}
268 EdgeBase(
const EdgeBase&) =
delete;
269 EdgeBase& operator=(
const EdgeBase&) =
delete;
271 : prev(prev), parent_(
parent) {}
274 virtual size_t size()
const = 0;
283 template<
class T,
class Visitor>
286 if (
auto retval = prev->traverse<T>(visitor))
289 for (
size_t i = 0; i < size(); ++i) {
291 if (
auto retval =
child->template traverse<T>(visitor))
300 std::vector<UserBasePtr> immediateChildren()
const {
301 std::vector<UserBasePtr> retval;
302 retval.reserve(size());
303 for (
size_t i = 0; i < size(); ++i)
311 std::vector<EdgeBase*> baseEdgeList()
const {
312 std::vector<EdgeBase*> retval;
314 retval.push_back(
child);
315 std::reverse(retval.begin(), retval.end());
371 ChangeSignal beforeChange_;
372 ChangeSignal afterChange_;
374#ifdef SAWYER_HAVE_CEREAL
376 friend class cereal::access;
378 template<
class Archive>
379 void CEREAL_SERIALIZE_FUNCTION_NAME(Archive &archive) {
380 archive(cereal::make_nvp(
"child", child_));
384#ifdef SAWYER_HAVE_BOOST_SERIALIZATION
386 friend class boost::serialization::access;
388 template<
class Archive>
389 void serialize(Archive &archive,
const unsigned ) {
390 archive & boost::serialization::make_nvp(
"child", child_);
453 if (newChild != child_) {
454 checkChildInsertion(newChild);
457 beforeChange_(oldChild, newChild);
461 child_->parent.reset();
467 newChild->parent.set(this->parent_);
471 afterChange_(oldChild, newChild);
477 return (*
this) =
parent();
482 explicit operator bool()
const {
483 return child_ !=
nullptr;
491 boost::signals2::connection
beforeChange(
const typename ChangeSignal::slot_type &slot) {
492 return beforeChange_.connect(slot);
499 boost::signals2::connection
afterChange(
const typename ChangeSignal::slot_type &slot) {
500 return afterChange_.connect(slot);
506 size_t size()
const override {
511 ASSERT_always_require(0 == i);
533 using Vector = std::vector<std::unique_ptr<Edge<Child>>>;
534 using ResizeSignal = boost::signals2::signal<void(
int,
ChildPtr)>;
538 using size_type =
typename Vector::size_type;
539 using difference_type =
typename Vector::difference_type;
544 template<
class BaseIterator>
546 BaseIterator baseIterator_;
550 Iterator(
const BaseIterator &baseIterator)
551 : baseIterator_(baseIterator) {}
554 : baseIterator_(other.baseIterator_) {}
557 baseIterator_ = other.baseIterator_;
583 Iterator& operator+=(difference_type n) {
588 Iterator operator+(difference_type n)
const {
594 Iterator& operator-=(difference_type n) {
599 Iterator operator-(difference_type n)
const {
605 difference_type operator-(
const Iterator &other)
const {
606 return other.baseIterator_ - baseIterator_;
609 auto& operator*()
const {
610 ASSERT_not_null(*baseIterator_);
611 return **baseIterator_;
614 auto& operator->()
const {
615 ASSERT_not_null(*baseIterator_);
616 return &**baseIterator_;
619 auto& operator[](difference_type i)
const {
620 ASSERT_not_null(baseIterator_[i]);
621 return *baseIterator_[i];
624 bool operator==(
const Iterator &other)
const {
625 return baseIterator_ == other.baseIterator_;
628 bool operator!=(
const Iterator &other)
const {
629 return baseIterator_ != other.baseIterator_;
632 bool operator<(
const Iterator &other)
const {
633 return baseIterator_ < other.baseIterator_;
636 bool operator<=(
const Iterator &other)
const {
637 return baseIterator_ <= other.baseIterator_;
640 bool operator>(
const Iterator &other)
const {
641 return baseIterator_ > other.baseIterator_;
644 bool operator>=(
const Iterator &other)
const {
645 return baseIterator_ >= other.baseIterator_;
654 ResizeSignal beforeResize_;
655 ResizeSignal afterResize_;
657#ifdef SAWYER_HAVE_CEREAL
659 friend class cereal::access;
661 template<
class Archive>
662 void CEREAL_SAVE_FUNCTION_NAME(Archive &archive)
const {
663 const size_t nEdges = edges_.size();
664 archive(CEREAL_NVP(nEdges));
665 for (
size_t i = 0; i < nEdges; ++i) {
667 archive(CEREAL_NVP(
child));
671 template<
class Archive>
672 void CEREAL_LOAD_FUNCTION_NAME(Archive &archive) {
674 archive(CEREAL_NVP(nEdges));
675 for (
size_t i = 0; i < nEdges; ++i) {
677 archive(CEREAL_NVP(
child));
683#ifdef SAWYER_HAVE_BOOST_SERIALIZATION
685 friend class boost::serialization::access;
687 template<
class Archive>
688 void save(Archive &archive,
const unsigned )
const {
689 const size_t nEdges = edges_.size();
690 archive <<BOOST_SERIALIZATION_NVP(nEdges);
691 for (
size_t i = 0; i < nEdges; ++i) {
693 archive <<BOOST_SERIALIZATION_NVP(
child);
697 template<
class Archive>
698 void load(Archive &archive,
const unsigned ) {
700 archive >>BOOST_SERIALIZATION_NVP(nEdges);
701 for (
size_t i = 0; i < nEdges; ++i) {
703 archive >>BOOST_SERIALIZATION_NVP(
child);
708 BOOST_SERIALIZATION_SPLIT_MEMBER();
727 return edges_.empty();
734 return edges_.size();
744 return edges_.capacity();
752 beforeResize_(+1, elmt);
753 auto edge = std::unique_ptr<Edge<Child>>(
new Edge<Child>(Link::NO, this->parent_, elmt));
754 edges_.push_back(std::move(edge));
755 afterResize_(+1, elmt);
762 ASSERT_forbid(edges_.empty());
763 ChildPtr removed = (*edges_.back())();
764 beforeResize_(-1, removed);
766 afterResize_(-1, removed);
773 ASSERT_require(i < edges_.size());
774 return *edges_.at(i);
777 ASSERT_require(i < edges_.size());
778 return *edges_.at(i);
792 ASSERT_forbid(
empty());
796 ASSERT_forbid(
empty());
805 ASSERT_forbid(
empty());
809 ASSERT_forbid(
empty());
829 boost::signals2::connection
beforeResize(
const typename ResizeSignal::slot_type &slot) {
830 return beforeResize_.connect(slot);
838 boost::signals2::connection
afterResize(
const typename ResizeSignal::slot_type &slot) {
839 return afterResize_.connect(slot);
859 EdgeBase *treeEdges_ =
nullptr;
861#ifdef SAWYER_HAVE_CEREAL
863 friend class cereal::access;
867 template<
class Archive>
869 CEREAL_SAVE_FUNCTION_NAME(Archive &archive)
const {
870 const std::vector<EdgeBase*> baseEdgeList = [
this]() {
872 return treeEdges_->baseEdgeList();
874 return std::vector<EdgeBase*>();
878 const size_t nBases = baseEdgeList.size();
879 archive(CEREAL_NVP(nBases));
880 for (
const EdgeBase *baseEdge: baseEdgeList) {
881 const size_t baseOffset = (
char*)baseEdge - (
char*)
this;
882 archive(CEREAL_NVP(baseOffset));
886 template<
class Archive>
888 CEREAL_LOAD_FUNCTION_NAME(Archive &archive)
const {
889 const size_t nBases = 0;
890 archive(CEREAL_NVP(nBases));
891 for (
size_t i = 0; i < nBases; ++i) {
892 size_t baseOffset = 0;
893 archive(CEREAL_NVP(baseOffset));
894 EdgeBase *baseEdge = (EdgeBase*)((
char*)
this + baseOffset);
895 baseEdge->reset(treeEdges_);
896 treeEdges_ = baseEdge;
901#ifdef SAWYER_HAVE_BOOST_SERIALIZATION
903 friend class boost::serialization::access;
907 template<
class Archive>
909 save(Archive &archive,
const unsigned )
const {
910 const std::vector<EdgeBase*> baseEdgeList = [
this]() {
912 return treeEdges_->baseEdgeList();
914 return std::vector<EdgeBase*>();
918 const size_t nBases = baseEdgeList.size();
919 archive <<BOOST_SERIALIZATION_NVP(nBases);
920 for (
const EdgeBase *baseEdge: baseEdgeList) {
921 const size_t baseOffset = (
char*)baseEdge - (
char*)
this;
922 archive <<BOOST_SERIALIZATION_NVP(baseOffset);
926 template<
class Archive>
928 load(Archive &archive,
const unsigned ) {
929 const size_t nBases = 0;
930 archive >>BOOST_SERIALIZATION_NVP(nBases);
931 for (
size_t i = 0; i < nBases; ++i) {
932 size_t baseOffset = 0;
933 archive >>BOOST_SERIALIZATION_NVP(baseOffset);
934 EdgeBase *baseEdge = (EdgeBase*)((
char*)
this + baseOffset);
935 baseEdge->reset(treeEdges_);
936 treeEdges_ = baseEdge;
940 BOOST_SERIALIZATION_SPLIT_MEMBER();
947 virtual void destructorHelper() {}
991 template<
class T,
class Visitor>
993 auto vertex = std::dynamic_pointer_cast<T>(this->
pointer());
1000 if (
auto result =
parent()->
template traverseReverse<T>(visitor))
1019 template<
class T,
class Visitor>
1021 auto vertex = std::dynamic_pointer_cast<T>(this->
pointer());
1027 if (
auto retval = treeEdges_->template traverse<T>(visitor))
1044 template<
class T,
class Visitor>
1046 return traverse<T>([&visitor] (
const std::shared_ptr<T> &vertex,
TraversalEvent event) {
1048 return visitor(vertex);
1050 return decltype(visitor(vertex))();
1062 template<
class T,
class Visitor>
1064 return traverse<T>([&visitor] (
const std::shared_ptr<T> &vertex,
TraversalEvent event) {
1066 return visitor(vertex);
1068 return decltype(visitor(vertex))();
1076 return traverseReverse<T>([](
const std::shared_ptr<T> &vertex,
TraversalEvent event) {
1084 return traverseReverse<T>([](
const std::shared_ptr<T> &vertex,
TraversalEvent event) {
1095 std::vector<std::shared_ptr<T>> retval;
1096 traverse<T>([&retval](
const std::shared_ptr<T> &vertex,
TraversalEvent event) {
1098 retval.push_back(vertex);
1121 ASSERT_require(this->parent_ ==
nullptr);
1141 ASSERT_not_null(parent_);
1142 return parent_->pointer();
1148 return ptr.get() == parent_;
1154 return ptr.get() != parent_;
1160 return parent_ == other.parent_;
1166 return parent_ != other.parent_;
1173 return parent_ == other();
1180 return parent_ != other();
1209 : EdgeBase(parent.treeEdges_, parent) {
1210 this->parent_.treeEdges_ =
this;
1213#ifndef DOCUMENTATION
1218 checkChildInsertion(
child);
1219 this->parent_.treeEdges_ =
this;
1225#ifndef DOCUMENTATION
1229 : EdgeBase(Link::YES == link ?
parent.treeEdges_ : nullptr,
parent), child_(
child) {
1230 checkChildInsertion(
child);
1231 if (Link::YES == link)
1232 this->parent_.treeEdges_ =
this;
1244 throw InsertionError(
child);
1246 for (
const UserBase *p = &this->parent_; p; p = p->parent().get()) {
1247 if (p ==
child.get())
1248 throw CycleError(
child);
1256const std::shared_ptr<T>&
1258 ASSERT_not_null(child_);
1264const std::shared_ptr<T>&
1273 return child_ == ptr;
1280 return child_ != ptr;
1287 return child_ == other();
1294 return child_.get() != other();
1302 return child_.get() == other().get();
1310 return child_.get() != other().get();
1321 this->parent_.treeEdges_ =
this;
1335 auto retval = std::dynamic_pointer_cast<UserBase>(this->shared_from_this());
1336 ASSERT_not_null(retval);
1344 return std::dynamic_pointer_cast<T>(this->shared_from_this());
1350 std::vector<EdgeBase*> edges;
1351 for (
const EdgeBase *edge = treeEdges_; edge; edge = edge->prev)
1352 edges.push_back(edge);
1353 for (
const EdgeBase *edge: boost::adaptors::reverse(edges)) {
1354 if (i < edge->size()) {
1355 return edge->pointer(i);
1367 for (
const EdgeBase *edge = treeEdges_; edge; edge = edge->prev)
Base class for Sawyer runtime errors.
RuntimeError(const std::string &mesg)
Constructor taking a description of the error.
Error when attaching a vertex to a tree would cause a cycle.
CycleError(const UserBasePtr &vertex)
Construct a new error with the vertex that caused the error.
A 1:N tree edge from parent to children.
size_t size() const override
Number of child edges.
boost::signals2::connection beforeResize(const typename ResizeSignal::slot_type &slot)
Functors to call before the vector size is changed.
void push_back(const ChildPtr &elmt)
Insert a child pointer at the end of this vertex.
Edge< Child > & at(size_t i)
Return the i'th edge.
const Edge< Child > & at(size_t i) const
Return the i'th edge.
boost::signals2::connection afterResize(const typename ResizeSignal::slot_type &slot)
Functors to call after the vector size is changed.
size_t capacity() const
Reserved capacity.
const Edge< Child > & front() const
Return the first edge.
Edge< Child > & operator[](size_t i)
Return the i'th edge.
iterator begin()
Return an iterator to the first edge.
Edge< Child > & front()
Return the first edge.
const Edge< Child > & operator[](size_t i) const
Return the i'th edge.
void reserve(size_t n)
Reserve space so the child edge vector can grow without being reallocated.
bool empty() const
Test whether vector is empty.
~EdgeVector()
Destructor clears children's parents.
void pop_back()
Erase a child edge from the end of this vertex.
std::shared_ptr< Child > ChildPtr
Type of pointers to children.
T Child
Type of children being pointed to.
const Edge< Child > & back() const
Return the last edge.
Edge< Child > & back()
Return the last edge.
iterator end()
Return an iterator to one past the last edge.
A parent-to-child edge in a tree.
T Child
Type of child being pointed to.
Edge & operator=(const ChildPtr &newChild)
Assign a pointer to a child.
std::shared_ptr< Child > ChildPtr
Type of pointer to the child.
bool operator==(const UserBasePtr &) const
Compare the child pointer to another pointer.
bool operator!=(const UserBasePtr &) const
Compare the child pointer to another pointer.
boost::signals2::connection beforeChange(const typename ChangeSignal::slot_type &slot)
Functors to call before the child pointer is changed.
const ChildPtr & operator()() const
Return the child if there is one, else null.
boost::signals2::connection afterChange(const typename ChangeSignal::slot_type &slot)
Functors to call after the child pointer is changed.
Edge & operator=(const ReverseEdge &parent)
Assign a pointer to a child.
~Edge()
Destructor clears child's parent.
const ChildPtr & operator->() const
Return the child if there is one, else null.
Edge(UserBase &parent, const ChildPtr &child)
Construct a child edge that belongs to the specified parent.
Base class for errors and exceptions for this vertex type.
UserBasePtr vertex
Vertex that caused the error.
Exception(const std::string &mesg, const UserBasePtr &vertex)
Construct a new error with the specified message and the causing vertex.
Error when attaching a vertex to a tree and the vertex is already attached somewhere else.
InsertionError(const UserBasePtr &vertex)
Construct a new error with the vertex that caused the error.
Points from a child to a parent in the tree.
UserBasePtr operator->() const
Return the parent if there is one, else null.
bool operator==(const UserBasePtr &) const
Compare the parent pointer to another pointer.
bool operator!=(const UserBasePtr &) const
Compare the parent pointer to another pointer.
UserBasePtr operator()() const
Return the parent if there is one, else null.
Base class for tree vertices.
auto traversePost(const Visitor &visitor)
Post-order forward traversal.
std::shared_ptr< T > isa()
Tests whether this object is a certain type.
std::shared_ptr< T > findFirstAncestor()
Traversal that finds the closest ancestor of type T or derived from T.
auto traversePre(const Visitor &visitor)
Pre-order forward traversal.
ReverseEdge parent
Pointer to the parent in the tree.
std::vector< std::shared_ptr< T > > findDescendants()
Traversal that finds all the descendants of a particular type.
auto traverseReverse(const Visitor &visitor)
Traverse in reverse direction from children to parents.
std::shared_ptr< UserBase > UserBasePtr
Pointer to user's base class.
UserBasePtr child(size_t i) const
Returns the pointer for a child.
UserBasePtr pointer()
Returns a shared pointer to this vertex.
B UserBase
User's base class.
std::shared_ptr< T > findLastAncestor()
Traversal that finds the farthest ancestor of type T or derived from T.
auto traverse(const Visitor &visitor)
Traverse in forward direction from parents to children.
size_t nChildren() const
Returns the number of children.
TraversalEvent
Traversal event.
@ ENTER
Pre-order visitation.
@ LEAVE
Post-order visitation.