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 
13 #include <dune/common/fvector.hh>
14 #include <dune/common/indices.hh>
16 
17 
18 
19 namespace Dune {
20 namespace Hybrid {
21 
22 namespace 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 
72 template<class T>
73 constexpr auto size(const T& t)
74 {
75  return Impl::size(t, PriorityTag<42>());
76 }
77 
78 
79 
80 namespace 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 
125 template<class Container, class Index>
126 constexpr 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 
133 namespace 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 
171 template<class Begin, class End>
172 constexpr auto integralRange(const Begin& begin, const End& end)
173 {
174  return Impl::integralRange(begin, end, PriorityTag<42>());
175 }
176 
190 template<class End>
191 constexpr auto integralRange(const End& end)
192 {
194 }
195 
196 
197 
198 namespace 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 
255 template<class Range, class F>
256 constexpr void forEach(Range&& range, F&& f)
257 {
258  Impl::forEach(std::forward<Range>(range), std::forward<F>(f), PriorityTag<42>());
259 }
260 
261 
262 
278 template<class Range, class T, class F>
279 constexpr 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 
289 namespace 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 
343 template<class Condition, class IfFunc, class ElseFunc>
344 decltype(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 
356 template<class Condition, class IfFunc>
357 void ifElse(const Condition& condition, IfFunc&& ifFunc)
358 {
359  ifElse(condition, std::forward<IfFunc>(ifFunc), [](auto&&) {});
360 }
361 
362 
363 
364 namespace 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 
417 template<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 
425 public:
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 
458 template<class Functor>
459 constexpr HybridFunctor<Functor> hybridFunctor(const Functor&)
460 {
461  return {};
462 }
463 
484 inline constexpr auto max = hybridFunctor(Impl::Max{});
485 
506 inline constexpr auto min = hybridFunctor(Impl::Min{});
507 
528 inline constexpr auto plus = hybridFunctor(std::plus<>{});
529 
550 inline constexpr auto minus = hybridFunctor(std::minus<>{});
551 
572 inline constexpr auto equals = hybridFunctor(std::equal_to<>{});
573 
574 namespace Impl {
575 
576  // This overload is selected if the passed value is already a compile time constant.
577  template<class Result, class T, T t0, T... tt, class ValueType, ValueType value, class Branches, class ElseBranch>
578  constexpr Result switchCases(std::integer_sequence<T, t0, tt...>, const std::integral_constant<ValueType, value>& /*value*/, Branches&& branches, ElseBranch&& elseBranch)
579  {
580  // In case we pass a value known at compile time, we no longer have to do
581  // a dynamic to static dispatch via recursion. The only thing that's left
582  // is to check if the value is contained in the passed range.
583  // If this is true, we have to pass it to the branches callback
584  // as an appropriate integral_constant type. Otherwise we have to
585  // execute the else callback.
586  if constexpr (((t0 == value) || ... || (tt == value)))
587  return branches(std::integral_constant<T, value>{});
588  else
589  return elseBranch();
590  }
591 
592  // This overload is selected if the passed value is dynamic.
593  template<class Result, class T, class Value, class Branches, class ElseBranch>
594  constexpr Result switchCases(std::integer_sequence<T>, const Value& /*value*/, Branches&& /*branches*/, ElseBranch&& elseBranch)
595  {
596  return elseBranch();
597  }
598 
599  template<class Result, class T, T t0, T... tt, class Value, class Branches, class ElseBranch>
600  constexpr Result switchCases(std::integer_sequence<T, t0, tt...>, const Value& value, Branches&& branches, ElseBranch&& elseBranch)
601  {
602  if (t0 == value)
603  return branches(std::integral_constant<T, t0>());
604  else
605  return Impl::switchCases<Result>(std::integer_sequence<T, tt...>(), value, branches, elseBranch);
606  }
607 
608  // This overload is selected if the range of cases is an IntegralRange
609  template <class Result, class T, class Value, class Branches, class ElseBranch>
610  constexpr Result switchCases(IntegralRange<T> range, const Value& value, Branches&& branches, ElseBranch&& elseBranch)
611  {
612  return range.contains(value) ? branches(T(value)) : elseBranch();
613  }
614 
615  // This overload is selected if the range of cases is a StaticIntegralRange
616  template <class Result, class T, T to, T from, class Value, class Branches, class ElseBranch>
617  constexpr Result switchCases(StaticIntegralRange<T, to, from> range, const Value& value, Branches&& branches, ElseBranch&& elseBranch)
618  {
619  using seq = typename decltype(range)::integer_sequence;
620  return Impl::switchCases<Result>(seq{}, value, branches, elseBranch);
621  }
622 
623 } // namespace Impl
624 
625 
626 
654 template<class Cases, class Value, class Branches, class ElseBranch>
655 constexpr decltype(auto) switchCases(const Cases& cases, const Value& value, Branches&& branches, ElseBranch&& elseBranch)
656 {
657  return Impl::switchCases<decltype(elseBranch())>(cases, value, std::forward<Branches>(branches), std::forward<ElseBranch>(elseBranch));
658 }
659 
679 template<class Cases, class Value, class Branches>
680 constexpr void switchCases(const Cases& cases, const Value& value, Branches&& branches)
681 {
682  Impl::switchCases<void>(cases, value, std::forward<Branches>(branches),
683  []{ assert(false && "value not found in range"); });
684 }
685 
704 template <class T, class Value, class Branches>
705 constexpr void switchCases(IntegralRange<T> range, const Value& value, Branches&& branches)
706 {
707  assert(range.contains(value) && "value not found in range");
708  branches(T(value));
709 }
710 
711 } // namespace Hybrid
712 } // namespace Dune
713 
714 
715 #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:175
constexpr bool contains(value_type index) const noexcept
check whether given index is within range [from, to)
Definition: rangeutilities.hh:205
static integer range for use in range-based for loops
Definition: rangeutilities.hh:228
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 void forEach(Range &&range, F &&f)
Range based for loop.
Definition: hybridutilities.hh:256
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 auto equals
Function object for performing equality comparison.
Definition: hybridutilities.hh:572
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:705
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 integral constant or any type derived from std::integral_constant.
Definition: typetraits.hh:411
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.80.0 (May 4, 22:30, 2024)