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