Commit af39e054 authored by Steffen Müthing's avatar Steffen Müthing

Merge branch 'feature/add-callback-traversal' into 'master'

Add tree traversal with callbacks

See merge request !2
parents f78f852b 6b46e670
Pipeline #7278 passed with stage
in 4 minutes and 2 seconds
......@@ -12,8 +12,21 @@ Changes
TypeTree 2.6
------------
- TypeTree has updated its minimum build toolchain requirements. You now need a compiler that is at
least compatible with GCC 5 in C++14 mode and CMake 3.1.0.
- TypeTree has updated its minimum build toolchain requirements. You now need a compiler that is at
least compatible with GCC 5 in C++14 mode and CMake 3.1.0.
- There is a new, simpler way of applying a functor to each tree node that keeps you from having
to write a visitor by yourself, e.g.:
```c++
int i = 0;
forEachNode(tree, [](auto&&... node) {
++i;
});
```
Thanks to Carsten Gräser for contributing this feature!
TypeTree 2.5
......
......@@ -161,6 +161,51 @@ namespace Dune {
#endif // DOXYGEN
namespace Detail {
template<class PreFunc, class LeafFunc, class PostFunc>
struct CallbackVisitor :
public TypeTree::TreeVisitor,
public TypeTree::DynamicTraversal
{
public:
CallbackVisitor(PreFunc& preFunc, LeafFunc& leafFunc, PostFunc& postFunc) :
preFunc_(preFunc),
leafFunc_(leafFunc),
postFunc_(postFunc)
{}
template<typename Node, typename TreePath>
void pre(Node&& node, TreePath treePath)
{
preFunc_(node, treePath);
}
template<typename Node, typename TreePath>
void leaf(Node&& node, TreePath treePath)
{
leafFunc_(node, treePath);
}
template<typename Node, typename TreePath>
void post(Node&& node, TreePath treePath)
{
postFunc_(node, treePath);
}
private:
PreFunc& preFunc_;
LeafFunc& leafFunc_;
PostFunc& postFunc_;
};
template<class PreFunc, class LeafFunc, class PostFunc>
auto callbackVisitor(PreFunc& preFunc, LeafFunc& leafFunc, PostFunc& postFunc)
{
return CallbackVisitor<PreFunc, LeafFunc, PostFunc>(preFunc, leafFunc, postFunc);
}
} // namespace Detail
// ********************************************************************************
......@@ -189,6 +234,71 @@ namespace Dune {
std::forward<Visitor>(visitor));
}
/**
* \brief Traverse tree and visit each node
*
* All passed callback functions are called with the
* node and corresponding treepath as arguments.
*
* \param tree The tree to traverse
* \param preFunc This function is called for each inner node before visiting its children
* \param leafFunc This function is called for each leaf node
* \param postFunc This function is called for each inner node after visiting its children
*/
template<class Tree, class PreFunc, class LeafFunc, class PostFunc>
void forEachNode(Tree&& tree, PreFunc&& preFunc, LeafFunc&& leafFunc, PostFunc&& postFunc)
{
applyToTree(tree, Detail::callbackVisitor(preFunc, leafFunc, postFunc));
}
/**
* \brief Traverse tree and visit each node
*
* All passed callback functions are called with the
* node and corresponding treepath as arguments.
*
* \param tree The tree to traverse
* \param innerFunc This function is called for each inner node before visiting its children
* \param leafFunc This function is called for each leaf node
*/
template<class Tree, class InnerFunc, class LeafFunc>
void forEachNode(Tree&& tree, InnerFunc&& innerFunc, LeafFunc&& leafFunc)
{
auto nop = [](auto&&... args) {};
forEachNode(tree, innerFunc, leafFunc, nop);
}
/**
* \brief Traverse tree and visit each node
*
* The passed callback function is called with the
* node and corresponding treepath as arguments.
*
* \param tree The tree to traverse
* \param nodeFunc This function is called for each node
*/
template<class Tree, class NodeFunc>
void forEachNode(Tree&& tree, NodeFunc&& nodeFunc)
{
forEachNode(tree, nodeFunc, nodeFunc);
}
/**
* \brief Traverse tree and visit each leaf node
*
* The passed callback function is called with the
* node and corresponding treepath as arguments.
*
* \param tree The tree to traverse
* \param leafFunc This function is called for each leaf node
*/
template<class Tree, class LeafFunc>
void forEachLeafNode(Tree&& tree, LeafFunc&& leafFunc)
{
auto nop = [](auto&&... args) {};
forEachNode(tree, nop, leafFunc, nop);
}
//! \} group Tree Traversal
} // namespace TypeTree
......
......@@ -21,3 +21,5 @@ dune_add_test(SOURCES testfilteredcompositenode.cc
dune_add_test(SOURCES testproxynode.cc
COMPILE_DEFINITIONS TEST_TYPETREE)
dune_add_test(SOURCES testcallbacktraversal.cc)
// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
// vi: set et ts=4 sw=2 sts=2:
#include <config.h>
#include <dune/common/test/testsuite.hh>
#include <dune/typetree/leafnode.hh>
#include <dune/typetree/powernode.hh>
#include <dune/typetree/compositenode.hh>
#include <dune/typetree/traversal.hh>
template<class P>
class SimpleLeafNode :
public Dune::TypeTree::LeafNode
{
using Base = Dune::TypeTree::LeafNode;
public:
using Payload = P;
SimpleLeafNode(const Payload& payload) :
payload_(payload)
{}
SimpleLeafNode(Payload&& payload) :
payload_(std::move(payload))
{}
const Payload& value() const
{ return payload_;}
Payload& value()
{ return payload_;}
private:
Payload payload_;
};
template<class P, class C, std::size_t n>
class SimplePowerNode:
public Dune::TypeTree::PowerNode<C,n>
{
using Base = Dune::TypeTree::PowerNode<C,n>;
public:
using Payload = P;
template<class... CC>
SimplePowerNode(const Payload& payload, CC&&... cc) :
Base(std::forward<CC>(cc)...),
payload_(payload)
{}
template<class... CC>
SimplePowerNode(Payload&& payload, CC&&... cc) :
Base(std::forward<CC>(cc)...),
payload_(std::move(payload))
{}
const Payload& value() const
{ return payload_;}
Payload& value()
{ return payload_;}
private:
Payload payload_;
};
template<class P, class... C>
class SimpleCompositeNode:
public Dune::TypeTree::CompositeNode<C...>
{
using Base = Dune::TypeTree::CompositeNode<C...>;
public:
using Payload = P;
template<class... CC>
SimpleCompositeNode(const Payload& payload, CC&&... cc) :
Base(std::forward<CC>(cc)...),
payload_(payload)
{}
template<class... CC>
SimpleCompositeNode(Payload&& payload, CC&&... cc) :
Base(std::forward<CC>(cc)...),
payload_(std::move(payload))
{}
const Payload& value() const
{ return payload_;}
Payload& value()
{ return payload_;}
private:
Payload payload_;
};
template<class P>
auto leafNode(P&& p)
{
return SimpleLeafNode<std::decay_t<P>>(std::forward<P>(p));
}
template<class P, class... C>
auto compositeNode(P&& p, C&&... c)
{
return SimpleCompositeNode<std::decay_t<P>, std::decay_t<C>...>(std::forward<P>(p), std::forward<C>(c)...);
}
template<class P, class C0, class... C>
auto powerNode(P&& p, C0&& c0, C&&... c)
{
return SimplePowerNode<std::decay_t<P>, std::decay_t<C0>, sizeof...(C)+1>(std::forward<P>(p), std::forward<C0>(c0), std::forward<C>(c)...);
}
int main()
{
Dune::TestSuite test("tree traversal check");
using Payload = std::size_t;
auto tree = compositeNode(
Payload(0),
powerNode(
Payload(0),
leafNode(Payload(0)),
leafNode(Payload(0)),
leafNode(Payload(0))
),
leafNode(Payload(0)));
{
std::size_t all = 0;
forEachNode(tree, [&](auto&& node, auto&& path) {
++all;
});
test.check(all==6)
<< "Counting all nodes with forEachNode failed. Result is " << all << " but should be " << 6;
}
{
std::size_t inner = 0;
std::size_t leaf = 0;
forEachNode(tree, [&](auto&& node, auto&& path) {
++inner;
}, [&](auto&& node, auto&& path) {
++leaf;
});
test.check(inner==2)
<< "Counting inner nodes with forEachNode failed. Result is " << inner << " but should be " << 2;
test.check(leaf==4)
<< "Counting leaf nodes with forEachNode failed. Result is " << leaf << " but should be " << 4;
}
{
std::size_t leaf = 0;
forEachLeafNode(tree, [&](auto&& node, auto&& path) {
++leaf;
});
test.check(leaf==4)
<< "Counting leaf nodes with forEachLeafNode failed. Result is " << leaf << " but should be " << 4;
}
{
auto countVisit = [] (auto&& node, auto&& path) {
++(node.value());
};
forEachNode(tree, countVisit, countVisit, countVisit);
std::size_t visits=0;
forEachNode(tree, [&](auto&& node, auto&& path) {
visits += node.value();
});
test.check(visits==8)
<< "Counting all node visitations failed. Result is " << visits << " but should be " << 8;
}
return test.exit();
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment