1 // -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 // vi: set et ts=4 sw=2 sts=2:
3 #ifndef DUNE_COMMON_HYBRIDUTILITIES_HH
4 #define DUNE_COMMON_HYBRIDUTILITIES_HH
5 
6 #include <tuple>
7 #include <utility>
8 
9 #include <dune/common/typetraits.hh>
10 #include <dune/common/typeutilities.hh>
11 #include <dune/common/fvector.hh>
12 #include <dune/common/indices.hh>
13 #include <dune/common/assertandreturn.hh>
14 #include <dune/common/rangeutilities.hh>
15 
16 
17 
18 namespace Dune {
19 namespace Hybrid {
20 
21 namespace Impl {
22 
23   // Try if tuple_size is implemented for class
24   template<class T, int i>
size(const Dune::FieldVector<T,i> &,const PriorityTag<5> &)25   constexpr auto size(const Dune::FieldVector<T, i>&, const PriorityTag<5>&)
26     -> decltype(std::integral_constant<std::size_t,i>())
27   {
28     return {};
29   }
30 
31   // Try if tuple_size is implemented for class
32   template<class T>
size(const T &,const PriorityTag<3> &)33   constexpr auto size(const T&, const PriorityTag<3>&)
34     -> decltype(std::integral_constant<std::size_t,std::tuple_size<T>::value>())
35   {
36     return {};
37   }
38 
39   // Try if there's a static constexpr size()
40   template<class T>
size(const T &,const PriorityTag<1> &)41   constexpr auto size(const T&, const PriorityTag<1>&)
42     -> decltype(std::integral_constant<std::size_t,T::size()>())
43   {
44     return {};
45   }
46 
47   // As a last resort try if there's a static constexpr size()
48   template<class T>
size(const T & t,const PriorityTag<0> &)49   constexpr auto size(const T& t, const PriorityTag<0>&)
50   {
51     return t.size();
52   }
53 
54 } // namespace Impl
55 
56 
57 
58 /**
59  * \brief Size query
60  *
61  * \ingroup HybridUtilities
62  *
63  * \tparam T Type of container whose size is queried
64  *
65  * \param t Container whose size is queried
66  *
67  * \return Size of t
68  *
69  * If the size of t is known at compile type the size is
70  * returned as std::integral_constant<std::size_t, size>.
71  * Otherwise the result of t.size() is returned.
72  *
73  * Supported types for deriving the size at compile time are:
74  * * instances of std::integer_sequence
75  * * all types std::tuple_size is implemented for
76  * * all typed that have a static method ::size()
77  * * instances of Dune::FieldVector
78  */
79 template<class T>
size(const T & t)80 constexpr auto size(const T& t)
81 {
82   return Impl::size(t, PriorityTag<42>());
83 }
84 
85 
86 
87 namespace Impl {
88 
89   template<class Container, class Index,
90     std::enable_if_t<IsTuple<std::decay_t<Container>>::value, int> = 0>
elementAt(Container && c,Index &&,PriorityTag<2>)91   constexpr decltype(auto) elementAt(Container&& c, Index&&, PriorityTag<2>)
92   {
93     return std::get<std::decay_t<Index>::value>(c);
94   }
95 
96   template<class T, T... t, class Index>
elementAt(std::integer_sequence<T,t...> c,Index,PriorityTag<1>)97   constexpr decltype(auto) elementAt(std::integer_sequence<T, t...> c, Index, PriorityTag<1>)
98   {
99     return Dune::integerSequenceEntry(c, std::integral_constant<std::size_t, Index::value>());
100   }
101 
102   template<class Container, class Index>
elementAt(Container && c,Index && i,PriorityTag<0>)103   constexpr decltype(auto) elementAt(Container&& c, Index&& i, PriorityTag<0>)
104   {
105     return c[i];
106   }
107 
108 } // namespace Impl
109 
110 
111 
112 /**
113  * \brief Get element at given position from container
114  *
115  * \ingroup HybridUtilities
116  *
117  * \tparam Container Type of given container
118  * \tparam Index Type of index
119  *
120  * \param c Given container
121  * \param i Index of element to obtain
122  *
123  * \return The element at position i, i.e. c[i]
124  *
125  * If this returns the i-th entry of c. It supports the following
126  * containers
127  * * Containers providing dynamic access via operator[]
128  * * Heterogeneous containers providing access via operator[](integral_constant<...>)
129  * * std::tuple<...>
130  * * std::integer_sequence
131  */
132 template<class Container, class Index>
elementAt(Container && c,Index && i)133 constexpr decltype(auto) elementAt(Container&& c, Index&& i)
134 {
135   return Impl::elementAt(std::forward<Container>(c), std::forward<Index>(i), PriorityTag<42>());
136 }
137 
138 
139 
140 namespace Impl {
141 
142   template<class Begin, class End,
143     std::enable_if_t<IsIntegralConstant<Begin>::value and IsIntegralConstant<End>::value, int> = 0>
integralRange(const Begin &,const End &,const PriorityTag<1> &)144   constexpr auto integralRange(const Begin& /*begin*/, const End& /*end*/, const PriorityTag<1>&)
145   {
146     static_assert(Begin::value <= End::value, "You cannot create an integralRange where end<begin");
147     return Dune::StaticIntegralRange<std::size_t, End::value, Begin::value>();
148   }
149 
150   // This should be constexpr but gcc-4.9 does not support
151   // the relaxed constexpr requirements. Hence for being
152   // constexpr the function body can only contain a return
153   // statement and no assertion before this.
154   template<class Begin, class End>
integralRange(const Begin & begin,const End & end,const PriorityTag<0> &)155   constexpr auto integralRange(const Begin& begin, const End& end, const PriorityTag<0>&)
156   {
157     return DUNE_ASSERT_AND_RETURN(begin<=end, Dune::IntegralRange<End>(begin, end));
158   }
159 
160 } // namespace Impl
161 
162 
163 
164 /**
165  * \brief Create an integral range
166  *
167  * \ingroup HybridUtilities
168  *
169  * \tparam Begin Type of begin entry of the range
170  * \tparam End Type of end entry of the range
171  *
172  * \param begin First entry of the range
173  * \param end One past the last entry of the range
174  *
175  * \returns An object encoding the given range
176  *
177  * If Begin and End are both instances of type
178  * std::integral_constant, the returned range
179  * encodes begin and end statically.
180  */
181 template<class Begin, class End>
integralRange(const Begin & begin,const End & end)182 constexpr auto integralRange(const Begin& begin, const End& end)
183 {
184   return Impl::integralRange(begin, end, PriorityTag<42>());
185 }
186 
187 /**
188  * \brief Create an integral range starting from 0
189  *
190  * \ingroup HybridUtilities
191  *
192  * \tparam End Type of end entry of the range
193  *
194  * \param end One past the last entry of the range
195  *
196  * \returns An object encoding the given range
197  *
198  * This is a short cut for integralRange(_0, end).
199  */
200 template<class End>
integralRange(const End & end)201 constexpr auto integralRange(const End& end)
202 {
203   return Impl::integralRange(Dune::Indices::_0, end, PriorityTag<42>());
204 }
205 
206 
207 
208 namespace Impl {
209 
210   template<class T>
evaluateFoldExpression(std::initializer_list<T> &&)211   constexpr void evaluateFoldExpression(std::initializer_list<T>&&)
212   {}
213 
214   template<class Range, class F, class Index, Index... i>
forEachIndex(Range && range,F && f,std::integer_sequence<Index,i...>)215   constexpr void forEachIndex(Range&& range, F&& f, std::integer_sequence<Index, i...>)
216   {
217     evaluateFoldExpression<int>({(f(Hybrid::elementAt(range, std::integral_constant<Index,i>())), 0)...});
218   }
219 
220   template<class F, class Index, Index... i>
forEach(std::integer_sequence<Index,i...>,F && f,PriorityTag<2>)221   constexpr void forEach(std::integer_sequence<Index, i...> /*range*/, F&& f, PriorityTag<2>)
222   {
223     evaluateFoldExpression<int>({(f(std::integral_constant<Index,i>()), 0)...});
224   }
225 
226 
227   template<class Range, class F,
228     std::enable_if_t<IsIntegralConstant<decltype(Hybrid::size(std::declval<Range>()))>::value, int> = 0>
forEach(Range && range,F && f,PriorityTag<1>)229   constexpr void forEach(Range&& range, F&& f, PriorityTag<1>)
230   {
231     auto size = Hybrid::size(range);
232     auto indices = std::make_index_sequence<size>();
233     (forEachIndex)(std::forward<Range>(range), std::forward<F>(f), indices);
234   }
235 
236   template<class Range, class F>
forEach(Range && range,F && f,PriorityTag<0>)237   constexpr void forEach(Range&& range, F&& f, PriorityTag<0>)
238   {
239       for(auto&& e : range)
240         f(e);
241   }
242 
243 } // namespace Impl
244 
245 
246 
247 /**
248  * \brief Range based for loop
249  *
250  * \ingroup HybridUtilities
251  *
252  * \tparam Range Type of given range
253  * \tparam F Type of given predicate
254  *
255  * \param range The range to loop over
256  * \param f A predicate that will be called with each entry of the range
257  *
258  * This supports looping over the following ranges
259  * * ranges obtained from integralRange()
260  * * all ranges that provide Hybrid::size() and Hybrid::elementAt()
261  *
262  * This especially included instances of std::integer_sequence,
263  * std::tuple, Dune::TupleVector, and Dune::MultiTypeBlockVector.
264  */
265 template<class Range, class F>
forEach(Range && range,F && f)266 constexpr void forEach(Range&& range, F&& f)
267 {
268   Impl::forEach(std::forward<Range>(range), std::forward<F>(f), PriorityTag<42>());
269 }
270 
271 
272 
273 /**
274  * \brief Accumulate values
275  *
276  * \ingroup HybridUtilities
277  *
278  * \tparam Range Type of given range
279  * \tparam T Type of accumulated value
280  * \tparam F Type of binary accumulation operator
281  *
282  * \param range The range of values to accumulate
283  * \param value Initial value for accumulation
284  * \param f Binary operator for accumulation
285  *
286  * This supports looping over the same ranges as Hybrid::forEach
287  */
288 template<class Range, class T, class F>
accumulate(Range && range,T value,F && f)289 constexpr T accumulate(Range&& range, T value, F&& f)
290 {
291   forEach(std::forward<Range>(range), [&](auto&& entry) {
292     value = f(value, entry);
293   });
294   return value;
295 }
296 
297 
298 
299 namespace Impl {
300 
301   struct Id {
302     template<class T>
operator ()Dune::Hybrid::Impl::Id303     constexpr T operator()(T&& x) const {
304       return std::forward<T>(x);
305     }
306   };
307 
308   template<class IfFunc, class ElseFunc>
ifElse(std::true_type,IfFunc && ifFunc,ElseFunc &&)309   constexpr decltype(auto) ifElse(std::true_type, IfFunc&& ifFunc, ElseFunc&& /*elseFunc*/)
310   {
311     return ifFunc(Id{});
312   }
313 
314   template<class IfFunc, class ElseFunc>
ifElse(std::false_type,IfFunc &&,ElseFunc && elseFunc)315   constexpr decltype(auto) ifElse(std::false_type, IfFunc&& /*ifFunc*/, ElseFunc&& elseFunc)
316   {
317     return elseFunc(Id{});
318   }
319 
320   template<class IfFunc, class ElseFunc>
ifElse(const bool & condition,IfFunc && ifFunc,ElseFunc && elseFunc)321   decltype(auto) ifElse(const bool& condition, IfFunc&& ifFunc, ElseFunc&& elseFunc)
322   {
323     if (condition)
324       return ifFunc(Id{});
325     else
326       return elseFunc(Id{});
327   }
328 
329 } // namespace Impl
330 
331 
332 
333 /**
334  * \brief A conditional expression
335  *
336  * \ingroup HybridUtilities
337  *
338  * This will call either ifFunc or elseFunc depending
339  * on the condition. In any case a single argument
340  * will be passed to the called function. This will always
341  * be the identity function. Passing an expression through
342  * this function will lead to lazy evaluation. This way both
343  * 'branches' can contain expressions that are only valid
344  * within this branch if the condition is a std::integral_constant<bool,*>.
345  *
346  * In order to do this, the passed functors must have a single
347  * argument of type auto.
348  *
349  * Due to the lazy evaluation mechanism and support for
350  * std::integral_constant<bool,*> this allows to emulate
351  * a static if statement.
352  */
353 template<class Condition, class IfFunc, class ElseFunc>
ifElse(const Condition & condition,IfFunc && ifFunc,ElseFunc && elseFunc)354 decltype(auto) ifElse(const Condition& condition, IfFunc&& ifFunc, ElseFunc&& elseFunc)
355 {
356   return Impl::ifElse(condition, std::forward<IfFunc>(ifFunc), std::forward<ElseFunc>(elseFunc));
357 }
358 
359 /**
360  * \brief A conditional expression
361  *
362  * \ingroup HybridUtilities
363  *
364  * This provides an ifElse conditional with empty else clause.
365  */
366 template<class Condition, class IfFunc>
ifElse(const Condition & condition,IfFunc && ifFunc)367 void ifElse(const Condition& condition, IfFunc&& ifFunc)
368 {
369   ifElse(condition, std::forward<IfFunc>(ifFunc), [](auto&&) {});
370 }
371 
372 
373 
374 namespace Impl {
375 
376   template<class T1, class T2>
equals(const T1 &,const T2 &,PriorityTag<1>)377   constexpr auto equals(const T1& /*t1*/, const T2& /*t2*/, PriorityTag<1>) -> decltype(T1::value, T2::value, std::integral_constant<bool,T1::value == T2::value>())
378   { return {}; }
379 
380   template<class T1, class T2>
equals(const T1 & t1,const T2 & t2,PriorityTag<0>)381   constexpr auto equals(const T1& t1, const T2& t2, PriorityTag<0>)
382   {
383     return t1==t2;
384   }
385 
386 } // namespace Impl
387 
388 
389 
390 /**
391  * \brief Equality comparison
392  *
393  * \ingroup HybridUtilities
394  *
395  * If both types have a static member value, the result of comparing
396  * these is returned as std::integral_constant<bool, *>. Otherwise
397  * the result of a runtime comparison of t1 and t2 is directly returned.
398  */
399 template<class T1, class T2>
equals(T1 && t1,T2 && t2)400 constexpr auto equals(T1&& t1,  T2&& t2)
401 {
402   return Impl::equals(std::forward<T1>(t1), std::forward<T2>(t2), PriorityTag<1>());
403 }
404 
405 
406 
407 namespace Impl {
408 
409   template<class Result, class T, class Value, class Branches, class ElseBranch>
switchCases(std::integer_sequence<T>,const Value &,Branches &&,ElseBranch && elseBranch)410   constexpr Result switchCases(std::integer_sequence<T>, const Value& /*value*/, Branches&& /*branches*/, ElseBranch&& elseBranch)
411   {
412     return elseBranch();
413   }
414 
415   template<class Result, class T, T t0, T... tt, class Value, class Branches, class ElseBranch>
switchCases(std::integer_sequence<T,t0,tt...>,const Value & value,Branches && branches,ElseBranch && elseBranch)416   constexpr Result switchCases(std::integer_sequence<T, t0, tt...>, const Value& value, Branches&& branches, ElseBranch&& elseBranch)
417   {
418     return ifElse(
419         Hybrid::equals(std::integral_constant<T, t0>(), value),
420       [&](auto id) -> decltype(auto) {
421         return id(branches)(std::integral_constant<T, t0>());
422       }, [&](auto id) -> decltype(auto) {
423         return Impl::switchCases<Result>(id(std::integer_sequence<T, tt...>()), value, branches, elseBranch);
424     });
425   }
426 
427 } // namespace Impl
428 
429 
430 
431 /**
432  * \brief Switch statement
433  *
434  * \ingroup HybridUtilities
435  *
436  * \tparam Cases Type of case range
437  * \tparam Value Type of value to check against the cases
438  * \tparam Branches Type of branch function
439  * \tparam ElseBranch Type of branch function
440  *
441  * \param cases A range of cases to check for
442  * \param value The value to check against the cases
443  * \param branches A callback that will be executed with matching entry from case list
444  * \param elseBranch A callback that will be executed if no other entry matches
445  *
446  * Value is checked against all entries of the given range.
447  * If one matches, then branches is executed with the matching
448  * value as single argument. If the range is an std::integer_sequence,
449  * the value is passed as std::integral_constant.
450  * If non of the entries matches, then elseBranch is executed
451  * without any argument.
452  *
453  * Notice that this short circuits, e.g., if one case matches,
454  * the others are no longer evaluated.
455  *
456  * The return value will be deduced from the else branch.
457  */
458 template<class Cases, class Value, class Branches, class ElseBranch>
switchCases(const Cases & cases,const Value & value,Branches && branches,ElseBranch && elseBranch)459 constexpr decltype(auto) switchCases(const Cases& cases, const Value& value, Branches&& branches, ElseBranch&& elseBranch)
460 {
461   return Impl::switchCases<decltype(elseBranch())>(cases, value, std::forward<Branches>(branches), std::forward<ElseBranch>(elseBranch));
462 }
463 
464 /**
465  * \brief Switch statement
466  *
467  * \ingroup HybridUtilities
468  *
469  * \tparam Cases Type of case range
470  * \tparam Value Type of value to check against the cases
471  * \tparam Branches Type of branch function
472  *
473  * \param cases A range of cases to check for
474  * \param value The value to check against the cases
475  * \param branches A callback that will be executed with matching entry from case list
476  *
477  * Value is checked against all entries of the given range.
478  * If one matches, then branches is executed with the matching
479  * value as single argument. If the range is an std::integer_sequence,
480  * the value is passed as std::integral_constant.
481  * If non of the entries matches, then elseBranch is executed
482  * without any argument.
483  */
484 template<class Cases, class Value, class Branches>
switchCases(const Cases & cases,const Value & value,Branches && branches)485 constexpr void switchCases(const Cases& cases, const Value& value, Branches&& branches)
486 {
487   Impl::switchCases<void>(cases, value, std::forward<Branches>(branches), []() {});
488 }
489 
490 
491 } // namespace Hybrid
492 } // namespace Dune
493 
494 
495 #endif // #ifndef DUNE_COMMON_HYBRIDUTILITIES_HH
496