Commit 697bec7a authored by Steffen Müthing's avatar Steffen Müthing

Merge branch 'feature/treecontainer' into 'master'

Implement helper makeTreeContainer() to construct container matching the tree structure

See merge request !37
parents 409f3056 b5b05e80
Pipeline #9672 passed with stage
in 5 minutes and 43 seconds
......@@ -19,6 +19,7 @@ install(FILES
transformationutilities.hh
traversal.hh
traversalutilities.hh
treecontainer.hh
treepath.hh
typetraits.hh
typetree.hh
......
// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
// vi: set et ts=4 sw=2 sts=2:
#ifndef DUNE_TYPETREE_TREECONTAINER_HH
#define DUNE_TYPETREE_TREECONTAINER_HH
#include <type_traits>
#include <utility>
#include <functional>
#include <array>
#include <dune/common/indices.hh>
#include <dune/common/tuplevector.hh>
#include <dune/typetree/treepath.hh>
namespace Dune {
namespace TypeTree {
namespace Detail {
/*
* \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
* nodes are represented as Dune::TupleVector's. The stored values for the leaf nodes
* are creating using a given predicate. Once created, the factory provides an
* operator() creating the container for the tree given as argument.
*
* \tparam LeafToValue Type of a predicate that determines the stored values at the leafs
*/
template<class LeafToValue>
class ContainerFactory
{
public:
/**
* \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)
{}
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)
{
return Dune::unpackIntegerSequence([&](auto... indices) {
return Dune::makeTupleVector((*this)(node.child(indices))...);
}, std::make_index_sequence<Node::degree()>());
}
private:
LeafToValue leafToValue_;
};
/*
* \brief Wrap nested container to provide a VectorBackend
*/
template<class Container>
class TreeContainerVectorBackend
{
template<class C>
static constexpr decltype(auto) accessByTreePath(C&& container, const HybridTreePath<>& path)
{
return container;
}
template<class C, class... T>
static constexpr decltype(auto) accessByTreePath(C&& container, const HybridTreePath<T...>& path)
{
auto head = path[Dune::Indices::_0];
auto tailPath = Dune::unpackIntegerSequence([&](auto... i){
return treePath(path[i+1]...);
}, std::make_index_sequence<sizeof...(T)-1>());
return accessByTreePath(container[head], tailPath);
}
public:
TreeContainerVectorBackend(Container&& container) :
container_(std::move(container))
{}
TreeContainerVectorBackend(TreeContainerVectorBackend&& other) :
container_(std::move(other.container_))
{}
template<class... T>
decltype(auto) operator[](const HybridTreePath<T...>& path) const
{
return accessByTreePath(container_, path);
}
template<class... T>
decltype(auto) operator[](const HybridTreePath<T...>& path)
{
return accessByTreePath(container_, path);
}
const Container& data() const
{
return container_;
}
Container& data()
{
return container_;
}
private:
Container container_;
};
template<class Container>
auto makeTreeContainerVectorBackend(Container&& container)
{
return TreeContainerVectorBackend<std::decay_t<Container>>(std::forward<Container>(container));
}
} // namespace Detail
/** \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 = Detail::ContainerFactory<decltype(f)>(f);
return Detail::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{};});
}
/**
* \brief Alias to container type generated by makeTreeContainer for given value and tree type
*/
template<class Value, class Tree>
using TreeContainer = std::decay_t<decltype(makeTreeContainer<Value>(std::declval<const Tree&>()))>;
//! \} group TypeTree
} // namespace TypeTree
} //namespace Dune
#endif // DUNE_TYPETREE_TREECONTAINER_HH
......@@ -25,3 +25,5 @@ dune_add_test(SOURCES testproxynode.cc
dune_add_test(SOURCES testcallbacktraversal.cc)
dune_add_test(SOURCES testhybridtreepath.cc)
dune_add_test(SOURCES testtreecontainer.cc)
#include "config.h"
#include "typetreetestutility.hh"
#include <dune/typetree/traversal.hh>
#include <dune/typetree/treecontainer.hh>
#include <dune/common/test/testsuite.hh>
template<class Tree>
std::string treeName(const Tree& tree)
{
std::string s;
Dune::TypeTree::forEachNode(tree,
[&](auto&& node, auto&& path){ s += node.name(); s += "<"; },
[&](auto&& node, auto&& path){ s += node.name(); s += ","; },
[&](auto&& node, auto&& path){ s += ">"; });
return s;
}
template<class F>
bool notThrown(F&& f)
{
try {
f();
return true;
}
catch(...) {}
return false;
}
template<class Tree, class Value>
Dune::TestSuite checkTreeContainer(const Tree& tree, const Value& value)
{
Dune::TestSuite test(treeName(tree));
auto container = Dune::TypeTree::makeTreeContainer<Value>(tree);
Dune::TypeTree::forEachLeafNode(tree, [&] (auto&& node, auto treePath) {
test.check(notThrown([&]() {
container[treePath] = value;
})) << "Assigning desired value to tree container entry failed";
});
Dune::TypeTree::forEachLeafNode(tree, [&] (auto&& node, auto treePath) {
test.check(container[treePath] == value)
<< "Value in tree container does not match assigned value";
});
return test;
}
int main(int argc, char** argv)
{
Dune::TestSuite test;
int v1 = 42;
std::vector<double> v2{1,2,3,4};
using SL1 = SimpleLeaf;
SL1 sl1;
test.subTest(checkTreeContainer(sl1, v1));
test.subTest(checkTreeContainer(sl1, v2));
using SP1 = SimplePower<SimpleLeaf,3>;
SP1 sp1(sl1, sl1, sl1);
test.subTest(checkTreeContainer(sp1, v1));
test.subTest(checkTreeContainer(sp1, v2));
using SL2 = SimpleLeaf;
using SP2 = SimplePower<SimpleLeaf,2>;
using SC1 = SimpleComposite<SL1,SP1,SP2>;
SL2 sl2;
SP2 sp2(sl2,sl2);
SC1 sc1_1(sl1,sp1,sp2);
test.subTest(checkTreeContainer(sc1_1, v1));
test.subTest(checkTreeContainer(sc1_1, v2));
test.report();
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