phoenix-firestorm/indra/llcommon/tests/lltreeiterators_test.cpp

1214 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>
#include <boost/foreach.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
* BOOST_FOREACH(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
* BOOST_FOREACH(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
* BOOST_FOREACH(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
* BOOST_FOREACH(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
* BOOST_FOREACH(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
* BOOST_FOREACH(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 BOOST_FOREACH()
// 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("BOOST_FOREACH(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.
BOOST_FOREACH(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("BOOST_FOREACH(EnhancedTreeNodePtr node, etnB2b->getRootRange<LLTreeIter::UP>())");
// std::cout << desc2 << '\n';
BOOST_FOREACH(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