1213 lines
42 KiB
C++
1213 lines
42 KiB
C++
/**
|
|
* @file lltreeiterators.cpp
|
|
* @author Nat Goodspeed
|
|
* @date 2008-08-20
|
|
* @brief Test of lltreeiterators.h
|
|
*
|
|
* $LicenseInfo:firstyear=2008&license=viewerlgpl$
|
|
* Second Life Viewer Source Code
|
|
* Copyright (C) 2010, Linden Research, Inc.
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation;
|
|
* version 2.1 of the License only.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*
|
|
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
|
* $/LicenseInfo$
|
|
*/
|
|
|
|
// Precompiled header
|
|
#include "linden_common.h"
|
|
|
|
|
|
// STL headers
|
|
// std headers
|
|
#include <iostream>
|
|
#include <sstream>
|
|
#include <string>
|
|
// external library headers
|
|
#include <boost/bind.hpp>
|
|
#include <boost/range/iterator_range.hpp>
|
|
|
|
// associated header
|
|
#include "../lltreeiterators.h"
|
|
#include "../llpointer.h"
|
|
|
|
#include "../test/lltut.h"
|
|
|
|
/*****************************************************************************
|
|
* tut test group
|
|
*****************************************************************************/
|
|
namespace tut
|
|
{
|
|
struct iter_data
|
|
{
|
|
};
|
|
typedef test_group<iter_data> iter_group;
|
|
typedef iter_group::object iter_object;
|
|
tut::iter_group ig("LLTreeIterators");
|
|
} // namespace tut
|
|
|
|
/*****************************************************************************
|
|
* boost::get_pointer() specialization for LLPointer<>
|
|
*****************************************************************************/
|
|
// This specialization of boost::get_pointer() undoubtedly belongs in
|
|
// llmemory.h. It's used by boost::bind() so that you can pass an
|
|
// LLPointer<Foo> as well as a Foo* to a functor such as
|
|
// boost::bind(&Foo::method, _1).
|
|
//namespace boost
|
|
//{
|
|
template <class NODE>
|
|
NODE* get_pointer(const LLPointer<NODE>& ptr) { return ptr.get(); }
|
|
//};
|
|
|
|
/*****************************************************************************
|
|
* ScopeLabel
|
|
*****************************************************************************/
|
|
class ScopeLabel
|
|
{
|
|
public:
|
|
ScopeLabel(const std::string& label): mLabel(label)
|
|
{
|
|
std::cout << "Entering " << mLabel << '\n';
|
|
}
|
|
~ScopeLabel()
|
|
{
|
|
std::cout << "Leaving " << mLabel << '\n';
|
|
}
|
|
private:
|
|
std::string mLabel;
|
|
};
|
|
|
|
/*****************************************************************************
|
|
* Cleanup
|
|
*****************************************************************************/
|
|
// Yes, we realize this is redundant with auto_ptr and LLPointer and all
|
|
// kinds of better mechanisms. But in this particular source file, we need to
|
|
// test nodes managed with plain old dumb pointers as well as nodes managed
|
|
// with LLPointer, so we introduce this mechanism.
|
|
//
|
|
// In the general case, when we declare a Cleanup for some pointer, delete the
|
|
// pointer when the Cleanup goes out of scope.
|
|
template <typename PTRTYPE>
|
|
struct Cleanup
|
|
{
|
|
Cleanup(const PTRTYPE& ptr): mPtr(ptr) {}
|
|
~Cleanup()
|
|
{
|
|
delete mPtr;
|
|
}
|
|
PTRTYPE mPtr;
|
|
};
|
|
|
|
// But when the pointer is an LLPointer<something>, Cleanup is a no-op:
|
|
// LLPointer will handle the cleanup automagically.
|
|
template <typename NODE>
|
|
struct Cleanup< LLPointer<NODE> >
|
|
{
|
|
Cleanup(const LLPointer<NODE>& ptr) {}
|
|
~Cleanup() {}
|
|
};
|
|
|
|
/*****************************************************************************
|
|
* Expected
|
|
*****************************************************************************/
|
|
// Expected is a base class used to capture the expected results -- a sequence
|
|
// of string node names -- from one of our traversals of this example data.
|
|
// Its subclasses initialize it with a pair of string iterators. It's not
|
|
// strictly necessary to customize Expected to model Boost.Range, it's just
|
|
// convenient.
|
|
struct Expected
|
|
{
|
|
template <typename ITER>
|
|
Expected(ITER begin, ITER end):
|
|
strings(begin, end)
|
|
{}
|
|
/*------ The following are to make Expected work with Boost.Range ------*/
|
|
typedef std::vector<std::string> container_type;
|
|
typedef container_type::iterator iterator;
|
|
typedef container_type::const_iterator const_iterator;
|
|
typedef container_type::size_type size_type;
|
|
container_type strings;
|
|
iterator begin() { return strings.begin(); }
|
|
iterator end() { return strings.end(); }
|
|
size_type size() { return strings.size(); }
|
|
const_iterator begin() const { return strings.begin(); }
|
|
const_iterator end() const { return strings.end(); }
|
|
};
|
|
|
|
// We have a couple of generic Expected template subclasses. This list of
|
|
// strings is used for the "else" case when all specializations fail.
|
|
const char* bad_strings[] = { "FAIL" };
|
|
|
|
/*****************************************************************************
|
|
* verify()
|
|
*****************************************************************************/
|
|
// test function: given (an object modeling) a Boost.Range of tree nodes,
|
|
// compare the sequence of visited node names with a range of expected name
|
|
// strings. Report success both with std::cout output and a bool return. The
|
|
// string desc parameter is to identify the different tests.
|
|
template <typename NODERANGE, typename STRINGRANGE>
|
|
bool verify(const std::string& desc, NODERANGE noderange, STRINGRANGE expected)
|
|
{
|
|
typename boost::range_iterator<NODERANGE>::type
|
|
nri = boost::begin(noderange),
|
|
nrend = boost::end(noderange);
|
|
typename boost::range_iterator<STRINGRANGE>::type
|
|
sri = boost::begin(expected),
|
|
srend = boost::end(expected);
|
|
// We choose to loop over both sequences explicitly rather than using
|
|
// std::equal() or std::lexicographical_compare(). The latter tells you
|
|
// whether one sequence is *less* than the other -- it doesn't tell you
|
|
// equality. std::equal() needs you to verify the sequence lengths ahead
|
|
// of time. Anyway, comparing explicitly allows us to report much more
|
|
// information about any sequence mismatch.
|
|
for ( ; nri != nrend && sri != srend; ++nri, ++sri)
|
|
{
|
|
if ((*nri)->name() != *sri)
|
|
{
|
|
std::cout << desc << " mismatch: "
|
|
<< "expected " << *sri << ", got " << (*nri)->name() << "\n";
|
|
return false;
|
|
}
|
|
}
|
|
if (nri != nrend)
|
|
{
|
|
std::cout << desc << " produced too many items:\n";
|
|
for ( ; nri != nrend; ++nri)
|
|
{
|
|
std::cout << " " << (*nri)->name() << '\n';
|
|
}
|
|
return false;
|
|
}
|
|
if (sri != srend)
|
|
{
|
|
std::cout << desc << " produced too few items, omitting:\n";
|
|
for ( ; sri != srend; ++sri)
|
|
{
|
|
std::cout << " " << *sri << '\n';
|
|
}
|
|
return false;
|
|
}
|
|
// std::cout << desc << " test passed\n";
|
|
return true;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* PlainNode: LLLinkIter, non-refcounted
|
|
*****************************************************************************/
|
|
class PlainNode
|
|
{
|
|
public:
|
|
PlainNode(const std::string& name, PlainNode* next=NULL):
|
|
mName(name),
|
|
mNext(next)
|
|
{}
|
|
~PlainNode()
|
|
{
|
|
delete mNext;
|
|
}
|
|
std::string name() const { return mName; }
|
|
PlainNode* next() const { return mNext; }
|
|
public: // if this were 'private', couldn't bind mNext
|
|
PlainNode* mNext;
|
|
private:
|
|
std::string mName;
|
|
};
|
|
|
|
namespace tut
|
|
{
|
|
template<> template<>
|
|
void iter_object::test<1>()
|
|
{
|
|
// set_test_name("LLLinkedIter -- non-refcounted class");
|
|
PlainNode* last(new PlainNode("c"));
|
|
PlainNode* second(new PlainNode("b", last));
|
|
PlainNode* first(new PlainNode("a", second));
|
|
Cleanup<PlainNode*> cleanup(first);
|
|
static const char* cseq[] = { "a", "b", "c" };
|
|
Expected seq(boost::begin(cseq), boost::end(cseq));
|
|
std::string desc1("Iterate by public link member");
|
|
// std::cout << desc1 << ":\n";
|
|
// Try instantiating an iterator with NULL. This test is less about
|
|
// "did we iterate once?" than "did we avoid blowing up?"
|
|
for (LLLinkedIter<PlainNode> pni(NULL, boost::bind(&PlainNode::mNext, _1)), end;
|
|
pni != end; ++pni)
|
|
{
|
|
// std::cout << (*pni)->name() << '\n';
|
|
ensure("LLLinkedIter<PlainNode>(NULL)", false);
|
|
}
|
|
ensure(desc1,
|
|
verify(desc1,
|
|
boost::make_iterator_range(LLLinkedIter<PlainNode>(first,
|
|
boost::bind(&PlainNode::mNext, _1)),
|
|
LLLinkedIter<PlainNode>()),
|
|
seq));
|
|
std::string desc2("Iterate by next() method");
|
|
// std::cout << desc2 << ":\n";
|
|
// for (LLLinkedIter<PlainNode> pni(first, boost::bind(&PlainNode::next, _1)); ! (pni == end); ++pni)
|
|
// std::cout << (**pni).name() << '\n';
|
|
ensure(desc2,
|
|
verify(desc2,
|
|
boost::make_iterator_range(LLLinkedIter<PlainNode>(first,
|
|
boost::bind(&PlainNode::next, _1)),
|
|
LLLinkedIter<PlainNode>()),
|
|
seq));
|
|
{
|
|
// LLLinkedIter<PlainNode> pni(first, boost::bind(&PlainNode::next, _1));
|
|
// std::cout << "First is " << (*pni++)->name() << '\n';
|
|
// std::cout << "Second is " << (*pni )->name() << '\n';
|
|
}
|
|
{
|
|
LLLinkedIter<PlainNode> pni(first, boost::bind(&PlainNode::next, _1));
|
|
ensure_equals("first", (*pni++)->name(), "a");
|
|
ensure_equals("second", (*pni )->name(), "b");
|
|
}
|
|
}
|
|
} // tut
|
|
|
|
/*****************************************************************************
|
|
* RCNode: LLLinkIter, refcounted
|
|
*****************************************************************************/
|
|
class RCNode;
|
|
typedef LLPointer<RCNode> RCNodePtr;
|
|
|
|
class RCNode: public LLRefCount
|
|
{
|
|
public:
|
|
RCNode(const std::string& name, const RCNodePtr& next=RCNodePtr()):
|
|
mName(name),
|
|
mNext(next)
|
|
{
|
|
// std::cout << "New RCNode(" << mName << ")\n";
|
|
}
|
|
RCNode(const RCNode& that):
|
|
mName(that.mName),
|
|
mNext(that.mNext)
|
|
{
|
|
// std::cout << "Copy RCNode(" << mName << ")\n";
|
|
}
|
|
virtual ~RCNode();
|
|
std::string name() const { return mName; }
|
|
RCNodePtr next() const { return mNext; }
|
|
public: // if this were 'private', couldn't bind mNext
|
|
RCNodePtr mNext;
|
|
private:
|
|
std::string mName;
|
|
};
|
|
|
|
std::ostream& operator<<(std::ostream& out, const RCNode& node)
|
|
{
|
|
out << "RCNode(" << node.name() << ')';
|
|
return out;
|
|
}
|
|
|
|
// This string contains the node name of the last RCNode destroyed. We use it
|
|
// to validate that LLLinkedIter<RCNode> in fact contains LLPointer<RCNode>,
|
|
// and that therefore an outstanding LLLinkedIter to an instance of a
|
|
// refcounted class suffices to keep that instance alive.
|
|
std::string last_RCNode_destroyed;
|
|
|
|
RCNode::~RCNode()
|
|
{
|
|
// std::cout << "Kill " << *this << "\n";
|
|
last_RCNode_destroyed = mName;
|
|
}
|
|
|
|
namespace tut
|
|
{
|
|
template<> template<>
|
|
void iter_object::test<2>()
|
|
{
|
|
// set_test_name("LLLinkedIter -- refcounted class");
|
|
LLLinkedIter<RCNode> rcni, end2;
|
|
{
|
|
// ScopeLabel label("inner scope");
|
|
RCNodePtr head(new RCNode("x", new RCNode("y", new RCNode("z"))));
|
|
// for (rcni = LLLinkedIter<RCNode>(head, boost::bind(&RCNode::mNext, _1)); rcni != end2; ++rcni)
|
|
// std::cout << **rcni << '\n';
|
|
rcni = LLLinkedIter<RCNode>(head, boost::bind(&RCNode::next, _1));
|
|
}
|
|
// std::cout << "Now the LLLinkedIter<RCNode> is the only remaining reference to RCNode chain\n";
|
|
ensure_equals(last_RCNode_destroyed, "");
|
|
ensure(rcni != end2);
|
|
ensure_equals((*rcni)->name(), "x");
|
|
++rcni;
|
|
ensure_equals(last_RCNode_destroyed, "x");
|
|
ensure(rcni != end2);
|
|
ensure_equals((*rcni)->name(), "y");
|
|
++rcni;
|
|
ensure_equals(last_RCNode_destroyed, "y");
|
|
ensure(rcni != end2);
|
|
ensure_equals((*rcni)->name(), "z");
|
|
++rcni;
|
|
ensure_equals(last_RCNode_destroyed, "z");
|
|
ensure(rcni == end2);
|
|
}
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* TreeNode
|
|
*****************************************************************************/
|
|
class TreeNode;
|
|
typedef LLPointer<TreeNode> TreeNodePtr;
|
|
|
|
/**
|
|
* TreeNode represents a refcounted tree-node class that hasn't (yet) been
|
|
* modified to incorporate LLTreeIter methods. This illustrates how you can
|
|
* use tree iterators either standalone, or with free functions.
|
|
*/
|
|
class TreeNode: public LLRefCount
|
|
{
|
|
public:
|
|
typedef std::vector<TreeNodePtr> list_type;
|
|
typedef list_type::const_iterator child_iterator;
|
|
|
|
// To avoid cycles, use a "weak" raw pointer for the parent link
|
|
TreeNode(const std::string& name, TreeNode* parent=0):
|
|
mParent(parent),
|
|
mName(name)
|
|
{}
|
|
TreeNodePtr newChild(const std::string& name)
|
|
{
|
|
TreeNodePtr child(new TreeNode(name, this));
|
|
mChildren.push_back(child);
|
|
return child;
|
|
}
|
|
std::string name() const { return mName; }
|
|
TreeNodePtr getParent() const { return mParent; }
|
|
child_iterator child_begin() const { return mChildren.begin(); }
|
|
child_iterator child_end() const { return mChildren.end(); }
|
|
private:
|
|
std::string mName;
|
|
// To avoid cycles, use a "weak" raw pointer for the parent link
|
|
TreeNode* mParent;
|
|
list_type mChildren;
|
|
};
|
|
|
|
/**
|
|
* This is an example of a helper function to facilitate iterating from a
|
|
* TreeNode up to the root or down from the root (see LLTreeIter::RootIter).
|
|
*
|
|
* Example:
|
|
* @code
|
|
* for (TreeNodePtr node : getRootRange<LLTreeIter::UP>(somenode))
|
|
* {
|
|
* std::cout << node->name() << '\n';
|
|
* }
|
|
* @endcode
|
|
*/
|
|
template <LLTreeIter::RootIter DISCRIM>
|
|
boost::iterator_range< LLTreeRootIter<DISCRIM, TreeNode> >
|
|
getRootRange(const TreeNodePtr& node)
|
|
{
|
|
typedef LLTreeRootIter<DISCRIM, TreeNode> iter_type;
|
|
typedef boost::iterator_range<iter_type> range_type;
|
|
return range_type(iter_type(node, boost::bind(&TreeNode::getParent, _1)),
|
|
iter_type());
|
|
}
|
|
|
|
/**
|
|
* This is an example of a helper function to facilitate walking a given
|
|
* TreeNode's subtree in any supported order (see LLTreeIter::WalkIter).
|
|
*
|
|
* Example:
|
|
* @code
|
|
* for (TreeNodePtr node : getWalkRange<LLTreeIter::DFS_PRE>(root))
|
|
* {
|
|
* std::cout << node->name() << '\n';
|
|
* }
|
|
* @endcode
|
|
*/
|
|
template <LLTreeIter::WalkIter DISCRIM>
|
|
boost::iterator_range< LLTreeWalkIter<DISCRIM, TreeNode, TreeNode::child_iterator> >
|
|
getWalkRange(const TreeNodePtr& node)
|
|
{
|
|
typedef LLTreeWalkIter<DISCRIM, TreeNode, TreeNode::child_iterator> iter_type;
|
|
typedef boost::iterator_range<iter_type> range_type;
|
|
return range_type(iter_type(node,
|
|
boost::bind(&TreeNode::child_begin, _1),
|
|
boost::bind(&TreeNode::child_end, _1)),
|
|
iter_type());
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* EnhancedTreeNode
|
|
*****************************************************************************/
|
|
class EnhancedTreeNode;
|
|
typedef LLPointer<EnhancedTreeNode> EnhancedTreeNodePtr;
|
|
|
|
/**
|
|
* More typically, you enhance the tree-node class itself with template
|
|
* methods like the above. This EnhancedTreeNode class illustrates the
|
|
* technique. Normally, of course, you'd simply add these methods to TreeNode;
|
|
* we put them in a separate class to preserve the undecorated TreeNode class
|
|
* to illustrate (and test) the use of plain tree iterators and standalone
|
|
* helper functions.
|
|
*
|
|
* We originally implemented EnhancedTreeNode as a subclass of TreeNode -- but
|
|
* because TreeNode stores and manipulates TreeNodePtrs and TreeNode*s,
|
|
* reusing its methods required so much ugly downcast logic that we gave up
|
|
* and restated the whole class. Bear in mind that logically these aren't two
|
|
* separate classes; logically they're two snapshots of the @em same class at
|
|
* different moments in time.
|
|
*/
|
|
class EnhancedTreeNode: public LLRefCount
|
|
{
|
|
public:
|
|
/*-------------- The following is restated from TreeNode ---------------*/
|
|
typedef std::vector<EnhancedTreeNodePtr> list_type;
|
|
typedef list_type::const_iterator child_iterator;
|
|
|
|
// To avoid cycles, use a "weak" raw pointer for the parent link
|
|
EnhancedTreeNode(const std::string& name, EnhancedTreeNode* parent=0):
|
|
mParent(parent),
|
|
mName(name)
|
|
{}
|
|
EnhancedTreeNodePtr newChild(const std::string& name)
|
|
{
|
|
EnhancedTreeNodePtr child(new EnhancedTreeNode(name, this));
|
|
mChildren.push_back(child);
|
|
return child;
|
|
}
|
|
std::string name() const { return mName; }
|
|
EnhancedTreeNodePtr getParent() const { return mParent; }
|
|
child_iterator child_begin() const { return mChildren.begin(); }
|
|
child_iterator child_end() const { return mChildren.end(); }
|
|
|
|
private:
|
|
std::string mName;
|
|
// To avoid cycles, use a "weak" raw pointer for the parent link
|
|
EnhancedTreeNode* mParent;
|
|
list_type mChildren;
|
|
public:
|
|
/*----- End of TreeNode; what follows is new with EnhancedTreeNode -----*/
|
|
|
|
/**
|
|
* Because the type of the iterator range returned by getRootRange()
|
|
* depends on the discriminator enum value, instead of a simple typedef we
|
|
* use a templated struct. Example usage:
|
|
*
|
|
* @code
|
|
* for (EnhancedTreeNode::root_range<LLTreeIter::UP>::type range =
|
|
* somenode->getRootRange<LLTreeIter::UP>();
|
|
* range.first != range.second; ++range.first)
|
|
* {
|
|
* std::cout << (*range.first)->name() << '\n';
|
|
* }
|
|
* @endcode
|
|
*/
|
|
template <LLTreeIter::RootIter DISCRIM>
|
|
struct root_range
|
|
{
|
|
typedef boost::iterator_range< LLTreeRootIter<DISCRIM, EnhancedTreeNode> > type;
|
|
};
|
|
|
|
/**
|
|
* Helper method for walking up to (or down from) the tree root. See
|
|
* LLTreeIter::RootIter.
|
|
*
|
|
* Example usage:
|
|
* @code
|
|
* for (EnhancedTreeNodePtr node : somenode->getRootRange<LLTreeIter::UP>())
|
|
* {
|
|
* std::cout << node->name() << '\n';
|
|
* }
|
|
* @endcode
|
|
*/
|
|
template <LLTreeIter::RootIter DISCRIM>
|
|
typename root_range<DISCRIM>::type getRootRange() const
|
|
{
|
|
typedef typename root_range<DISCRIM>::type range_type;
|
|
typedef typename range_type::iterator iter_type;
|
|
return range_type(iter_type(const_cast<EnhancedTreeNode*>(this),
|
|
boost::bind(&EnhancedTreeNode::getParent, _1)),
|
|
iter_type());
|
|
}
|
|
|
|
/**
|
|
* Because the type of the iterator range returned by getWalkRange()
|
|
* depends on the discriminator enum value, instead of a simple typedef we
|
|
* use a templated stuct. Example usage:
|
|
*
|
|
* @code
|
|
* for (EnhancedTreeNode::walk_range<LLTreeIter::DFS_PRE>::type range =
|
|
* somenode->getWalkRange<LLTreeIter::DFS_PRE>();
|
|
* range.first != range.second; ++range.first)
|
|
* {
|
|
* std::cout << (*range.first)->name() << '\n';
|
|
* }
|
|
* @endcode
|
|
*/
|
|
template <LLTreeIter::WalkIter DISCRIM>
|
|
struct walk_range
|
|
{
|
|
typedef boost::iterator_range< LLTreeWalkIter<DISCRIM,
|
|
EnhancedTreeNode,
|
|
EnhancedTreeNode::child_iterator> > type;
|
|
};
|
|
|
|
/**
|
|
* Helper method for walking a given node's subtree in any supported
|
|
* order (see LLTreeIter::WalkIter).
|
|
*
|
|
* Example usage:
|
|
* @code
|
|
* for (EnhancedTreeNodePtr node : somenode->getWalkRange<LLTreeIter::DFS_PRE>())
|
|
* {
|
|
* std::cout << node->name() << '\n';
|
|
* }
|
|
* @endcode
|
|
*/
|
|
template <LLTreeIter::WalkIter DISCRIM>
|
|
typename walk_range<DISCRIM>::type getWalkRange() const
|
|
{
|
|
typedef typename walk_range<DISCRIM>::type range_type;
|
|
typedef typename range_type::iterator iter_type;
|
|
return range_type(iter_type(const_cast<EnhancedTreeNode*>(this),
|
|
boost::bind(&EnhancedTreeNode::child_begin, _1),
|
|
boost::bind(&EnhancedTreeNode::child_end, _1)),
|
|
iter_type());
|
|
}
|
|
};
|
|
|
|
/*****************************************************************************
|
|
* PlainTree
|
|
*****************************************************************************/
|
|
struct PlainTree
|
|
{
|
|
PlainTree(const std::string& name, PlainTree* parent=0):
|
|
mName(name),
|
|
mParent(parent),
|
|
mNextSibling(0),
|
|
mFirstChild(0)
|
|
{
|
|
mLastChildLink = &mFirstChild;
|
|
}
|
|
~PlainTree()
|
|
{
|
|
delete mNextSibling;
|
|
delete mFirstChild;
|
|
}
|
|
PlainTree* newChild(const std::string& name)
|
|
{
|
|
PlainTree* child(new PlainTree(name, this));
|
|
*mLastChildLink = child;
|
|
mLastChildLink = &child->mNextSibling;
|
|
return child;
|
|
}
|
|
std::string name() const { return mName; }
|
|
|
|
std::string mName;
|
|
PlainTree* mParent;
|
|
PlainTree* mNextSibling;
|
|
PlainTree* mFirstChild;
|
|
PlainTree** mLastChildLink;
|
|
};
|
|
|
|
// This "classic" tree tracks each node's children with a linked list anchored
|
|
// at the parent's mFirstChild and linked through each child's mNextSibling.
|
|
// LLTreeDFSIter<> and LLTreeBFSIter<> need functors to return begin()/end()
|
|
// iterators over a given node's children. But because this tree's children
|
|
// aren't stored in an STL container, we can't just export that container's
|
|
// begin()/end(). Instead we'll use LLLinkedIter<> to view the hand-maintained
|
|
// linked list as an iterator range. The straightforward way to do that would
|
|
// be to add child_begin() and child_end() methods. But let's say (for the
|
|
// sake of argument) that this struct is so venerable we don't dare modify it
|
|
// even to add new methods. Well, we can use free functions (or functors) too.
|
|
LLLinkedIter<PlainTree> PlainTree_child_begin(PlainTree* node)
|
|
{
|
|
return LLLinkedIter<PlainTree>(node->mFirstChild, boost::bind(&PlainTree::mNextSibling, _1));
|
|
}
|
|
|
|
LLLinkedIter<PlainTree> PlainTree_child_end(PlainTree* node)
|
|
{
|
|
return LLLinkedIter<PlainTree>();
|
|
}
|
|
|
|
/**
|
|
* This is an example of a helper function to facilitate iterating from a
|
|
* PlainTree up to the root or down from the root (see LLTreeIter::RootIter).
|
|
* Note that we're simply overloading the same getRootRange() helper function
|
|
* name we used for TreeNode.
|
|
*
|
|
* Example:
|
|
* @code
|
|
* for (PlainTree* node : getRootRange<LLTreeIter::UP>(somenode))
|
|
* {
|
|
* std::cout << node->name() << '\n';
|
|
* }
|
|
* @endcode
|
|
*/
|
|
template <LLTreeIter::RootIter DISCRIM>
|
|
boost::iterator_range< LLTreeRootIter<DISCRIM, PlainTree> >
|
|
getRootRange(PlainTree* node)
|
|
{
|
|
typedef LLTreeRootIter<DISCRIM, PlainTree> iter_type;
|
|
typedef boost::iterator_range<iter_type> range_type;
|
|
return range_type(iter_type(node, boost::bind(&PlainTree::mParent, _1)),
|
|
iter_type());
|
|
}
|
|
|
|
/**
|
|
* This is an example of a helper function to facilitate walking a given
|
|
* PlainTree's subtree in any supported order (see LLTreeIter::WalkIter). Note
|
|
* that we're simply overloading the same getWalkRange() helper function name
|
|
* we used for TreeNode.
|
|
*
|
|
* Example:
|
|
* @code
|
|
* for (PlainTree* node : getWalkRange<LLTreeIter::DFS_PRE>(root))
|
|
* {
|
|
* std::cout << node->name() << '\n';
|
|
* }
|
|
* @endcode
|
|
*/
|
|
template <LLTreeIter::WalkIter DISCRIM>
|
|
boost::iterator_range< LLTreeWalkIter<DISCRIM, PlainTree, LLLinkedIter<PlainTree> > >
|
|
getWalkRange(PlainTree* node)
|
|
{
|
|
typedef LLTreeWalkIter<DISCRIM, PlainTree, LLLinkedIter<PlainTree> > iter_type;
|
|
typedef boost::iterator_range<iter_type> range_type;
|
|
return range_type(iter_type(node,
|
|
PlainTree_child_begin,
|
|
PlainTree_child_end),
|
|
iter_type());
|
|
}
|
|
|
|
// We could go through the exercise of writing EnhancedPlainTree containing
|
|
// root_range, getRootRange(), walk_range and getWalkRange() members -- but we
|
|
// won't. See EnhancedTreeNode for examples.
|
|
|
|
/*****************************************************************************
|
|
* Generic tree test data
|
|
*****************************************************************************/
|
|
template <class NODE>
|
|
typename LLPtrTo<NODE>::type example_tree()
|
|
{
|
|
typedef typename LLPtrTo<NODE>::type NodePtr;
|
|
NodePtr root(new NODE("root"));
|
|
NodePtr A(root->newChild("A"));
|
|
NodePtr A1(A->newChild("A1"));
|
|
/* NodePtr A1a*/(A1->newChild("A1a"));
|
|
/* NodePtr A1b*/(A1->newChild("A1b"));
|
|
/* NodePtr A1c*/(A1->newChild("A1c"));
|
|
NodePtr A2(A->newChild("A2"));
|
|
/* NodePtr A2a*/(A2->newChild("A2a"));
|
|
/* NodePtr A2b*/(A2->newChild("A2b"));
|
|
/* NodePtr A2c*/(A2->newChild("A2c"));
|
|
NodePtr A3(A->newChild("A3"));
|
|
/* NodePtr A3a*/(A3->newChild("A3a"));
|
|
/* NodePtr A3b*/(A3->newChild("A3b"));
|
|
/* NodePtr A3c*/(A3->newChild("A3c"));
|
|
NodePtr B(root->newChild("B"));
|
|
NodePtr B1(B->newChild("B1"));
|
|
/* NodePtr B1a*/(B1->newChild("B1a"));
|
|
/* NodePtr B1b*/(B1->newChild("B1b"));
|
|
/* NodePtr B1c*/(B1->newChild("B1c"));
|
|
NodePtr B2(B->newChild("B2"));
|
|
/* NodePtr B2a*/(B2->newChild("B2a"));
|
|
/* NodePtr B2b*/(B2->newChild("B2b"));
|
|
/* NodePtr B2c*/(B2->newChild("B2c"));
|
|
NodePtr B3(B->newChild("B3"));
|
|
/* NodePtr B3a*/(B3->newChild("B3a"));
|
|
/* NodePtr B3b*/(B3->newChild("B3b"));
|
|
/* NodePtr B3c*/(B3->newChild("B3c"));
|
|
NodePtr C(root->newChild("C"));
|
|
NodePtr C1(C->newChild("C1"));
|
|
/* NodePtr C1a*/(C1->newChild("C1a"));
|
|
/* NodePtr C1b*/(C1->newChild("C1b"));
|
|
/* NodePtr C1c*/(C1->newChild("C1c"));
|
|
NodePtr C2(C->newChild("C2"));
|
|
/* NodePtr C2a*/(C2->newChild("C2a"));
|
|
/* NodePtr C2b*/(C2->newChild("C2b"));
|
|
/* NodePtr C2c*/(C2->newChild("C2c"));
|
|
NodePtr C3(C->newChild("C3"));
|
|
/* NodePtr C3a*/(C3->newChild("C3a"));
|
|
/* NodePtr C3b*/(C3->newChild("C3b"));
|
|
/* NodePtr C3c*/(C3->newChild("C3c"));
|
|
return root;
|
|
}
|
|
|
|
// WalkExpected<WalkIter> is the list of string node names we expect from a
|
|
// WalkIter traversal of our example_tree() data.
|
|
template <LLTreeIter::WalkIter DISCRIM>
|
|
struct WalkExpected: public Expected
|
|
{
|
|
// Initialize with bad_strings: we don't expect to use this generic case,
|
|
// only the specializations. Note that for a classic C-style array we must
|
|
// pass a pair of iterators rather than extracting boost::begin() and
|
|
// boost::end() within the target constructor: a template ctor accepts
|
|
// these classic C-style arrays as char** rather than char*[length]. Oh well.
|
|
WalkExpected(): Expected(boost::begin(bad_strings), boost::end(bad_strings)) {}
|
|
};
|
|
|
|
// list of string node names we expect from traversing example_tree() in
|
|
// DFS_PRE order
|
|
const char* dfs_pre_strings[] =
|
|
{
|
|
"root",
|
|
"A",
|
|
"A1",
|
|
"A1a",
|
|
"A1b",
|
|
"A1c",
|
|
"A2",
|
|
"A2a",
|
|
"A2b",
|
|
"A2c",
|
|
"A3",
|
|
"A3a",
|
|
"A3b",
|
|
"A3c",
|
|
"B",
|
|
"B1",
|
|
"B1a",
|
|
"B1b",
|
|
"B1c",
|
|
"B2",
|
|
"B2a",
|
|
"B2b",
|
|
"B2c",
|
|
"B3",
|
|
"B3a",
|
|
"B3b",
|
|
"B3c",
|
|
"C",
|
|
"C1",
|
|
"C1a",
|
|
"C1b",
|
|
"C1c",
|
|
"C2",
|
|
"C2a",
|
|
"C2b",
|
|
"C2c",
|
|
"C3",
|
|
"C3a",
|
|
"C3b",
|
|
"C3c"
|
|
};
|
|
|
|
// specialize WalkExpected<DFS_PRE> with the expected strings
|
|
template <>
|
|
struct WalkExpected<LLTreeIter::DFS_PRE>: public Expected
|
|
{
|
|
WalkExpected(): Expected(boost::begin(dfs_pre_strings), boost::end(dfs_pre_strings)) {}
|
|
};
|
|
|
|
// list of string node names we expect from traversing example_tree() in
|
|
// DFS_POST order
|
|
const char* dfs_post_strings[] =
|
|
{
|
|
"A1a",
|
|
"A1b",
|
|
"A1c",
|
|
"A1",
|
|
"A2a",
|
|
"A2b",
|
|
"A2c",
|
|
"A2",
|
|
"A3a",
|
|
"A3b",
|
|
"A3c",
|
|
"A3",
|
|
"A",
|
|
"B1a",
|
|
"B1b",
|
|
"B1c",
|
|
"B1",
|
|
"B2a",
|
|
"B2b",
|
|
"B2c",
|
|
"B2",
|
|
"B3a",
|
|
"B3b",
|
|
"B3c",
|
|
"B3",
|
|
"B",
|
|
"C1a",
|
|
"C1b",
|
|
"C1c",
|
|
"C1",
|
|
"C2a",
|
|
"C2b",
|
|
"C2c",
|
|
"C2",
|
|
"C3a",
|
|
"C3b",
|
|
"C3c",
|
|
"C3",
|
|
"C",
|
|
"root"
|
|
};
|
|
|
|
// specialize WalkExpected<DFS_POST> with the expected strings
|
|
template <>
|
|
struct WalkExpected<LLTreeIter::DFS_POST>: public Expected
|
|
{
|
|
WalkExpected(): Expected(boost::begin(dfs_post_strings), boost::end(dfs_post_strings)) {}
|
|
};
|
|
|
|
// list of string node names we expect from traversing example_tree() in BFS order
|
|
const char* bfs_strings[] =
|
|
{
|
|
"root",
|
|
"A",
|
|
"B",
|
|
"C",
|
|
"A1",
|
|
"A2",
|
|
"A3",
|
|
"B1",
|
|
"B2",
|
|
"B3",
|
|
"C1",
|
|
"C2",
|
|
"C3",
|
|
"A1a",
|
|
"A1b",
|
|
"A1c",
|
|
"A2a",
|
|
"A2b",
|
|
"A2c",
|
|
"A3a",
|
|
"A3b",
|
|
"A3c",
|
|
"B1a",
|
|
"B1b",
|
|
"B1c",
|
|
"B2a",
|
|
"B2b",
|
|
"B2c",
|
|
"B3a",
|
|
"B3b",
|
|
"B3c",
|
|
"C1a",
|
|
"C1b",
|
|
"C1c",
|
|
"C2a",
|
|
"C2b",
|
|
"C2c",
|
|
"C3a",
|
|
"C3b",
|
|
"C3c"
|
|
};
|
|
|
|
// specialize WalkExpected<BFS> with the expected strings
|
|
template <>
|
|
struct WalkExpected<LLTreeIter::BFS>: public Expected
|
|
{
|
|
WalkExpected(): Expected(boost::begin(bfs_strings), boost::end(bfs_strings)) {}
|
|
};
|
|
|
|
// extract a particular "arbitrary" node from the example_tree() data: the
|
|
// second (middle) node at each child level
|
|
template <class NODE, typename CHILDITER>
|
|
typename LLPtrTo<NODE>::type
|
|
get_B2b(const typename LLPtrTo<NODE>::type& root,
|
|
const boost::function<CHILDITER(const typename LLPtrTo<NODE>::type&)>& child_begin)
|
|
{
|
|
typedef typename LLPtrTo<NODE>::type NodePtr;
|
|
CHILDITER Bi(child_begin(root));
|
|
++Bi;
|
|
NodePtr B(*Bi);
|
|
CHILDITER B2i(child_begin(B));
|
|
++B2i;
|
|
NodePtr B2(*B2i);
|
|
CHILDITER B2bi(child_begin(B2));
|
|
++B2bi;
|
|
NodePtr B2b(*B2bi);
|
|
return B2b;
|
|
}
|
|
|
|
// RootExpected<RootIter> is the list of string node names we expect from a
|
|
// RootIter traversal of our example_tree() data.
|
|
template <LLTreeIter::RootIter DISCRIM>
|
|
struct RootExpected: public Expected
|
|
{
|
|
// Initialize with bad_strings: we don't expect to use this generic case,
|
|
// only the specializations.
|
|
RootExpected(): Expected(boost::begin(bad_strings), boost::end(bad_strings)) {}
|
|
};
|
|
|
|
// list of string node names we expect from traversing UP from
|
|
// example_tree()'s B2b node
|
|
const char* up_from_B2b[] =
|
|
{
|
|
"B2b",
|
|
"B2",
|
|
"B",
|
|
"root"
|
|
};
|
|
|
|
// specialize RootExpected<UP> with the expected strings
|
|
template <>
|
|
struct RootExpected<LLTreeIter::UP>: public Expected
|
|
{
|
|
RootExpected(): Expected(boost::begin(up_from_B2b), boost::end(up_from_B2b)) {}
|
|
};
|
|
|
|
// list of string node names we expect from traversing DOWN to
|
|
// example_tree()'s B2b node
|
|
const char* down_to_B2b[] =
|
|
{
|
|
"root",
|
|
"B",
|
|
"B2",
|
|
"B2b"
|
|
};
|
|
|
|
// specialize RootExpected<DOWN> with the expected strings
|
|
template <>
|
|
struct RootExpected<LLTreeIter::DOWN>: public Expected
|
|
{
|
|
RootExpected(): Expected(boost::begin(down_to_B2b), boost::end(down_to_B2b)) {}
|
|
};
|
|
|
|
/*****************************************************************************
|
|
* Generic tree test functions
|
|
*****************************************************************************/
|
|
template<LLTreeIter::RootIter DISCRIM, class NODE, typename PARENTFUNC>
|
|
bool LLTreeRootIter_test(const std::string& itername, const std::string& nodename,
|
|
const typename LLPtrTo<NODE>::type& node,
|
|
PARENTFUNC parentfunc)
|
|
{
|
|
std::ostringstream desc;
|
|
desc << itername << '<' << nodename << "> from " << node->name();
|
|
if (! verify(desc.str(),
|
|
boost::make_iterator_range(LLTreeRootIter<DISCRIM, NODE>(node, parentfunc),
|
|
LLTreeRootIter<DISCRIM, NODE>()),
|
|
RootExpected<DISCRIM>()))
|
|
return false;
|
|
// std::cout << desc.str() << '\n';
|
|
// Try instantiating an iterator with NULL (that is, a default-constructed
|
|
// node pointer). This test is less about "did we iterate once?" than "did
|
|
// we avoid blowing up?"
|
|
for (LLTreeRootIter<DISCRIM, NODE> hri = LLTreeRootIter<DISCRIM, NODE>(typename LLPtrTo<NODE>::type(), parentfunc), hrend;
|
|
hri != hrend; /* ++hri */) // incrementing is moot, and MSVC complains
|
|
{
|
|
// std::cout << nodename << '(' << (*hri)->name() << ")\n";
|
|
std::cout << itername << '<' << nodename << ">(NULL)\n";
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
template<class NODE, typename CHILDITER, typename PARENTFUNC, typename CHILDFUNC>
|
|
bool LLTreeUpIter_test(const std::string& nodename, PARENTFUNC parentfunc, CHILDFUNC childfunc)
|
|
{
|
|
bool success = true;
|
|
typedef typename LLPtrTo<NODE>::type ptr_type;
|
|
ptr_type root(example_tree<NODE>());
|
|
Cleanup<ptr_type> cleanup(root);
|
|
ptr_type B2b(get_B2b<NODE, CHILDITER>(root, childfunc));
|
|
if (! LLTreeRootIter_test<LLTreeIter::UP, NODE>("LLTreeUpIter", nodename, B2b, parentfunc))
|
|
success = false;
|
|
if (! LLTreeRootIter_test<LLTreeIter::DOWN, NODE>("LLTreeDownIter", nodename, B2b, parentfunc))
|
|
success = false;
|
|
return success;
|
|
}
|
|
|
|
template <LLTreeIter::WalkIter DISCRIM, class NODE, typename CHILDITER,
|
|
typename CHILDBEGINFUNC, typename CHILDENDFUNC>
|
|
bool LLTreeWalkIter_test(const std::string& itername, const std::string& nodename,
|
|
CHILDBEGINFUNC childbegin, CHILDENDFUNC childend)
|
|
{
|
|
typename LLPtrTo<NODE>::type root(example_tree<NODE>());
|
|
Cleanup<typename LLPtrTo<NODE>::type> cleanup(root);
|
|
std::ostringstream desc;
|
|
desc << itername << '<' << nodename << "> from " << root->name();
|
|
if (! verify(desc.str(),
|
|
boost::make_iterator_range(LLTreeWalkIter<DISCRIM, NODE, CHILDITER>(root,
|
|
childbegin,
|
|
childend),
|
|
LLTreeWalkIter<DISCRIM, NODE, CHILDITER>()),
|
|
WalkExpected<DISCRIM>()))
|
|
return false;
|
|
// Try instantiating an iterator with NULL (that is, a default-constructed
|
|
// node pointer). This test is less about "did we iterate once?" than "did
|
|
// we avoid blowing up?"
|
|
for (LLTreeWalkIter<DISCRIM, NODE, CHILDITER> twi = LLTreeWalkIter<DISCRIM, NODE, CHILDITER>(typename LLPtrTo<NODE>::type(),
|
|
childbegin,
|
|
childend),
|
|
twend;
|
|
twi != twend; /* ++twi */) // incrementing is moot, and MSVC complains
|
|
{
|
|
std::cout << itername << '<' << nodename << ">(NULL)\n";
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
template <class NODE, typename CHILDITER,
|
|
typename PARENTFUNC, typename CHILDBEGINFUNC, typename CHILDENDFUNC>
|
|
bool LLTreeIter_tests(const std::string& nodename,
|
|
PARENTFUNC parentfunc, CHILDBEGINFUNC childbegin, CHILDENDFUNC childend)
|
|
{
|
|
bool success = true;
|
|
if (! LLTreeUpIter_test<NODE, CHILDITER>(nodename, parentfunc, childbegin))
|
|
success = false;
|
|
/*==========================================================================*|
|
|
LLTreeIter_test<NODE, LLTreeDFSIter<NODE, CHILDITER> >("LLTreeDFSIter", nodename,
|
|
childbegin, childend);
|
|
LLTreeIter_test<NODE, LLTreeDFSPostIter<NODE, CHILDITER> >("LLTreeDFSPostIter", nodename,
|
|
childbegin, childend);
|
|
LLTreeIter_test<NODE, LLTreeBFSIter<NODE, CHILDITER> >("LLTreeBFSIter", nodename,
|
|
childbegin, childend);
|
|
|*==========================================================================*/
|
|
if (! LLTreeWalkIter_test<LLTreeIter::DFS_PRE, NODE, CHILDITER>("LLTreeDFSIter", nodename,
|
|
childbegin, childend))
|
|
success = false;
|
|
if (! LLTreeWalkIter_test<LLTreeIter::DFS_POST, NODE, CHILDITER>("LLTreeDFSPostIter", nodename,
|
|
childbegin, childend))
|
|
success = false;
|
|
if (! LLTreeWalkIter_test<LLTreeIter::BFS, NODE, CHILDITER>("LLTreeBFSIter", nodename,
|
|
childbegin, childend))
|
|
success = false;
|
|
return success;
|
|
}
|
|
|
|
namespace tut
|
|
{
|
|
template<> template<>
|
|
void iter_object::test<3>()
|
|
{
|
|
// set_test_name("LLTreeIter tests");
|
|
ensure(LLTreeIter_tests<TreeNode, TreeNode::child_iterator>
|
|
("TreeNode",
|
|
boost::bind(&TreeNode::getParent, _1),
|
|
boost::bind(&TreeNode::child_begin, _1),
|
|
boost::bind(&TreeNode::child_end, _1)));
|
|
ensure(LLTreeIter_tests<PlainTree, LLLinkedIter<PlainTree> >
|
|
("PlainTree",
|
|
boost::bind(&PlainTree::mParent, _1),
|
|
PlainTree_child_begin,
|
|
PlainTree_child_end));
|
|
}
|
|
|
|
template<> template<>
|
|
void iter_object::test<4>()
|
|
{
|
|
// set_test_name("getRootRange() tests");
|
|
// This test function illustrates the looping techniques described in the
|
|
// comments for the getRootRange() free function, the
|
|
// EnhancedTreeNode::root_range template and the
|
|
// EnhancedTreeNode::getRootRange() method. Obviously the for()
|
|
// forms are more succinct.
|
|
TreeNodePtr tnroot(example_tree<TreeNode>());
|
|
TreeNodePtr tnB2b(get_B2b<TreeNode, TreeNode::child_iterator>
|
|
(tnroot, boost::bind(&TreeNode::child_begin, _1)));
|
|
|
|
std::string desc1("for (TreeNodePr : getRootRange<LLTreeIter::UP>(tnB2b))");
|
|
// std::cout << desc1 << "\n";
|
|
// Although we've commented out the output statement, ensure that the
|
|
// loop construct is still valid, as promised by the getRootRange()
|
|
// documentation.
|
|
for (TreeNodePtr node : getRootRange<LLTreeIter::UP>(tnB2b))
|
|
{
|
|
// std::cout << node->name() << '\n';
|
|
}
|
|
ensure(desc1,
|
|
verify(desc1, getRootRange<LLTreeIter::UP>(tnB2b), RootExpected<LLTreeIter::UP>()));
|
|
|
|
EnhancedTreeNodePtr etnroot(example_tree<EnhancedTreeNode>());
|
|
EnhancedTreeNodePtr etnB2b(get_B2b<EnhancedTreeNode, EnhancedTreeNode::child_iterator>
|
|
(etnroot, boost::bind(&EnhancedTreeNode::child_begin, _1)));
|
|
|
|
// std::cout << "EnhancedTreeNode::root_range<LLTreeIter::DOWN>::type range =\n"
|
|
// << " etnB2b->getRootRange<LLTreeIter::DOWN>();\n"
|
|
// << "for (EnhancedTreeNode::root_range<LLTreeIter::DOWN>::type::iterator ri = range.begin();\n"
|
|
// << " ri != range.end(); ++ri)\n";
|
|
EnhancedTreeNode::root_range<LLTreeIter::DOWN>::type range =
|
|
etnB2b->getRootRange<LLTreeIter::DOWN>();
|
|
for (EnhancedTreeNode::root_range<LLTreeIter::DOWN>::type::iterator ri = range.begin();
|
|
ri != range.end(); ++ri)
|
|
{
|
|
// std::cout << (*ri)->name() << '\n';
|
|
}
|
|
|
|
std::string desc2("for (EnhancedTreeNodePtr node : etnB2b->getRootRange<LLTreeIter::UP>())");
|
|
// std::cout << desc2 << '\n';
|
|
for (EnhancedTreeNodePtr node : etnB2b->getRootRange<LLTreeIter::UP>())
|
|
{
|
|
// std::cout << node->name() << '\n';
|
|
}
|
|
ensure(desc2,
|
|
verify(desc2, etnB2b->getRootRange<LLTreeIter::UP>(), RootExpected<LLTreeIter::UP>()));
|
|
}
|
|
|
|
template<> template<>
|
|
void iter_object::test<5>()
|
|
{
|
|
// set_test_name("getWalkRange() tests");
|
|
// This test function doesn't illustrate the looping permutations for
|
|
// getWalkRange(); see getRootRange_tests() for such examples. This
|
|
// function simply verifies that they all work.
|
|
|
|
// TreeNode, using helper function
|
|
TreeNodePtr tnroot(example_tree<TreeNode>());
|
|
std::string desc_tnpre("getWalkRange<LLTreeIter::DFS_PRE>(tnroot)");
|
|
ensure(desc_tnpre,
|
|
verify(desc_tnpre,
|
|
getWalkRange<LLTreeIter::DFS_PRE>(tnroot),
|
|
WalkExpected<LLTreeIter::DFS_PRE>()));
|
|
std::string desc_tnpost("getWalkRange<LLTreeIter::DFS_POST>(tnroot)");
|
|
ensure(desc_tnpost,
|
|
verify(desc_tnpost,
|
|
getWalkRange<LLTreeIter::DFS_POST>(tnroot),
|
|
WalkExpected<LLTreeIter::DFS_POST>()));
|
|
std::string desc_tnb("getWalkRange<LLTreeIter::BFS>(tnroot)");
|
|
ensure(desc_tnb,
|
|
verify(desc_tnb,
|
|
getWalkRange<LLTreeIter::BFS>(tnroot),
|
|
WalkExpected<LLTreeIter::BFS>()));
|
|
|
|
// EnhancedTreeNode, using method
|
|
EnhancedTreeNodePtr etnroot(example_tree<EnhancedTreeNode>());
|
|
std::string desc_etnpre("etnroot->getWalkRange<LLTreeIter::DFS_PRE>()");
|
|
ensure(desc_etnpre,
|
|
verify(desc_etnpre,
|
|
etnroot->getWalkRange<LLTreeIter::DFS_PRE>(),
|
|
WalkExpected<LLTreeIter::DFS_PRE>()));
|
|
std::string desc_etnpost("etnroot->getWalkRange<LLTreeIter::DFS_POST>()");
|
|
ensure(desc_etnpost,
|
|
verify(desc_etnpost,
|
|
etnroot->getWalkRange<LLTreeIter::DFS_POST>(),
|
|
WalkExpected<LLTreeIter::DFS_POST>()));
|
|
std::string desc_etnb("etnroot->getWalkRange<LLTreeIter::BFS>()");
|
|
ensure(desc_etnb,
|
|
verify(desc_etnb,
|
|
etnroot->getWalkRange<LLTreeIter::BFS>(),
|
|
WalkExpected<LLTreeIter::BFS>()));
|
|
|
|
// PlainTree, using helper function
|
|
PlainTree* ptroot(example_tree<PlainTree>());
|
|
Cleanup<PlainTree*> cleanup(ptroot);
|
|
std::string desc_ptpre("getWalkRange<LLTreeIter::DFS_PRE>(ptroot)");
|
|
ensure(desc_ptpre,
|
|
verify(desc_ptpre,
|
|
getWalkRange<LLTreeIter::DFS_PRE>(ptroot),
|
|
WalkExpected<LLTreeIter::DFS_PRE>()));
|
|
std::string desc_ptpost("getWalkRange<LLTreeIter::DFS_POST>(ptroot)");
|
|
ensure(desc_ptpost,
|
|
verify(desc_ptpost,
|
|
getWalkRange<LLTreeIter::DFS_POST>(ptroot),
|
|
WalkExpected<LLTreeIter::DFS_POST>()));
|
|
std::string desc_ptb("getWalkRange<LLTreeIter::BFS>(ptroot)");
|
|
ensure(desc_ptb,
|
|
verify(desc_ptb,
|
|
getWalkRange<LLTreeIter::BFS>(ptroot),
|
|
WalkExpected<LLTreeIter::BFS>()));
|
|
}
|
|
} // tut
|