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>
15#include <dune/common/integersequence.hh>
17
18
19
20namespace Dune {
21
25namespace Hybrid {
26
27namespace Impl {
28
29 // Try if std::tuple_size is implemented for class
30 template<class T>
31 constexpr auto size(const T&, const PriorityTag<2>&)
32 -> decltype(std::integral_constant<std::size_t,std::tuple_size<T>::value>())
33 {
34 return {};
35 }
36
37 // Try if there's a static constexpr size() method
38 template<class T>
39 constexpr auto size(const T&, const PriorityTag<1>&)
40 -> decltype(std::integral_constant<std::size_t,T::size()>())
41 {
42 return {};
43 }
44
45 // As a last resort try if there's a non-static size()
46 template<class T>
47 constexpr auto size(const T& t, const PriorityTag<0>&)
48 {
49 return t.size();
50 }
51
52} // namespace Impl
53
54
55
77template<class T>
78constexpr auto size(const T& t)
79{
80 return Impl::size(t, PriorityTag<42>());
81}
82
83
84
85namespace Impl {
86
87 template<class Container, class Index,
88 std::enable_if_t<IsTuple<std::decay_t<Container>>::value, int> = 0>
89 constexpr decltype(auto) elementAt(Container&& c, Index&&, PriorityTag<2>)
90 {
91 return std::get<std::decay_t<Index>::value>(c);
92 }
93
94 template<class T, T... t, class Index>
95 constexpr decltype(auto) elementAt(std::integer_sequence<T, t...> c, Index, PriorityTag<1>)
96 {
97 return Dune::get<Index::value>(c);
98 }
99
100 template<class Container, class Index>
101 constexpr decltype(auto) elementAt(Container&& c, Index&& i, PriorityTag<0>)
102 {
103 return c[i];
104 }
105
106} // namespace Impl
107
108
109
130template<class Container, class Index>
131constexpr decltype(auto) elementAt(Container&& c, Index&& i)
132{
133 return Impl::elementAt(std::forward<Container>(c), std::forward<Index>(i), PriorityTag<42>());
134}
135
136
137
138namespace Impl {
139
140 template<class Begin, class End,
141 std::enable_if_t<IsIntegralConstant<Begin>::value and IsIntegralConstant<End>::value, int> = 0>
142 constexpr auto integralRange(const Begin& /*begin*/, const End& /*end*/, const PriorityTag<1>&)
143 {
144 static_assert(Begin::value <= End::value, "You cannot create an integralRange where end<begin");
146 }
147
148 template<class Begin, class End>
149 constexpr auto integralRange(const Begin& begin, const End& end, const PriorityTag<0>&)
150 {
151 assert(begin<=end && "You cannot create an integralRange where end<begin");
152 return Dune::IntegralRange<End>(begin, end);
153 }
154
155} // namespace Impl
156
157
158
176template<class Begin, class End>
177constexpr auto integralRange(const Begin& begin, const End& end)
178{
179 return Impl::integralRange(begin, end, PriorityTag<42>());
180}
181
195template<class End>
196constexpr auto integralRange(const End& end)
197{
199}
200
201
202
203namespace Impl {
204
205 template<class T>
206 constexpr void evaluateFoldExpression(std::initializer_list<T>&&)
207 {}
208
209 template<class Range, class F, class Index, Index... i>
210 constexpr void forEachIndex(Range&& range, F&& f, std::integer_sequence<Index, i...>)
211 {
212 evaluateFoldExpression<int>({(f(Hybrid::elementAt(range, std::integral_constant<Index,i>())), 0)...});
213 }
214
215 template<class F, class Index, Index... i>
216 constexpr void forEach(std::integer_sequence<Index, i...> /*range*/, F&& f, PriorityTag<2>)
217 {
218 evaluateFoldExpression<int>({(f(std::integral_constant<Index,i>()), 0)...});
219 }
220
221
222 template<class Range, class F,
223 std::enable_if_t<IsIntegralConstant<decltype(Hybrid::size(std::declval<Range>()))>::value, int> = 0>
224 constexpr void forEach(Range&& range, F&& f, PriorityTag<1>)
225 {
226 auto size = Hybrid::size(range);
227 auto indices = std::make_index_sequence<size>();
228 (forEachIndex)(std::forward<Range>(range), std::forward<F>(f), indices);
229 }
230
231 template<class Range, class F>
232 constexpr void forEach(Range&& range, F&& f, PriorityTag<0>)
233 {
234 for(auto&& e : range)
235 f(e);
236 }
237
238} // namespace Impl
239
240
241
260template<class Range, class F>
261constexpr void forEach(Range&& range, F&& f)
262{
263 Impl::forEach(std::forward<Range>(range), std::forward<F>(f), PriorityTag<42>());
264}
265
266
267
283template<class Range, class T, class F>
284constexpr T accumulate(Range&& range, T value, F&& f)
285{
286 forEach(std::forward<Range>(range), [&](auto&& entry) {
287 value = f(value, entry);
288 });
289 return value;
290}
291
292
293
294namespace Impl {
295
296 struct Id {
297 template<class T>
298 constexpr T operator()(T&& x) const {
299 return std::forward<T>(x);
300 }
301 };
302
303 template<class IfFunc, class ElseFunc>
304 constexpr decltype(auto) ifElse(std::true_type, IfFunc&& ifFunc, ElseFunc&& /*elseFunc*/)
305 {
306 return ifFunc(Id{});
307 }
308
309 template<class IfFunc, class ElseFunc>
310 constexpr decltype(auto) ifElse(std::false_type, IfFunc&& /*ifFunc*/, ElseFunc&& elseFunc)
311 {
312 return elseFunc(Id{});
313 }
314
315 template<class IfFunc, class ElseFunc>
316 decltype(auto) ifElse(const bool& condition, IfFunc&& ifFunc, ElseFunc&& elseFunc)
317 {
318 if (condition)
319 return ifFunc(Id{});
320 else
321 return elseFunc(Id{});
322 }
323
324} // namespace Impl
325
326
327
348template<class Condition, class IfFunc, class ElseFunc>
349decltype(auto) ifElse(const Condition& condition, IfFunc&& ifFunc, ElseFunc&& elseFunc)
350{
351 return Impl::ifElse(condition, std::forward<IfFunc>(ifFunc), std::forward<ElseFunc>(elseFunc));
352}
353
361template<class Condition, class IfFunc>
362void ifElse(const Condition& condition, IfFunc&& ifFunc)
363{
364 ifElse(condition, std::forward<IfFunc>(ifFunc), [](auto&&) {});
365}
366
367
368
369namespace Impl {
370
371 struct Max {
372 template<class... Args>
373 constexpr decltype(auto) operator()(Args&&... args) const
374 {
375 using T = std::common_type_t<Args...>;
376 return std::max({T(args)...});
377 }
378 };
379
380 struct Min {
381 template<class... Args>
382 constexpr decltype(auto) operator()(Args&&... args) const
383 {
384 using T = std::common_type_t<Args...>;
385 return std::min({T(args)...});
386 }
387 };
388
389} // namespace Impl
390
391
422template<class Functor>
424
425 static_assert(std::is_default_constructible_v<Functor>,
426 "Operator in integral expressions shall be constexpr default constructible");
427
428 inline static constexpr Functor _functor = Functor{};
429
430public:
431
441 template<class... Args>
442 constexpr decltype(auto) operator()(const Args&... args) const
443 {
444 if constexpr (std::conjunction_v<IsCompileTimeConstant<Args>...>)
445 {
446 constexpr auto result = _functor(Args::value...);
447 // apply functor on integral constant arguments and return an integral constant of the result
448 // this is guaranteed to be evaluated at compile-time
449 return std::integral_constant<std::remove_cv_t<decltype(result)>,result>{};
450 } else {
451 // apply functor directly on arguments and return the result of the functor
452 // (integral constants are likely to be casted to underlying type)
453 // this not is guaranteed to be evaluated at compile-time although is possible if expression is constexpr
454 return _functor(args...);
455 }
456 }
457};
458
463template<class Functor>
464constexpr HybridFunctor<Functor> hybridFunctor(const Functor&)
465{
466 return {};
467}
468
489inline constexpr auto max = hybridFunctor(Impl::Max{});
490
511inline constexpr auto min = hybridFunctor(Impl::Min{});
512
533inline constexpr auto plus = hybridFunctor(std::plus<>{});
534
555inline constexpr auto minus = hybridFunctor(std::minus<>{});
556
577inline constexpr auto equal_to = hybridFunctor(std::equal_to<>{});
578
579
580namespace Impl {
581
582 // This overload is selected if the passed value is already a compile time constant.
583 template<class Result, class T, T t0, T... tt, class ValueType, ValueType value, class Branches, class ElseBranch>
584 constexpr Result switchCases(std::integer_sequence<T, t0, tt...>, const std::integral_constant<ValueType, value>& /*value*/, Branches&& branches, ElseBranch&& elseBranch)
585 {
586 // In case we pass a value known at compile time, we no longer have to do
587 // a dynamic to static dispatch via recursion. The only thing that's left
588 // is to check if the value is contained in the passed range.
589 // If this is true, we have to pass it to the branches callback
590 // as an appropriate integral_constant type. Otherwise we have to
591 // execute the else callback.
592 if constexpr (((t0 == value) || ... || (tt == value)))
593 return branches(std::integral_constant<T, value>{});
594 else
595 return elseBranch();
596 }
597
598 // This overload is selected if the passed value is dynamic.
599 template<class Result, class T, class Value, class Branches, class ElseBranch>
600 constexpr Result switchCases(std::integer_sequence<T>, const Value& /*value*/, Branches&& /*branches*/, ElseBranch&& elseBranch)
601 {
602 return elseBranch();
603 }
604
605 template<class Result, class T, T t0, T... tt, class Value, class Branches, class ElseBranch>
606 constexpr Result switchCases(std::integer_sequence<T, t0, tt...>, const Value& value, Branches&& branches, ElseBranch&& elseBranch)
607 {
608 if (t0 == value)
609 return branches(std::integral_constant<T, t0>());
610 else
611 return Impl::switchCases<Result>(std::integer_sequence<T, tt...>(), value, branches, elseBranch);
612 }
613
614 // This overload is selected if the range of cases is an IntegralRange
615 template <class Result, class T, class Value, class Branches, class ElseBranch>
616 constexpr Result switchCases(IntegralRange<T> range, const Value& value, Branches&& branches, ElseBranch&& elseBranch)
617 {
618 return range.contains(value) ? branches(T(value)) : elseBranch();
619 }
620
621 // This overload is selected if the range of cases is a StaticIntegralRange
622 template <class Result, class T, T to, T from, class Value, class Branches, class ElseBranch>
623 constexpr Result switchCases(StaticIntegralRange<T, to, from> range, const Value& value, Branches&& branches, ElseBranch&& elseBranch)
624 {
625 using seq = typename decltype(range)::integer_sequence;
626 return Impl::switchCases<Result>(seq{}, value, branches, elseBranch);
627 }
628
629} // namespace Impl
630
631
632
660template<class Cases, class Value, class Branches, class ElseBranch>
661constexpr decltype(auto) switchCases(const Cases& cases, const Value& value, Branches&& branches, ElseBranch&& elseBranch)
662{
663 return Impl::switchCases<decltype(elseBranch())>(cases, value, std::forward<Branches>(branches), std::forward<ElseBranch>(elseBranch));
664}
665
685template<class Cases, class Value, class Branches>
686constexpr void switchCases(const Cases& cases, const Value& value, Branches&& branches)
687{
688 Impl::switchCases<void>(cases, value, std::forward<Branches>(branches),
689 []{ assert(false && "value not found in range"); });
690}
691
710template <class T, class Value, class Branches>
711constexpr void switchCases(IntegralRange<T> range, const Value& value, Branches&& branches)
712{
713 assert(range.contains(value) && "value not found in range");
714 branches(T(value));
715}
716
717} // namespace Hybrid
718} // namespace Dune
719
720
721#endif // #ifndef DUNE_COMMON_HYBRIDUTILITIES_HH
Adapter of a hybrid functor that maintains results hybrid.
Definition: hybridutilities.hh:423
dynamic integer range for use in range-based for loops
Definition: rangeutilities.hh:171
static integer range for use in range-based for loops
Definition: rangeutilities.hh:224
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
void ifElse(const Condition &condition, IfFunc &&ifFunc)
A conditional expression.
Definition: hybridutilities.hh:362
constexpr auto size(const T &t)
Size query.
Definition: hybridutilities.hh:78
constexpr auto integralRange(const End &end)
Create an integral range starting from 0.
Definition: hybridutilities.hh:196
constexpr auto minus
Function object for performing subtraction.
Definition: hybridutilities.hh:555
constexpr void forEach(Range &&range, F &&f)
Range based for loop.
Definition: hybridutilities.hh:261
constexpr auto equal_to
Function object for performing equality comparison.
Definition: hybridutilities.hh:577
constexpr decltype(auto) switchCases(const Cases &cases, const Value &value, Branches &&branches, ElseBranch &&elseBranch)
Switch statement.
Definition: hybridutilities.hh:661
constexpr auto max
Function object that returns the greater of the given values.
Definition: hybridutilities.hh:489
constexpr auto plus
Function object for performing addition.
Definition: hybridutilities.hh:533
constexpr auto min
Function object that returns the smaller of the given values.
Definition: hybridutilities.hh:511
decltype(auto) ifElse(const Condition &condition, IfFunc &&ifFunc, ElseFunc &&elseFunc)
A conditional expression.
Definition: hybridutilities.hh:349
constexpr auto integralRange(const Begin &begin, const End &end)
Create an integral range.
Definition: hybridutilities.hh:177
constexpr decltype(auto) elementAt(Container &&c, Index &&i)
Get element at given position from container.
Definition: hybridutilities.hh:131
constexpr T accumulate(Range &&range, T value, F &&f)
Accumulate values.
Definition: hybridutilities.hh:284
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
constexpr HybridFunctor< Functor > hybridFunctor(const Functor &)
Returns an HybridFunctor adaptor.
Definition: hybridutilities.hh:464
Dune namespace
Definition: alignedallocator.hh:13
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:87
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 & Uni Heidelberg  |  generated with Hugo v0.111.3 (Jan 8, 23:33, 2026)