Dune Core Modules (unstable)

hybridutilities.hh
1// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2// vi: set et ts=4 sw=2 sts=2:
3// SPDX-FileCopyrightInfo: Copyright © DUNE Project contributors, see file LICENSE.md in module root
4// SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception
5#ifndef DUNE_COMMON_HYBRIDUTILITIES_HH
6#define DUNE_COMMON_HYBRIDUTILITIES_HH
7
8#include <tuple>
9#include <utility>
10
14#include <dune/common/indices.hh>
16
17
18
19namespace Dune {
20namespace Hybrid {
21
22namespace Impl {
23
24 // Try if std::tuple_size is implemented for class
25 template<class T>
26 constexpr auto size(const T&, const PriorityTag<2>&)
27 -> decltype(std::integral_constant<std::size_t,std::tuple_size<T>::value>())
28 {
29 return {};
30 }
31
32 // Try if there's a static constexpr size() method
33 template<class T>
34 constexpr auto size(const T&, const PriorityTag<1>&)
35 -> decltype(std::integral_constant<std::size_t,T::size()>())
36 {
37 return {};
38 }
39
40 // As a last resort try if there's a non-static size()
41 template<class T>
42 constexpr auto size(const T& t, const PriorityTag<0>&)
43 {
44 return t.size();
45 }
46
47} // namespace Impl
48
49
50
72template<class T>
73constexpr auto size(const T& t)
74{
75 return Impl::size(t, PriorityTag<42>());
76}
77
78
79
80namespace Impl {
81
82 template<class Container, class Index,
83 std::enable_if_t<IsTuple<std::decay_t<Container>>::value, int> = 0>
84 constexpr decltype(auto) elementAt(Container&& c, Index&&, PriorityTag<2>)
85 {
86 return std::get<std::decay_t<Index>::value>(c);
87 }
88
89 template<class T, T... t, class Index>
90 constexpr decltype(auto) elementAt(std::integer_sequence<T, t...> c, Index, PriorityTag<1>)
91 {
92 return Dune::integerSequenceEntry(c, std::integral_constant<std::size_t, Index::value>());
93 }
94
95 template<class Container, class Index>
96 constexpr decltype(auto) elementAt(Container&& c, Index&& i, PriorityTag<0>)
97 {
98 return c[i];
99 }
100
101} // namespace Impl
102
103
104
125template<class Container, class Index>
126constexpr decltype(auto) elementAt(Container&& c, Index&& i)
127{
128 return Impl::elementAt(std::forward<Container>(c), std::forward<Index>(i), PriorityTag<42>());
129}
130
131
132
133namespace Impl {
134
135 template<class Begin, class End,
136 std::enable_if_t<IsIntegralConstant<Begin>::value and IsIntegralConstant<End>::value, int> = 0>
137 constexpr auto integralRange(const Begin& /*begin*/, const End& /*end*/, const PriorityTag<1>&)
138 {
139 static_assert(Begin::value <= End::value, "You cannot create an integralRange where end<begin");
141 }
142
143 template<class Begin, class End>
144 constexpr auto integralRange(const Begin& begin, const End& end, const PriorityTag<0>&)
145 {
146 assert(begin<=end && "You cannot create an integralRange where end<begin");
147 return Dune::IntegralRange<End>(begin, end);
148 }
149
150} // namespace Impl
151
152
153
171template<class Begin, class End>
172constexpr auto integralRange(const Begin& begin, const End& end)
173{
174 return Impl::integralRange(begin, end, PriorityTag<42>());
175}
176
190template<class End>
191constexpr auto integralRange(const End& end)
192{
194}
195
196
197
198namespace Impl {
199
200 template<class T>
201 constexpr void evaluateFoldExpression(std::initializer_list<T>&&)
202 {}
203
204 template<class Range, class F, class Index, Index... i>
205 constexpr void forEachIndex(Range&& range, F&& f, std::integer_sequence<Index, i...>)
206 {
207 evaluateFoldExpression<int>({(f(Hybrid::elementAt(range, std::integral_constant<Index,i>())), 0)...});
208 }
209
210 template<class F, class Index, Index... i>
211 constexpr void forEach(std::integer_sequence<Index, i...> /*range*/, F&& f, PriorityTag<2>)
212 {
213 evaluateFoldExpression<int>({(f(std::integral_constant<Index,i>()), 0)...});
214 }
215
216
217 template<class Range, class F,
218 std::enable_if_t<IsIntegralConstant<decltype(Hybrid::size(std::declval<Range>()))>::value, int> = 0>
219 constexpr void forEach(Range&& range, F&& f, PriorityTag<1>)
220 {
221 auto size = Hybrid::size(range);
222 auto indices = std::make_index_sequence<size>();
223 (forEachIndex)(std::forward<Range>(range), std::forward<F>(f), indices);
224 }
225
226 template<class Range, class F>
227 constexpr void forEach(Range&& range, F&& f, PriorityTag<0>)
228 {
229 for(auto&& e : range)
230 f(e);
231 }
232
233} // namespace Impl
234
235
236
255template<class Range, class F>
256constexpr void forEach(Range&& range, F&& f)
257{
258 Impl::forEach(std::forward<Range>(range), std::forward<F>(f), PriorityTag<42>());
259}
260
261
262
278template<class Range, class T, class F>
279constexpr T accumulate(Range&& range, T value, F&& f)
280{
281 forEach(std::forward<Range>(range), [&](auto&& entry) {
282 value = f(value, entry);
283 });
284 return value;
285}
286
287
288
289namespace Impl {
290
291 struct Id {
292 template<class T>
293 constexpr T operator()(T&& x) const {
294 return std::forward<T>(x);
295 }
296 };
297
298 template<class IfFunc, class ElseFunc>
299 constexpr decltype(auto) ifElse(std::true_type, IfFunc&& ifFunc, ElseFunc&& /*elseFunc*/)
300 {
301 return ifFunc(Id{});
302 }
303
304 template<class IfFunc, class ElseFunc>
305 constexpr decltype(auto) ifElse(std::false_type, IfFunc&& /*ifFunc*/, ElseFunc&& elseFunc)
306 {
307 return elseFunc(Id{});
308 }
309
310 template<class IfFunc, class ElseFunc>
311 decltype(auto) ifElse(const bool& condition, IfFunc&& ifFunc, ElseFunc&& elseFunc)
312 {
313 if (condition)
314 return ifFunc(Id{});
315 else
316 return elseFunc(Id{});
317 }
318
319} // namespace Impl
320
321
322
343template<class Condition, class IfFunc, class ElseFunc>
344decltype(auto) ifElse(const Condition& condition, IfFunc&& ifFunc, ElseFunc&& elseFunc)
345{
346 return Impl::ifElse(condition, std::forward<IfFunc>(ifFunc), std::forward<ElseFunc>(elseFunc));
347}
348
356template<class Condition, class IfFunc>
357void ifElse(const Condition& condition, IfFunc&& ifFunc)
358{
359 ifElse(condition, std::forward<IfFunc>(ifFunc), [](auto&&) {});
360}
361
362
363
364namespace Impl {
365
366 struct Max {
367 template<class... Args>
368 constexpr decltype(auto) operator()(Args&&... args) const
369 {
370 using T = std::common_type_t<Args...>;
371 return std::max({T(args)...});
372 }
373 };
374
375 struct Min {
376 template<class... Args>
377 constexpr decltype(auto) operator()(Args&&... args) const
378 {
379 using T = std::common_type_t<Args...>;
380 return std::min({T(args)...});
381 }
382 };
383
384} // namespace Impl
385
386
417template<class Functor>
419
420 static_assert(std::is_default_constructible_v<Functor>,
421 "Operator in integral expressions shall be constexpr default constructible");
422
423 inline static constexpr Functor _functor = Functor{};
424
425public:
426
436 template<class... Args>
437 constexpr decltype(auto) operator()(const Args&... args) const
438 {
439 if constexpr (std::conjunction_v<IsCompileTimeConstant<Args>...>)
440 {
441 constexpr auto result = _functor(Args::value...);
442 // apply functor on integral constant arguments and return an integral constant of the result
443 // this is guaranteed to be evaluated at compile-time
444 return std::integral_constant<std::remove_cv_t<decltype(result)>,result>{};
445 } else {
446 // apply functor directly on arguments and return the result of the functor
447 // (integral constants are likely to be casted to underlying type)
448 // this not is guaranteed to be evaluated at compile-time although is possible if expression is constexpr
449 return _functor(args...);
450 }
451 }
452};
453
458template<class Functor>
459constexpr HybridFunctor<Functor> hybridFunctor(const Functor&)
460{
461 return {};
462}
463
484inline constexpr auto max = hybridFunctor(Impl::Max{});
485
506inline constexpr auto min = hybridFunctor(Impl::Min{});
507
528inline constexpr auto plus = hybridFunctor(std::plus<>{});
529
550inline constexpr auto minus = hybridFunctor(std::minus<>{});
551
572inline constexpr auto equal_to = hybridFunctor(std::equal_to<>{});
573
574
586template<class T1, class T2>
587[[deprecated("Use Hybrid::equal_to instead!")]] constexpr auto equals(T1&& t1, T2&& t2){
588 return equal_to(std::forward<T1>(t1), std::forward<T2>(t2));
589}
590
591
592namespace Impl {
593
594 // This overload is selected if the passed value is already a compile time constant.
595 template<class Result, class T, T t0, T... tt, class ValueType, ValueType value, class Branches, class ElseBranch>
596 constexpr Result switchCases(std::integer_sequence<T, t0, tt...>, const std::integral_constant<ValueType, value>& /*value*/, Branches&& branches, ElseBranch&& elseBranch)
597 {
598 // In case we pass a value known at compile time, we no longer have to do
599 // a dynamic to static dispatch via recursion. The only thing that's left
600 // is to check if the value is contained in the passed range.
601 // If this is true, we have to pass it to the branches callback
602 // as an appropriate integral_constant type. Otherwise we have to
603 // execute the else callback.
604 if constexpr (((t0 == value) || ... || (tt == value)))
605 return branches(std::integral_constant<T, value>{});
606 else
607 return elseBranch();
608 }
609
610 // This overload is selected if the passed value is dynamic.
611 template<class Result, class T, class Value, class Branches, class ElseBranch>
612 constexpr Result switchCases(std::integer_sequence<T>, const Value& /*value*/, Branches&& /*branches*/, ElseBranch&& elseBranch)
613 {
614 return elseBranch();
615 }
616
617 template<class Result, class T, T t0, T... tt, class Value, class Branches, class ElseBranch>
618 constexpr Result switchCases(std::integer_sequence<T, t0, tt...>, const Value& value, Branches&& branches, ElseBranch&& elseBranch)
619 {
620 if (t0 == value)
621 return branches(std::integral_constant<T, t0>());
622 else
623 return Impl::switchCases<Result>(std::integer_sequence<T, tt...>(), value, branches, elseBranch);
624 }
625
626 // This overload is selected if the range of cases is an IntegralRange
627 template <class Result, class T, class Value, class Branches, class ElseBranch>
628 constexpr Result switchCases(IntegralRange<T> range, const Value& value, Branches&& branches, ElseBranch&& elseBranch)
629 {
630 return range.contains(value) ? branches(T(value)) : elseBranch();
631 }
632
633 // This overload is selected if the range of cases is a StaticIntegralRange
634 template <class Result, class T, T to, T from, class Value, class Branches, class ElseBranch>
635 constexpr Result switchCases(StaticIntegralRange<T, to, from> range, const Value& value, Branches&& branches, ElseBranch&& elseBranch)
636 {
637 using seq = typename decltype(range)::integer_sequence;
638 return Impl::switchCases<Result>(seq{}, value, branches, elseBranch);
639 }
640
641} // namespace Impl
642
643
644
672template<class Cases, class Value, class Branches, class ElseBranch>
673constexpr decltype(auto) switchCases(const Cases& cases, const Value& value, Branches&& branches, ElseBranch&& elseBranch)
674{
675 return Impl::switchCases<decltype(elseBranch())>(cases, value, std::forward<Branches>(branches), std::forward<ElseBranch>(elseBranch));
676}
677
697template<class Cases, class Value, class Branches>
698constexpr void switchCases(const Cases& cases, const Value& value, Branches&& branches)
699{
700 Impl::switchCases<void>(cases, value, std::forward<Branches>(branches),
701 []{ assert(false && "value not found in range"); });
702}
703
722template <class T, class Value, class Branches>
723constexpr void switchCases(IntegralRange<T> range, const Value& value, Branches&& branches)
724{
725 assert(range.contains(value) && "value not found in range");
726 branches(T(value));
727}
728
729} // namespace Hybrid
730} // namespace Dune
731
732
733#endif // #ifndef DUNE_COMMON_HYBRIDUTILITIES_HH
Adapter of a hybrid functor that maintains results hybrid.
Definition: hybridutilities.hh:418
dynamic integer range for use in range-based for loops
Definition: rangeutilities.hh:177
constexpr bool contains(value_type index) const noexcept
check whether given index is within range [from, to)
Definition: rangeutilities.hh:207
static integer range for use in range-based for loops
Definition: rangeutilities.hh:230
Implements a vector constructed from a given type representing a field and a compile-time given size.
constexpr index_constant< 0 > _0
Compile time index with value 0.
Definition: indices.hh:52
constexpr auto integerSequenceEntry(std::integer_sequence< T, t... >, std::integral_constant< std::size_t, index > i)
Get entry of std::integer_sequence.
Definition: typetraits.hh:492
void ifElse(const Condition &condition, IfFunc &&ifFunc)
A conditional expression.
Definition: hybridutilities.hh:357
constexpr auto size(const T &t)
Size query.
Definition: hybridutilities.hh:73
constexpr auto integralRange(const End &end)
Create an integral range starting from 0.
Definition: hybridutilities.hh:191
constexpr auto minus
Function object for performing subtraction.
Definition: hybridutilities.hh:550
constexpr auto equals(T1 &&t1, T2 &&t2)
Equality comparison.
Definition: hybridutilities.hh:587
constexpr void forEach(Range &&range, F &&f)
Range based for loop.
Definition: hybridutilities.hh:256
constexpr auto equal_to
Function object for performing equality comparison.
Definition: hybridutilities.hh:572
constexpr auto max
Function object that returns the greater of the given values.
Definition: hybridutilities.hh:484
constexpr auto plus
Function object for performing addition.
Definition: hybridutilities.hh:528
constexpr auto min
Function object that returns the smaller of the given values.
Definition: hybridutilities.hh:506
constexpr decltype(auto) elementAt(Container &&c, Index &&i)
Get element at given position from container.
Definition: hybridutilities.hh:126
constexpr T accumulate(Range &&range, T value, F &&f)
Accumulate values.
Definition: hybridutilities.hh:279
constexpr void switchCases(IntegralRange< T > range, const Value &value, Branches &&branches)
Switch statement.
Definition: hybridutilities.hh:723
Dune namespace.
Definition: alignedallocator.hh:13
constexpr std::integral_constant< std::size_t, sizeof...(II)> size(std::integer_sequence< T, II... >)
Return the size of the sequence.
Definition: integersequence.hh:75
Utilities for reduction like operations on ranges.
Check if T is an std::integral_constant<I, i>
Definition: typetraits.hh:384
Helper class for tagging priorities.
Definition: typeutilities.hh:73
Traits for type conversions and type information.
Utilities for type computations, constraining overloads, ...
Creative Commons License   |  Legal Statements / Impressum  |  Hosted by TU Dresden  |  generated with Hugo v0.111.3 (Dec 21, 23:30, 2024)