// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- // vi: set et ts=4 sw=2 sts=2: #ifndef DUNE_SOLVERS_COMMON_ALGORITHM_HH #define DUNE_SOLVERS_COMMON_ALGORITHM_HH #include <dune/common/indices.hh> #include <dune/common/typeutilities.hh> #include <dune/common/typetraits.hh> #include <dune/istl/multitypeblockvector.hh> namespace Dune { namespace Solvers { // Implementation of integralRangeFor namespace Imp { template<class ST, ST begin, ST end> struct StaticForLoop { template<class F, class...Args> static void apply(F&& f, Args&&... args) { f(std::integral_constant<ST, begin>(), std::forward<Args>(args)...); StaticForLoop<ST, begin+1, end>::apply(std::forward<F>(f), std::forward<Args>(args)...); } }; template<class ST, ST end> struct StaticForLoop<ST, end, end> { template<class F, class...Args> static void apply(F&& f, Args&&...) {} }; // Overload for static ranges template<class Index, class Begin, class End, class F, class... Args, std::enable_if_t<IsIntegralConstant<Begin>::value and IsIntegralConstant<End>::value, int> = 0> void integralRangeFor(Begin&& begin, End&& end, F&& f, Args&&... args) { static const Index begin_t = std::decay_t<Begin>::value; static const Index end_t = std::decay_t<End>::value; StaticForLoop<Index, begin_t, end_t>::apply(std::forward<F>(f), std::forward<Args>(args)...); } // Overload for dynamic ranges template<class Index, class Begin, class End, class F, class... Args, std::enable_if_t<not(IsIntegralConstant<Begin>::value and IsIntegralConstant<End>::value), int> = 0> void integralRangeFor(Begin&& begin, End&& end, F&& f, Args&&... args) { for(Index i=begin; i != end; ++i) f(i, std::forward<Args>(args)...); } } /** * \brief Hybrid for loop over integral range * * \tparam Index Raw type of used indices * \tparam Begin Type of begin index * \tparam End Type of end index * \tparam F Type of functor containing the loop body * \tparam Args Types of further arguments to the loop body * * \param begin Initial index * \param end One past last index * \param f Functor to call in each loop instance * \param args Additional arguments to be passed to the functor * * This is a hybrid for loop that can work on statically and dynamically * sized containers. The functor is called with index as first argument * and all additional arguments. If begin and end are both of type * std::integral_constant<*,*> than the loop is static with indices * of the form std::integral_constant<Index, *>, otherwise the loop * is dynamic with indices type Index. */ template<class Index, class Begin, class End, class F, class... Args> void integralRangeFor(Begin&& begin, End&& end, F&& f, Args&&... args) { Imp::integralRangeFor<Index>(std::forward<Begin>(begin), std::forward<End>(end), std::forward<F>(f), std::forward<Args>(args)...); } // Implementation of hybridEquals namespace Imp { // Compute t1==t2 either statically or dynamically template<class T1, class T2> constexpr auto hybridEquals(const T1& t1, const T2& t2, PriorityTag<1>) -> decltype(T1::value, T2::value, std::integral_constant<bool,T1::value == T2::value>()) { return {}; } template<class T1, class T2> constexpr auto hybridEquals(const T1& t1, const T2& t2, PriorityTag<0>) { return t1==t2; } } //end namespace Imp /** * \brief Hybrid equality comparison * * If both types have a static member value, the result of comparing * these is returned as std::integral_constant<bool, *>. Otherwise * the result of a runtime comparison of t1 and t2 is directly returned. */ template<class T1, class T2> constexpr auto hybridEquals(const T1& t1, const T2& t2) { return Imp::hybridEquals(t1, t2, PriorityTag<1>()); } // Implementation of hybridIf namespace Imp { template<class IfFunc, class ElseFunc> constexpr void hybridIf(std::true_type, IfFunc&& ifFunc, ElseFunc&& elseFunc) { ifFunc([](auto&& x) { return std::forward<decltype(x)>(x);}); } template<class IfFunc, class ElseFunc> constexpr void hybridIf(std::false_type, IfFunc&& ifFunc, ElseFunc&& elseFunc) { elseFunc([](auto&& x) { return std::forward<decltype(x)>(x);}); } template<class IfFunc, class ElseFunc> constexpr void hybridIf(const bool& condition, IfFunc&& ifFunc, ElseFunc&& elseFunc) { if (condition) ifFunc([](auto&& x) { return std::forward<decltype(x)>(x);}); else elseFunc([](auto&& x) { return std::forward<decltype(x)>(x);}); } } //end namespace Imp /** * \brief Hybrid if * * This will call either ifFunc or elseFunc depending * on the condition. In any case a single argument * will be passed to the called function. This will always * be the indentity function. Passing an expression through * this function will lead to lazy evaluation. This way both * 'branches' can contain expressions that are only valid * within this branch if the condition is a std::integral_constant<bool,*>. * * In order to do this, the passed functors must have a single * argument of type auto. */ template<class Condition, class IfFunc, class ElseFunc> constexpr void hybridIf(const Condition& condition, IfFunc&& ifFunc, ElseFunc&& elseFunc) { Imp::hybridIf(condition, std::forward<IfFunc>(ifFunc), std::forward<ElseFunc>(elseFunc)); } /** * \brief Hybrid if * * This provides a hybridIf with empty else clause. */ template<class Condition, class IfFunc> constexpr void hybridIf(const Condition& condition, IfFunc&& ifFunc) { hybridIf(condition, std::forward<IfFunc>(ifFunc), [](auto&& i) {}); } // Everything in the next namespace block is just used to implement StaticSize, HasStaticSize, hybridSize namespace Imp { // As a last resort try if there's a static constexpr size() template<class T> constexpr auto staticSize(const T*, const PriorityTag<0>&) -> decltype(std::integral_constant<std::size_t,T::size()>()) { return {}; } // Try if tuple_size is implemented for class template<class T> constexpr auto staticSize(const T*, const PriorityTag<2>&) -> decltype(std::integral_constant<std::size_t,std::tuple_size<T>::value>()) { return {}; } // Try if tuple_size is implemented for class template<class T, int i> constexpr auto staticSize(const Dune::FieldVector<T, i>*, const PriorityTag<3>&) -> decltype(std::integral_constant<std::size_t,i>()) { return {}; } template<class T> constexpr std::false_type hasStaticSize(const T* t, const PriorityTag<0>& p) { return {}; } template<class T> constexpr auto hasStaticSize(const T* t, const PriorityTag<1>& p) -> decltype(staticSize(t ,PriorityTag<42>()), std::true_type()) { return {}; } } /** * \brief Check if type is a statically sized container * * \ingroup Utility * * Derives from std::true_type or std::false_type */ template<class T> struct HasStaticSize : public decltype(Imp::hasStaticSize((typename std::decay<T>::type*)(nullptr), PriorityTag<42>())) {}; /** * \brief Obtain size of statically sized container * * \ingroup Utility * * Derives from std::integral_constant<std::size_t, size> */ template<class T> struct StaticSize : public decltype(Imp::staticSize((typename std::decay<T>::type*)(nullptr), PriorityTag<42>())) {}; /** * \brief Hybrid size query * * \tparam T Type of container whose size is queried * * \param t Container whose size is queried * * This function is hybrid in the sense that it returns a statically * encoded size, i.e., an integral_constant if possible and the * dynamic result of the t.size() method otherwise. * * This is the static-size overload which returns the size i * as std::integral_constant<std::size_t, i>. */ template<class T, std::enable_if_t<HasStaticSize<T>::value, int> = 0> constexpr auto hybridSize(const T& t) { return Imp::staticSize((T*)(nullptr), PriorityTag<42>()); } /** * \brief Hybrid size query * * \tparam T Type of container whose size is queried * * \param t Container whose size is queried * * This function is hybrid in the sense that it returns a statically * encoded size, i.e., an integral_constant if possible and the * dynamic result of the *.size() method otherwise. * * This is the dynamic-size overload which returns the result * of t.size(). */ template<class T, std::enable_if_t<not HasStaticSize<T>::value, int> = 0> constexpr auto hybridSize(const T& t) { return t.size(); } /** * \brief Hybrid for loop over sparse range */ template<class... T, class F> void sparseRangeFor(const Dune::MultiTypeBlockVector<T...>& range, F&& f) { integralRangeFor<std::size_t>(Indices::_0, hybridSize(range), [&](auto&& i) { f(range[i], i); }); } /** * \brief Hybrid for loop over sparse range */ template<class Range, class F> void sparseRangeFor(Range&& range, F&& f) { auto it = range.begin(); auto end = range.end(); for(; it!=end; ++it) f(*it, it.index()); } } // namespace Solvers } // namespace Dune #endif// DUNE_SOLVERS_COMMON_FORLOOP_HH