Loading...
Searching...
No Matches
hybridutilities.hh
Go to the documentation of this file.
// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
// vi: set et ts=4 sw=2 sts=2:
// SPDX-FileCopyrightInfo: Copyright © DUNE Project contributors, see file LICENSE.md in module root
// SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception
#ifndef DUNE_COMMON_HYBRIDUTILITIES_HH
#define DUNE_COMMON_HYBRIDUTILITIES_HH
#include <tuple>
#include <utility>
#include <dune/common/typetraits.hh>
#include <dune/common/typeutilities.hh>
#include <dune/common/fvector.hh>
#include <dune/common/indices.hh>
#include <dune/common/integersequence.hh>
#include <dune/common/rangeutilities.hh>
namespace Dune {
namespace Hybrid {
namespace Impl {
// Try if std::tuple_size is implemented for class
template<class T>
constexpr auto size(const T&, const PriorityTag<2>&)
-> decltype(std::integral_constant<std::size_t,std::tuple_size<T>::value>())
{
return {};
}
// Try if there's a static constexpr size() method
template<class T>
constexpr auto size(const T&, const PriorityTag<1>&)
-> decltype(std::integral_constant<std::size_t,T::size()>())
{
return {};
}
// As a last resort try if there's a non-static size()
template<class T>
constexpr auto size(const T& t, const PriorityTag<0>&)
{
return t.size();
}
} // namespace Impl
template<class T>
constexpr auto size(const T& t)
{
return Impl::size(t, PriorityTag<42>());
}
namespace Impl {
template<class Container, class Index,
std::enable_if_t<IsTuple<std::decay_t<Container>>::value, int> = 0>
constexpr decltype(auto) elementAt(Container&& c, Index&&, PriorityTag<2>)
{
return std::get<std::decay_t<Index>::value>(c);
}
template<class T, T... t, class Index>
constexpr decltype(auto) elementAt(std::integer_sequence<T, t...> c, Index, PriorityTag<1>)
{
return Dune::get<Index::value>(c);
}
template<class Container, class Index>
constexpr decltype(auto) elementAt(Container&& c, Index&& i, PriorityTag<0>)
{
return c[i];
}
} // namespace Impl
template<class Container, class Index>
constexpr decltype(auto) elementAt(Container&& c, Index&& i)
{
return Impl::elementAt(std::forward<Container>(c), std::forward<Index>(i), PriorityTag<42>());
}
namespace Impl {
template<class Begin, class End,
std::enable_if_t<IsIntegralConstant<Begin>::value and IsIntegralConstant<End>::value, int> = 0>
constexpr auto integralRange(const Begin& /*begin*/, const End& /*end*/, const PriorityTag<1>&)
{
static_assert(Begin::value <= End::value, "You cannot create an integralRange where end<begin");
return Dune::StaticIntegralRange<std::size_t, End::value, Begin::value>();
}
template<class Begin, class End>
constexpr auto integralRange(const Begin& begin, const End& end, const PriorityTag<0>&)
{
assert(begin<=end && "You cannot create an integralRange where end<begin");
return Dune::IntegralRange<End>(begin, end);
}
} // namespace Impl
template<class Begin, class End>
constexpr auto integralRange(const Begin& begin, const End& end)
{
return Impl::integralRange(begin, end, PriorityTag<42>());
}
template<class End>
constexpr auto integralRange(const End& end)
{
return Impl::integralRange(Dune::Indices::_0, end, PriorityTag<42>());
}
namespace Impl {
template<class T>
constexpr void evaluateFoldExpression(std::initializer_list<T>&&)
{}
template<class Range, class F, class Index, Index... i>
constexpr void forEachIndex(Range&& range, F&& f, std::integer_sequence<Index, i...>)
{
evaluateFoldExpression<int>({(f(Hybrid::elementAt(range, std::integral_constant<Index,i>())), 0)...});
}
template<class F, class Index, Index... i>
constexpr void forEach(std::integer_sequence<Index, i...> /*range*/, F&& f, PriorityTag<2>)
{
evaluateFoldExpression<int>({(f(std::integral_constant<Index,i>()), 0)...});
}
template<class Range, class F,
std::enable_if_t<IsIntegralConstant<decltype(Hybrid::size(std::declval<Range>()))>::value, int> = 0>
constexpr void forEach(Range&& range, F&& f, PriorityTag<1>)
{
auto size = Hybrid::size(range);
auto indices = std::make_index_sequence<size>();
(forEachIndex)(std::forward<Range>(range), std::forward<F>(f), indices);
}
template<class Range, class F>
constexpr void forEach(Range&& range, F&& f, PriorityTag<0>)
{
for(auto&& e : range)
f(e);
}
} // namespace Impl
template<class Range, class F>
constexpr void forEach(Range&& range, F&& f)
{
Impl::forEach(std::forward<Range>(range), std::forward<F>(f), PriorityTag<42>());
}
template<class Range, class T, class F>
constexpr T accumulate(Range&& range, T value, F&& f)
{
forEach(std::forward<Range>(range), [&](auto&& entry) {
value = f(value, entry);
});
return value;
}
namespace Impl {
struct Id {
template<class T>
constexpr T operator()(T&& x) const {
return std::forward<T>(x);
}
};
template<class IfFunc, class ElseFunc>
constexpr decltype(auto) ifElse(std::true_type, IfFunc&& ifFunc, ElseFunc&& /*elseFunc*/)
{
return ifFunc(Id{});
}
template<class IfFunc, class ElseFunc>
constexpr decltype(auto) ifElse(std::false_type, IfFunc&& /*ifFunc*/, ElseFunc&& elseFunc)
{
return elseFunc(Id{});
}
template<class IfFunc, class ElseFunc>
decltype(auto) ifElse(const bool& condition, IfFunc&& ifFunc, ElseFunc&& elseFunc)
{
if (condition)
return ifFunc(Id{});
else
return elseFunc(Id{});
}
} // namespace Impl
template<class Condition, class IfFunc, class ElseFunc>
decltype(auto) ifElse(const Condition& condition, IfFunc&& ifFunc, ElseFunc&& elseFunc)
{
return Impl::ifElse(condition, std::forward<IfFunc>(ifFunc), std::forward<ElseFunc>(elseFunc));
}
template<class Condition, class IfFunc>
void ifElse(const Condition& condition, IfFunc&& ifFunc)
{
ifElse(condition, std::forward<IfFunc>(ifFunc), [](auto&&) {});
}
namespace Impl {
struct Max {
template<class... Args>
constexpr decltype(auto) operator()(Args&&... args) const
{
using T = std::common_type_t<Args...>;
return std::max({T(args)...});
}
};
struct Min {
template<class... Args>
constexpr decltype(auto) operator()(Args&&... args) const
{
using T = std::common_type_t<Args...>;
return std::min({T(args)...});
}
};
} // namespace Impl
template<class Functor>
class HybridFunctor {
static_assert(std::is_default_constructible_v<Functor>,
"Operator in integral expressions shall be constexpr default constructible");
inline static constexpr Functor _functor = Functor{};
public:
template<class... Args>
constexpr decltype(auto) operator()(const Args&... args) const
{
if constexpr (std::conjunction_v<IsCompileTimeConstant<Args>...>)
{
constexpr auto result = _functor(Args::value...);
// apply functor on integral constant arguments and return an integral constant of the result
// this is guaranteed to be evaluated at compile-time
return std::integral_constant<std::remove_cv_t<decltype(result)>,result>{};
} else {
// apply functor directly on arguments and return the result of the functor
// (integral constants are likely to be casted to underlying type)
// this not is guaranteed to be evaluated at compile-time although is possible if expression is constexpr
return _functor(args...);
}
}
};
template<class Functor>
constexpr HybridFunctor<Functor> hybridFunctor(const Functor&)
{
return {};
}
inline constexpr auto max = hybridFunctor(Impl::Max{});
inline constexpr auto min = hybridFunctor(Impl::Min{});
inline constexpr auto plus = hybridFunctor(std::plus<>{});
inline constexpr auto minus = hybridFunctor(std::minus<>{});
inline constexpr auto equal_to = hybridFunctor(std::equal_to<>{});
namespace Impl {
// This overload is selected if the passed value is already a compile time constant.
template<class Result, class T, T t0, T... tt, class ValueType, ValueType value, class Branches, class ElseBranch>
constexpr Result switchCases(std::integer_sequence<T, t0, tt...>, const std::integral_constant<ValueType, value>& /*value*/, Branches&& branches, ElseBranch&& elseBranch)
{
// In case we pass a value known at compile time, we no longer have to do
// a dynamic to static dispatch via recursion. The only thing that's left
// is to check if the value is contained in the passed range.
// If this is true, we have to pass it to the branches callback
// as an appropriate integral_constant type. Otherwise we have to
// execute the else callback.
if constexpr (((t0 == value) || ... || (tt == value)))
return branches(std::integral_constant<T, value>{});
else
return elseBranch();
}
// This overload is selected if the passed value is dynamic.
template<class Result, class T, class Value, class Branches, class ElseBranch>
constexpr Result switchCases(std::integer_sequence<T>, const Value& /*value*/, Branches&& /*branches*/, ElseBranch&& elseBranch)
{
return elseBranch();
}
template<class Result, class T, T t0, T... tt, class Value, class Branches, class ElseBranch>
constexpr Result switchCases(std::integer_sequence<T, t0, tt...>, const Value& value, Branches&& branches, ElseBranch&& elseBranch)
{
if (t0 == value)
return branches(std::integral_constant<T, t0>());
else
return Impl::switchCases<Result>(std::integer_sequence<T, tt...>(), value, branches, elseBranch);
}
// This overload is selected if the range of cases is an IntegralRange
template <class Result, class T, class Value, class Branches, class ElseBranch>
constexpr Result switchCases(IntegralRange<T> range, const Value& value, Branches&& branches, ElseBranch&& elseBranch)
{
return range.contains(value) ? branches(T(value)) : elseBranch();
}
// This overload is selected if the range of cases is a StaticIntegralRange
template <class Result, class T, T to, T from, class Value, class Branches, class ElseBranch>
constexpr Result switchCases(StaticIntegralRange<T, to, from> range, const Value& value, Branches&& branches, ElseBranch&& elseBranch)
{
using seq = typename decltype(range)::integer_sequence;
return Impl::switchCases<Result>(seq{}, value, branches, elseBranch);
}
} // namespace Impl
template<class Cases, class Value, class Branches, class ElseBranch>
constexpr decltype(auto) switchCases(const Cases& cases, const Value& value, Branches&& branches, ElseBranch&& elseBranch)
{
return Impl::switchCases<decltype(elseBranch())>(cases, value, std::forward<Branches>(branches), std::forward<ElseBranch>(elseBranch));
}
template<class Cases, class Value, class Branches>
constexpr void switchCases(const Cases& cases, const Value& value, Branches&& branches)
{
Impl::switchCases<void>(cases, value, std::forward<Branches>(branches),
[]{ assert(false && "value not found in range"); });
}
template <class T, class Value, class Branches>
constexpr void switchCases(IntegralRange<T> range, const Value& value, Branches&& branches)
{
assert(range.contains(value) && "value not found in range");
branches(T(value));
}
} // namespace Hybrid
} // namespace Dune
#endif // #ifndef DUNE_COMMON_HYBRIDUTILITIES_HH
typetraits.hh
Traits for type conversions and type information.
typeutilities.hh
Utilities for type computations, constraining overloads, ...
indices.hh
rangeutilities.hh
Utilities for reduction like operations on ranges.
fvector.hh
Implements a vector constructed from a given type representing a field and a compile-time given size.
integersequence.hh
Dune::Indices::_0
constexpr index_constant< 0 > _0
Compile time index with value 0.
Definition indices.hh:52
Dune::range
static constexpr IntegralRange< std::decay_t< T > > range(T &&from, U &&to) noexcept
free standing function for setting up a range based for loop over an integer range for (auto i: range...
Definition rangeutilities.hh:288
Dune::Hybrid::size
constexpr auto size(const T &t)
Size query.
Definition hybridutilities.hh:78
Dune::Hybrid::minus
constexpr auto minus
Function object for performing subtraction.
Definition hybridutilities.hh:555
Dune::Hybrid::forEach
constexpr void forEach(Range &&range, F &&f)
Range based for loop.
Definition hybridutilities.hh:261
Dune::Hybrid::equal_to
constexpr auto equal_to
Function object for performing equality comparison.
Definition hybridutilities.hh:577
Dune::Hybrid::switchCases
constexpr decltype(auto) switchCases(const Cases &cases, const Value &value, Branches &&branches, ElseBranch &&elseBranch)
Switch statement.
Definition hybridutilities.hh:661
Dune::Hybrid::max
constexpr auto max
Function object that returns the greater of the given values.
Definition hybridutilities.hh:489
Dune::Hybrid::plus
constexpr auto plus
Function object for performing addition.
Definition hybridutilities.hh:533
Dune::Hybrid::min
constexpr auto min
Function object that returns the smaller of the given values.
Definition hybridutilities.hh:511
Dune::Hybrid::ifElse
decltype(auto) ifElse(const Condition &condition, IfFunc &&ifFunc, ElseFunc &&elseFunc)
A conditional expression.
Definition hybridutilities.hh:349
Dune::Hybrid::integralRange
constexpr auto integralRange(const Begin &begin, const End &end)
Create an integral range.
Definition hybridutilities.hh:177
Dune::Hybrid::elementAt
constexpr decltype(auto) elementAt(Container &&c, Index &&i)
Get element at given position from container.
Definition hybridutilities.hh:131
Dune::Hybrid::accumulate
constexpr T accumulate(Range &&range, T value, F &&f)
Accumulate values.
Definition hybridutilities.hh:284
Dune
Dune namespace
Definition alignedallocator.hh:13
Dune::get
constexpr auto get(std::integer_sequence< T, II... >, std::integral_constant< std::size_t, pos >={})
Return the entry at position pos of the given sequence.
Definition integersequence.hh:22
Dune::Hybrid
Namespace with loops, conditionals and other constructs that can handle both compile-time and run-tim...
Definition hybridutilities.hh:25
Dune::Hybrid::hybridFunctor
constexpr HybridFunctor< Functor > hybridFunctor(const Functor &)
Returns an HybridFunctor adaptor.
Definition hybridutilities.hh:464
Dune::Max
Definition binaryfunctions.hh:34
Dune::Hybrid::HybridFunctor
Adapter of a hybrid functor that maintains results hybrid.
Definition hybridutilities.hh:423
Dune::IntegralRange
dynamic integer range for use in range-based for loops
Definition rangeutilities.hh:171
Dune::StaticIntegralRange
static integer range for use in range-based for loops
Definition rangeutilities.hh:224
Dune::IsIntegralConstant
Check if T is an std::integral_constant<I, i>.
Definition typetraits.hh:384
Dune::PriorityTag
Helper class for tagging priorities.
Definition typeutilities.hh:73