Commit ad86d17a authored by Praetorius, Simon's avatar Praetorius, Simon
Browse files

Merge branch 'feature/typetree_cleanup' into 'master'

update traversal and tree container based on if constexpr feature

See merge request !182
parents 23df0b03 8c255ac5
#pragma once
#include <dune/common/hybridutilities.hh>
#include <dune/common/rangeutilities.hh>
#include <dune/typetree/childextraction.hh>
#include <dune/typetree/nodetags.hh>
#include <dune/typetree/treepath.hh>
#include <dune/typetree/visitor.hh>
#include <amdis/common/ForEach.hpp>
#include <amdis/common/Logical.hpp>
#include <amdis/common/Range.hpp>
#include <amdis/common/TypeTraits.hpp>
// NOTE: backport of dune/typetree/traversal.hpp from Dune 2.7
namespace AMDiS {
enum class TreePathType
{
DYNAMIC, STATIC
};
namespace Impl {
// This is a constexpr version of the ternery operator c?t1:t1.
// In contrast to the latter the type of t1 and t2 can be different.
// Notice that std::conditional would not do the trick, because
// it only selects between types.
template<bool c, class T1, class T2,
std::enable_if_t<c, int> = 0>
constexpr auto conditionalValue(T1&& t1, T2&& t2) {
return std::forward<T1>(t1);
}
template<bool c, class T1, class T2,
std::enable_if_t<not c, int> = 0>
constexpr auto conditionalValue(T1&& t1, T2&& t2) {
return std::forward<T2>(t2);
}
/* The signature is the same as for the public applyToTree
* function in Dune::Typetree, despite the additionally passed
* treePath argument. The path passed here is associated to
* the tree and the relative paths of the children (wrt. to tree)
* are appended to this. Hence the behavior of the public function
* is resembled by passing an empty treePath.
*/
/*
* This is the overload for leaf traversal
*/
template<class T, class TP, class V,
std::enable_if_t<remove_cvref_t<T>::isLeaf, int> = 0>
void apply_to_tree(T&& tree, TP treePath, V&& visitor)
{
visitor.leaf(tree, treePath);
}
/*
* This is the general overload doing child traversal.
*/
template<class T, class TP, class V,
std::enable_if_t<not remove_cvref_t<T>::isLeaf, int> = 0>
void apply_to_tree(T&& tree, TP treePath, V&& visitor)
{
// Do we really want to take care for const-ness of the Tree
// when instantiating VisitChild below? I'd rather expect this:
// using Tree = remove_cvref_t<T>;
// using Visitor = remove_cvref_t<V>;
using Tree = std::remove_reference_t<T>;
using Visitor = std::remove_reference_t<V>;
visitor.pre(tree, treePath);
// Use statically encoded degree unless tree
// is a power node and dynamic traversal is requested.
constexpr auto useDynamicTraversal = (Tree::isPower and Visitor::treePathType==TreePathType::DYNAMIC);
auto degree = conditionalValue<useDynamicTraversal>(Tree::degree(), Dune::index_constant<Tree::degree()>{});
auto indices = Dune::range(degree);
Dune::Hybrid::forEach(indices, [&](auto i) {
auto childTP = Dune::TypeTree::push_back(treePath, i);
auto&& child = tree.child(i);
using Child = TYPEOF(child);
visitor.beforeChild(tree, child, treePath, i);
// This requires that visiotor.in(...) can always be instantiated,
// even if there's a single child only.
if (i>0)
visitor.in(tree, treePath);
static constexpr auto visitChild = Visitor::template VisitChild<Tree,Child,TP>::value;
if constexpr(visitChild)
applyToTree(child, childTP, visitor);
visitor.afterChild(tree, child, treePath, i);
});
visitor.post(tree, treePath);
}
// Overload for leaf nodes
template<class Tree, class TP, class Pre, class Leaf, class Post,
std::enable_if_t<remove_cvref_t<Tree>::isLeaf, int> = 0>
void for_each_node(Tree&& tree, TP treePath, Pre&& /*preFunc*/, Leaf&& leafFunc, Post&& /*postFunc*/)
{
leafFunc(tree, treePath);
}
// Overload for non-leaf nodes
// Forward declaration needed for recursion
template<class Tree, class TP, class Pre, class Leaf, class Post,
std::enable_if_t<not remove_cvref_t<Tree>::isLeaf,int> = 0>
void for_each_node(Tree&& tree, TP treePath, Pre&& preFunc, Leaf&& leafFunc, Post&& postFunc);
// Helper for power nodes
template<class Tree, class TP, class Pre, class Leaf, class Post, std::size_t... I,
std::enable_if_t<remove_cvref_t<Tree>::isPower, int> = 0>
void for_each_node_unfold(Tree&& tree, TP treePath, Pre&& preFunc, Leaf&& leafFunc, Post&& postFunc, std::index_sequence<I...>)
{
for (std::size_t i = 0; i < sizeof...(I); ++i)
Impl::for_each_node(tree.child(i), Dune::TypeTree::push_back(treePath, i), preFunc, leafFunc, postFunc);
}
// Helper for composite nodes
template<class Tree, class TP, class Pre, class Leaf, class Post, std::size_t... I,
std::enable_if_t<not remove_cvref_t<Tree>::isPower, int> = 0>
void for_each_node_unfold(Tree&& tree, TP treePath, Pre&& preFunc, Leaf&& leafFunc, Post&& postFunc, std::index_sequence<I...>)
{
(void)std::initializer_list<int>{(
Impl::for_each_node(tree.child(Dune::index_constant<I>{}),
Dune::TypeTree::push_back(treePath, Dune::index_constant<I>{}), preFunc, leafFunc, postFunc),0)...
};
}
/*
* Traverse tree and visit each node. The signature is the same
* as for the public for_each_node function in Dune::Typtree,
* despite the additionally passed treePath argument. The path
* passed here is associated to the tree and the relative
* paths of the children (wrt. to tree) are appended to this.
* Hence the behavior of the public function is resembled
* by passing an empty treePath.
*
* See also the specialization for leaf-nodes.
*/
template<class Tree, class TP, class Pre, class Leaf, class Post,
std::enable_if_t<not remove_cvref_t<Tree>::isLeaf,int>>
void for_each_node(Tree&& tree, TP treePath, Pre&& preFunc, Leaf&& leafFunc, Post&& postFunc)
{
auto indices = std::make_index_sequence<TYPEOF(tree)::degree()>{};
preFunc(tree, treePath);
Impl::for_each_node_unfold(tree, treePath, preFunc, leafFunc, postFunc, indices);
postFunc(tree, treePath);
}
} // namespace Impl
// ********************************************************************************
// Public Interface
// ********************************************************************************
//! Apply visitor to TypeTree.
namespace AMDiS
{
namespace Impl
{
/// \brief Helper function that returns the degree of a Tree.
/**
* \code
* #include <amdis/typetree/Traversal.hpp>
* \endcode
* This function applies the given visitor to the given tree. Both visitor and tree may be const
* or non-const (if the compiler supports rvalue references, they may even be a non-const temporary).
* The return type is either `size_t` if it is a dynamic tree or the flag `dynamic`
* is set to `true`, or as the type returned by the `degree()` member function of the
* tree.
*
* \note The visitor must implement the interface laid out by DefaultVisitor (most easily achieved by
* inheriting from it) and specify the required type of tree traversal (static or dynamic) by
* inheriting from either StaticTraversal or DynamicTraversal.
*
* \param tree The tree the visitor will be applied to.
* \param visitor The visitor to apply to the tree.
*/
template<typename Tree, typename Visitor>
void apply_to_tree(Tree&& tree, Visitor&& visitor)
* This function allows to change the tree traversal from static to dynamic in case
* of power nodes and uses static traversal for composite and dynamic traversal for
* all dynamic nodes.
**/
template <bool dynamic = true, class Tree>
auto traversalDegree(Tree const& tree)
{
auto root = Dune::TypeTree::hybridTreePath();
Impl::apply_to_tree(tree, root, visitor);
if constexpr (dynamic && Tree::isPower)
return std::size_t(tree.degree());
else if constexpr (Tree::isPower || Tree::isComposite)
return std::integral_constant<std::size_t, Tree::degree()>{};
else
return tree.degree();
}
/**
* \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 Pre, class Leaf, class Post>
void for_each_node(Tree&& tree, Pre&& preFunc, Leaf&& leafFunc, Post&& postFunc)
* Traverse tree and visit each node. The signature is the same
* as for the public for_each_node function in Dune::Typtree,
* despite the additionally passed treePath argument. The path
* passed here is associated to the tree and the relative
* paths of the children (wrt. to tree) are appended to this.
* Hence the behavior of the public function is resembled
* by passing an empty treePath.
**/
template <class Tree, class TreePath, class PreFunc, class LeafFunc, class PostFunc>
void for_each_node(Tree&& tree, TreePath treePath, PreFunc&& preFunc, LeafFunc&& leafFunc, PostFunc&& postFunc)
{
auto root = Dune::TypeTree::hybridTreePath();
Impl::for_each_node(tree, root, 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 Inner, class Leaf>
void for_each_node(Tree&& tree, Inner&& innerFunc, Leaf&& leafFunc)
{
auto root = Dune::TypeTree::hybridTreePath();
Impl::for_each_node(tree, root, innerFunc, leafFunc, NoOp{});
}
/**
* \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 for_each_node(Tree&& tree, NodeFunc&& nodeFunc)
{
auto root = Dune::TypeTree::hybridTreePath();
Impl::for_each_node(tree, root, nodeFunc, nodeFunc, NoOp{});
using TreeType = std::decay_t<Tree>;
if constexpr (TreeType::isLeaf) {
// If we have a leaf tree just visit it using the leaf function.
leafFunc(tree, treePath);
} else {
// Otherwise visit the tree with the pre function,
// visit all children using a static or dynamic loop, and
// finally visit the tree with the post function.
preFunc(tree, treePath);
Dune::Hybrid::forEach(Dune::range(traversalDegree(tree)), [&](auto i) {
auto childTreePath = push_back(treePath, i);
Impl::for_each_node(tree.child(i), childTreePath, preFunc, leafFunc, postFunc);
});
postFunc(tree, treePath);
}
}
/**
* \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 Leaf>
void for_each_leaf_node(Tree&& tree, Leaf&& leafFunc)
{
auto root = Dune::TypeTree::hybridTreePath();
Impl::for_each_node(tree, root, NoOp{}, leafFunc, NoOp{});
}
} // end namespace Impl
/// \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 for_each_node(Tree&& tree, PreFunc&& preFunc, LeafFunc&& leafFunc, PostFunc&& postFunc)
{
auto root = Dune::TypeTree::hybridTreePath();
Impl::for_each_node(tree, root, 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 for_each_node(Tree&& tree, InnerFunc&& innerFunc, LeafFunc&& leafFunc)
{
auto root = Dune::TypeTree::hybridTreePath();
Impl::for_each_node(tree, root, innerFunc, leafFunc, NoOp{});
}
/// \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 for_each_node(Tree&& tree, NodeFunc&& nodeFunc)
{
auto root = Dune::TypeTree::hybridTreePath();
Impl::for_each_node(tree, root, nodeFunc, nodeFunc, NoOp{});
}
/// \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 for_each_leaf_node(Tree&& tree, LeafFunc&& leafFunc)
{
auto root = Dune::TypeTree::hybridTreePath();
Impl::for_each_node(tree, root, NoOp{}, leafFunc, NoOp{});
}
} // end namespace AMDiS
......@@ -20,8 +20,8 @@ namespace AMDiS
{
namespace Impl
{
/*
* \brief A factory class creating a hybrid container compatible with a type tree
/// \brief A factory class creating a hybrid container compatible with a type tree
/**
*
* This class allows to create a nested hybrid container having the same structure
* as a given type tree. Power nodes are represented as std::array's while composite
......@@ -31,44 +31,41 @@ namespace AMDiS
*
* \tparam LeafToValue Type of a predicate that determines the stored values at the leafs
*/
template<class LeafToValue>
template <class LeafToValue>
class ContainerFactory
{
public:
/// \brief Create ContainerFactory
/**
* \brief Create ContainerFactory
*
* The given predicate will be stored by value.
*
* \param A predicate used to generate the stored values for the leaves
*/
ContainerFactory(LeafToValue leafToValue) :
leafToValue_(leafToValue)
ContainerFactory(LeafToValue leafToValue)
: leafToValue_(std::move(leafToValue))
{}
template<class Node,
std::enable_if_t<Node::isLeaf, int> = 0>
auto operator()(const Node& node)
{
return leafToValue_(node);
}
template<class Node,
std::enable_if_t<Node::isPower, int> = 0>
auto operator()(const Node& node)
{
using TransformedChild = decltype((*this)(node.child(0)));
return std::array<TransformedChild, Node::degree()>();
}
template<class Node,
std::enable_if_t<Node::isComposite, int> = 0>
auto operator()(const Node& node)
/// \brief Return a container for storing the node content
template <class Node>
auto operator() (Node const& node) const
{
return Tools::apply_indices([&](auto... indices) {
return Dune::makeTupleVector((*this)(node.child(indices))...);
}, index_t<Node::degree()>{});
if constexpr (Node::isLeaf)
return leafToValue_(node);
else
if constexpr (Node::isPower) {
using TransformedChild = decltype((*this)(node.child(0)));
return std::array<TransformedChild, Node::degree()>();
}
else
if constexpr (Node::isComposite) {
return Tools::apply_indices<Node::degree()>(
[&](auto... i) { return Dune::makeTupleVector((*this)(node.child(i))...); });
}
else {
static_assert(Node::isLeaf || Node::isPower || Node::isComposite,
"Node must be one of leaf,power,composite.");
return;
}
}
private:
......@@ -76,67 +73,69 @@ namespace AMDiS
};
/*
* \brief Wrap nested container to provide a VectorBackend
*/
template<class Container>
/// \brief Wrap nested container to provide a VectorBackend
template <class Container>
class TreeContainerVectorBackend
{
using Self = TreeContainerVectorBackend;
template<class C>
static constexpr decltype(auto) accessByTreePath(C&& container, const Dune::TypeTree::HybridTreePath<>& path)
template <class C>
static constexpr decltype(auto) accessByTreePath(C&& container, Dune::TypeTree::HybridTreePath<> const& path)
{
return container;
}
template<class C, class... T>
static constexpr decltype(auto) accessByTreePath(C&& container, const Dune::TypeTree::HybridTreePath<T...>& path)
template <class C, class... T>
static constexpr decltype(auto) accessByTreePath(C&& container, Dune::TypeTree::HybridTreePath<T...> const& path)
{
auto head = Dune::TypeTree::treePathEntry(path,Dune::Indices::_0);
return accessByTreePath(container[head], pop_front(path));
}
public:
TreeContainerVectorBackend() = default;
/// \brief Default-construct the tree-container
TreeContainerVectorBackend()
: container_()
{}
/// \brief Construct the tree-container from a given container storage
TreeContainerVectorBackend(Container&& container)
: container_(std::move(container))
{}
TreeContainerVectorBackend(const Self&) = default;
TreeContainerVectorBackend(Self&&) = default;
Self& operator=(const Self&) = default;
Self& operator=(Self&&) = default;
template<class... T>
decltype(auto) operator[](const Dune::TypeTree::HybridTreePath<T...>& path) const
/// \brief Access a (const) element of the container by treepath
template <class... T>
decltype(auto) operator[](Dune::TypeTree::HybridTreePath<T...> const& path) const
{
return accessByTreePath(container_, path);
}
template<class... T>
decltype(auto) operator[](const Dune::TypeTree::HybridTreePath<T...>& path)
/// \brief Access a (mutable) element of the container by treepath
template <class... T>
decltype(auto) operator[](Dune::TypeTree::HybridTreePath<T...> const& path)
{
return accessByTreePath(container_, path);
}
const Container& data() const
/// \brief Obtain the container (const)
Container const& data() const
{
return container_;
}
/// \brief Obtain the container (mutable)
Container& data()
{
return container_;
}
/// \brief Compare two containers for equality
bool operator==(TreeContainerVectorBackend const& other) const
{
return container_ == other.container_;
}
/// \brief Compare two containers for inequality
bool operator!=(TreeContainerVectorBackend const& other) const
{
return container_ != other.container_;
......@@ -146,7 +145,7 @@ namespace AMDiS
Container container_;
};
template<class Container>
template <class Container>
auto makeTreeContainerVectorBackend(Container&& container)
{
return TreeContainerVectorBackend<remove_cvref_t<Container>>(FWD(container));
......@@ -154,67 +153,59 @@ namespace AMDiS
} // end namespace Impl
/** \addtogroup TypeTree
* \{
*/
/**
* \brief Create container havin the same structure as the given tree
*
* This class allows to create a nested hybrid container having the same structure
* as a given type tree. Power nodes are represented as std::array's while composite
* nodes are represented as Dune::TupleVector's. The stored values for the leaf nodes
* are creating using a given predicate. For convenience the created container is
* not returned directly. Instead, the returned object stores the container and
* provides operator[] access using a HybridTreePath.
*
* \param tree The tree which should be mapper to a container
* \param leafToValue A predicate used to generate the stored values for the leaves
*
* \returns A container matching the tree structure
*/
template<class Tree, class LeafToValue>
auto makeTreeContainer(const Tree& tree, LeafToValue&& leafToValue)
{
auto f = std::ref(leafToValue);
auto factory = Impl::ContainerFactory<decltype(f)>(f);
return Impl::makeTreeContainerVectorBackend(factory(tree));
}
/**
* \brief Create container havin the same structure as the given tree
*
* This class allows to create a nested hybrid container having the same structure
* as a given type tree. Power nodes are represented as std::array's while composite
* nodes are represented as Dune::TupleVector's. The stored values for the leaf nodes
* are of the given type Value. For convenience the created container is
* not returned directly. Instead, the returned object stores the container and
* provides operator[] access using a HybridTreePath.
*
* \tparam Value Type of the values to be stored for the leafs. Should be default constructible.
* \param leafToValue A predicate used to generate the stored values for the leaves
*
* \returns A container matching the tree structure
*/
template<class Value, class Tree>
auto makeTreeContainer(const Tree& tree)
{
return makeTreeContainer(tree, [](const auto&) {return Value{};});
}
template<template<class> class NodeData, class Tree>
auto makeTreeContainer(const Tree& tree)
{
return makeTreeContainer(tree, [](const auto& node) {return NodeData<TYPEOF(node)>{};});
}
/// \brief Create container havin the same structure as the given tree
/**
* This class allows to create a nested hybrid container having the same structure
* as a given type tree. Power nodes are represented as std::array's while composite
* nodes are represented as Dune::TupleVector's. The stored values for the leaf nodes
* are creating using a given predicate. For convenience the created container is
* not returned directly. Instead, the returned object stores the container and
* provides operator[] access using a HybridTreePath.
*
* \param tree The tree which should be mapper to a container
* \param leafToValue A predicate used to generate the stored values for the leaves
*
* \returns A container matching the tree structure