diff --git a/dune/subgrid/CMakeLists.txt b/dune/subgrid/CMakeLists.txt index 5f078aad77f62ec6cb17e1a9db05778c01c5b56a..c09b0c42fa656bcabbfd5ae6fc2d9088e1427a0a 100644 --- a/dune/subgrid/CMakeLists.txt +++ b/dune/subgrid/CMakeLists.txt @@ -1,3 +1,4 @@ +add_subdirectory(common) add_subdirectory(facetools) add_subdirectory(subgrid) add_subdirectory(test) diff --git a/dune/subgrid/common/CMakeLists.txt b/dune/subgrid/common/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..a31ec91c4cfb2205d700956f5a1633d72fe8b067 --- /dev/null +++ b/dune/subgrid/common/CMakeLists.txt @@ -0,0 +1,5 @@ +set(commonincludedir ${CMAKE_INSTALL_INCLUDEDIR}/dune/subgrid/common) +set(commoninclude_HEADERS variant.hh) +# include not needed for CMake +# include $(top_srcdir)/am/global-rules +install(FILES ${commoninclude_HEADERS} DESTINATION ${commonincludedir}) diff --git a/dune/subgrid/common/variant.hh b/dune/subgrid/common/variant.hh new file mode 100644 index 0000000000000000000000000000000000000000..e2132ea6c66de8266669eb87f8b9a44fe6143837 --- /dev/null +++ b/dune/subgrid/common/variant.hh @@ -0,0 +1,387 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: +#ifndef DUNE_COMMON_STD_VARIANT_HH +#define DUNE_COMMON_STD_VARIANT_HH +#include <tuple> +#include <memory> +#include <dune/common/hybridutilities.hh> +#include <dune/common/exceptions.hh> + +namespace Dune { +namespace Std { +namespace Impl { + + /* helper constructs to find position of a type T in a pack Ts... */ + template <typename T, typename... Ts> + struct index_in_pack; + + template <typename T, typename... Ts> + struct index_in_pack<T, T, Ts...> : std::integral_constant<std::size_t, 0> {}; + + template <typename T, typename U, typename... Ts> + struct index_in_pack<T, U, Ts...> : std::integral_constant<std::size_t, 1 + index_in_pack<T, Ts...>::value> {}; + + /* end helper constructs to find position of a type T in a pack Ts... */ + + template<typename Tp> + struct Buffer_ : std::aligned_storage<sizeof(Tp)> { + using Storage = typename std::aligned_storage_t<sizeof(Tp)>::type; + Storage storage_; + + void* addr() { + return static_cast<void*>(&storage_); + } + + const void* addr() const { + return static_cast<const void*>(&storage_); + } + + Tp* ptr() { + return static_cast<Tp*>(addr()); + } + + const Tp* ptr() const { + return static_cast<const Tp*>(addr()); + } + }; + + template<typename Tp, bool isTrivial> + struct TypeStorage_ { }; + + template<typename Tp> + struct TypeStorage_<Tp, true> { + TypeStorage_(Tp t) : + tp_(t) {} + + template<typename... Args> + TypeStorage_(Args... args) : + tp_(args...) {} + + auto& get() { + return tp_; + } + const auto& get() const { + return tp_; + } + private: + Tp tp_; + }; + + template<typename Tp> + struct TypeStorage_<Tp, false> { + TypeStorage_(Tp t) { + ::new (&tp_) Tp(t); + } + + template<typename... Args> + TypeStorage_(Args... args) { + ::new (&tp_) Tp(std::forward<Args>(args)...); + } + + auto& get() { + return *(tp_.ptr()); + } + const auto& get() const { + return *(tp_.ptr()); + } + + private: + Buffer_<Tp> tp_; + }; + + template<typename... T> + union variant_union_ {}; + + template<typename Head_, typename... Tail_> + union variant_union_<Head_, Tail_...> { + constexpr variant_union_() : + tail_() {} + + template<typename... Args> + constexpr variant_union_(std::integral_constant<size_t, 0>, Args&&... args) : + head_(std::forward<Args...>(args)...) {} + + template<size_t N, typename... Args> + constexpr variant_union_(std::integral_constant<size_t, N>, Args&&... args) : + tail_(std::integral_constant<size_t, N-1>(), std::forward<Args...>(args)...) {} + + auto& getByIndex(std::integral_constant<size_t, 0>) { + return head_.get(); + } + + const auto& getByIndex(std::integral_constant<size_t, 0>) const { + return head_.get(); + } + + + template<size_t N> + auto& getByIndex(std::integral_constant<size_t, N>) { + return tail_.getByIndex(std::integral_constant<size_t, N-1>()); + } + + template<size_t N> + const auto& getByIndex(std::integral_constant<size_t, N>) const { + return tail_.getByIndex(std::integral_constant<size_t, N-1>()); + } + + template<typename Tp> + void set(Tp obj) { + Dune::Hybrid::ifElse(std::is_same<Tp, Head_>(), + [&](auto&& id) { head_=std::move(id(obj)); }, + [&](auto&& id) { return id(tail_).set(std::move(obj)); } + ); + } + + constexpr size_t size() const { + return sizeof...(Tail_)+1; + } + + private: + TypeStorage_<Head_, std::is_literal_type<Head_>::value> head_; + variant_union_<Tail_...> tail_; + }; + + template<typename...T> + struct variant_{ + + constexpr variant_() : + unions_() {} + + template<typename Tp> + constexpr variant_(Tp obj) : + unions_(), + index_(index_in_pack<Tp, T...>::value) + { + unions_.set(std::move(obj)); + } + + template<typename Tp> + auto& get() { + constexpr size_t idx = index_in_pack<Tp, T...>::value; + if (index_ != idx) + DUNE_THROW(Dune::Exception, "Bad variant access."); + + return get<idx>(); + } + + template<typename Tp> + const auto& get() const { + constexpr size_t idx = index_in_pack<Tp, T...>::value; + if (index_ != idx) + DUNE_THROW(Dune::Exception, "Bad variant access."); + + return get<idx>(); + } + + template<typename Tp> + Tp* get_if() { + if (not holds_alternative<Tp>()) + return (Tp*) nullptr; + else + return &(get<Tp>()); + } + + template<typename Tp> + const Tp* get_if() const { + if (not holds_alternative<Tp>()) + return (Tp*) nullptr; + else + return &(get<Tp>()); + } + + template<size_t N> + auto* get_if() { + using Tp = std::decay_t<decltype(get<N>())>; + if (not holds_alternative<N>()) + return (Tp*) nullptr; + else + return &(get<Tp>()); + } + + template<size_t N> + const auto* get_if() const { + using Tp = std::decay_t<decltype(get<N>())>; + if (not holds_alternative<N>()) + return (Tp*) nullptr; + else + return &(get<Tp>()); + } + + template<size_t N> + auto& get() { + if (index_ != N) + DUNE_THROW(Dune::Exception, "Bad variant access."); + return unions_.template getByIndex(std::integral_constant<size_t, N>()); + } + template<size_t N> + const auto& get() const { + if (index_ != N) + DUNE_THROW(Dune::Exception, "Bad variant access."); + return unions_.template getByIndex(std::integral_constant<size_t, N>()); + } + + template<typename Tp> + constexpr Tp& operator=(Tp obj) { + unions_.set(std::move(obj)); + constexpr auto index = index_in_pack<Tp, T...>::value; + index_=index; + return unions_.getByIndex(std::integral_constant<size_t,index>()); + } + + constexpr std::size_t index() const noexcept { + return index_; + } + + constexpr auto size() const { + return sizeof...(T); + } + + /* \brief Apply visitor to the active variant. + * + * visit assumes that the result of + * func(T) has the same type for all types T + * in this variant. + */ + template<typename F> + auto visit(F&& func) { + using namespace Dune::Hybrid; + + using Result = decltype(func(unions_.getByIndex(std::integral_constant<size_t, 0>()))); + + return ifElse(std::is_same<Result, void>(), [&, this](auto id) { + constexpr auto tsize = size_; + Dune::Hybrid::forEach(Dune::Hybrid::integralRange(std::integral_constant<size_t, tsize>()), [&](auto i) { + if (i==this->index_) + func(id(unions_).getByIndex(std::integral_constant<size_t, i>())); + }); + return;}, + [&func,this](auto id) { + constexpr auto tsize = size_; + + auto result = std::unique_ptr<Result>(); + + Dune::Hybrid::forEach(Dune::Hybrid::integralRange(std::integral_constant<size_t, tsize>()), [&, this](auto i) { + if (i==this->index_) + result = std::make_unique<Result>(func(id(this->unions_).getByIndex(std::integral_constant<size_t, i>()))); + }); + return *result; + }); + } + + template<typename F> + auto visit(F&& func) const { + using namespace Dune::Hybrid; + + using Result = decltype(func(unions_.getByIndex(std::integral_constant<size_t, 0>()))); + + return ifElse(std::is_same<Result, void>(), [&, this](auto id) { + constexpr auto tsize = size_; + Dune::Hybrid::forEach(Dune::Hybrid::integralRange(std::integral_constant<size_t, tsize>()), [&](auto i) { + if (i==this->index_) + func(id(unions_).getByIndex(std::integral_constant<size_t, i>())); + }); + return;}, + [&func,this](auto id) { + constexpr auto tsize = size_; + + auto result = std::unique_ptr<Result>(); + + Dune::Hybrid::forEach(Dune::Hybrid::integralRange(std::integral_constant<size_t, tsize>()), [&, this](auto i) { + if (i==this->index_) + result = std::make_unique<Result>(func(id(this->unions_).getByIndex(std::integral_constant<size_t, i>()))); + }); + return *result; + }); + } + + /** \brief Check if a given type is the one that is currently active in the variant. */ + template<typename Tp> + constexpr bool holds_alternative() const { + // I have no idea how this could be really constexpr, but for STL-conformity, + // I'll leave the modifier there. + return (index_in_pack<Tp, T...>::value == index_); + } + + /** \brief Check if a given type is the one that is currently active in the variant. */ + template<size_t N> + constexpr bool holds_alternative() const { + // I have no idea how this could be really constexpr, but for STL-conformity, + // I'll leave the modifier there. + return (N == index_); + } + + private: + variant_union_<T...> unions_; + std::size_t index_; + constexpr static auto size_ = sizeof...(T); + }; + +} // end namespace Impl + + /** \brief Incomplete re-implementation of C++17's std::variant. */ + template<typename ...T> + using variant = Impl::variant_<T...>; + + template<size_t N, typename... T> + auto& get(variant<T...>& var) { + return var.template get<N>(); + } + + template<size_t N, typename... T> + const auto& get(const variant<T...>& var) { + return var.template get<N>(); + } + + template<typename F, typename... T> + auto visit(F&& visitor, variant<T...>& var) { + return var.visit(std::forward<F>(visitor)); + } + + template<typename F, typename... T> + auto visit(F&& visitor, const variant<T...>& var) { + return var.visit(std::forward<F>(visitor)); + } + + template<typename Tp, typename ...T> + auto& get(variant<T...>& var) { + return var.template get<Tp>(); + } + + template<typename Tp, typename ...T> + const auto& get(const variant<T...>& var) { + return var.template get<Tp>(); + } + + template<typename Tp, typename ...T> + const auto* get_if(const variant<T...>& var) { + return var.template get_if<Tp>(); + } + + template<typename Tp, typename ...T> + auto* get_if(variant<T...>& var) { + return var.template get_if<Tp>(); + } + + template<size_t N, typename ...T> + const auto* get_if(const variant<T...>& var) { + return var.template get_if<N>(); + } + + template<size_t N, typename ...T> + auto* get_if(variant<T...>& var) { + return var.template get_if<N>(); + } + + template<typename Tp, typename ...T> + constexpr bool holds_alternative(const variant<T...>& var) { + return var.template holds_alternative<Tp>(); + } + + template <typename... T> + constexpr auto variant_size_v(const variant<T...>&) { + return std::integral_constant<std::size_t,sizeof...(T)>::value; + } + +} // end namespace Std +} // end namespace Dune +#endif diff --git a/dune/subgrid/test/CMakeLists.txt b/dune/subgrid/test/CMakeLists.txt index 6bc998a46579b03de3f37a4c53f7281ef75ea68c..81d3862e3ca7ab6bdf798db9117e3cb103ce9039 100644 --- a/dune/subgrid/test/CMakeLists.txt +++ b/dune/subgrid/test/CMakeLists.txt @@ -1,5 +1,5 @@ -set(TESTPROGS test-w-onedgrid test-w-yaspgrid test-w-alugrid) +set(TESTPROGS test-w-onedgrid test-w-yaspgrid test-w-alugrid testvariant) if(ALBERTA_FOUND) list(APPEND TESTPROGS test-w-albertagrid2d) diff --git a/dune/subgrid/test/testvariant.cc b/dune/subgrid/test/testvariant.cc new file mode 100644 index 0000000000000000000000000000000000000000..ca0393f89e1c53ab03a65ce361ddeea4fe45d7bb --- /dev/null +++ b/dune/subgrid/test/testvariant.cc @@ -0,0 +1,107 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#include <iostream> +#include <dune/common/parallel/mpihelper.hh> // An initializer of MPI +#include <dune/common/exceptions.hh> // We use exceptions +#include <dune/common/test/testsuite.hh> + +#include <dune/subgrid/common/variant.hh> + +// some non-default constructible type +struct F { + int i; + F() = delete; + F(int j) : + i(j) {} +}; + +Dune::TestSuite testVariant() { + using namespace Dune; + TestSuite suite; + + int i = 42; + double d = 3.14; + F f(13); + using V = std::vector<int>; + + auto variant = Std::variant<int, double, F, V>(); + + suite.check(Std::variant_size_v(variant) == 4, "Test variant_size_v"); + + + variant = d; + suite.check(Std::holds_alternative<double>(variant), "Test holds_alternative"); + + variant = f; + suite.check(Std::holds_alternative<F>(variant), "Test holds_alternative"); + + variant = i; + suite.check(Std::holds_alternative<int>(variant), "Test holds_alternative"); + + suite.check(Std::get<int>(variant) == i, "Test get<Type>"); + suite.check(Std::get<0>(variant) == i, "Test get<Index>"); + + suite.check(Std::get_if<int>(variant) != nullptr, "Test get_if on right type"); + suite.check(Std::get_if<double>(variant) == nullptr, "Test get_if on wrong type"); + + suite.check(Std::get_if<0>(variant) != nullptr, "Test get_if on right index"); + suite.check(Std::get_if<1>(variant) == nullptr, "Test get_if on wrong index"); + + // test if get<Type> throws if one tries to get the wrong type: + try { + // currently hold type is still int, so double should throw + Std::get<double>(variant); + suite.check(false, "Test get<Type> on wrong type should have thrown"); + } + catch (...) { + suite.check(true, "Test get<Type> on wrong type has thrown"); + } + + + variant = V(1); + suite.check(Std::get<V>(variant).size() == 1, "Test with non-trivial type"); + + variant = f; + + suite.check(variant.index() == 2, "Test index()"); // we're at type F, which has position 2 + + // Demonstrate visit concept and using vector as an example of a non-POD type + using V2 = std::vector<double>; + Std::variant<V, V2> variant2; + variant2 = V(1); + auto size = [](auto&& v) {return v.size();}; + suite.check(Std::visit(size, variant2)== 1, "Test visit"); + variant2 = V2(2); + suite.check(Std::visit(size, variant2)== 2, "Test visit"); + + // try on a const reference: + const auto& constv2 = variant2; + suite.check(Std::visit(size, constv2)== 2, "Test const visit"); + suite.check(Std::get_if<V2>(constv2) != nullptr, "Test const get_if"); + + + return suite; +} + +int main(int argc, char** argv) +{ + try{ + // Maybe initialize MPI + Dune::MPIHelper::instance(argc, argv); + + Dune::TestSuite suite; + suite.subTest(testVariant()); + + return suite.exit(); + } + catch (Dune::Exception &e){ + std::cerr << "Dune reported error: " << e << std::endl; + } + catch (...){ + std::cerr << "Unknown exception thrown!" << std::endl; + } +}