]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | /*! |
2 | @file | |
3 | Forward declares `boost::hana::eval_if`. | |
4 | ||
b32b8144 | 5 | @copyright Louis Dionne 2013-2017 |
7c673cae FG |
6 | Distributed under the Boost Software License, Version 1.0. |
7 | (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt) | |
8 | */ | |
9 | ||
10 | #ifndef BOOST_HANA_FWD_EVAL_IF_HPP | |
11 | #define BOOST_HANA_FWD_EVAL_IF_HPP | |
12 | ||
13 | #include <boost/hana/config.hpp> | |
14 | #include <boost/hana/core/when.hpp> | |
15 | ||
16 | ||
1e59de90 | 17 | namespace boost { namespace hana { |
7c673cae FG |
18 | //! Conditionally execute one of two branches based on a condition. |
19 | //! @ingroup group-Logical | |
20 | //! | |
21 | //! Given a condition and two branches in the form of lambdas or | |
22 | //! `hana::lazy`s, `eval_if` will evaluate the branch selected by the | |
23 | //! condition with `eval` and return the result. The exact requirements | |
24 | //! for what the branches may be are the same requirements as those for | |
25 | //! the `eval` function. | |
26 | //! | |
27 | //! | |
28 | //! Deferring compile-time evaluation inside `eval_if` | |
29 | //! -------------------------------------------------- | |
30 | //! By passing a unary callable to `eval_if`, it is possible to defer | |
31 | //! the compile-time evaluation of selected expressions inside the | |
32 | //! lambda. This is useful when instantiating a branch would trigger | |
33 | //! a compile-time error; we only want the branch to be instantiated | |
34 | //! when that branch is selected. Here's how it can be achieved. | |
35 | //! | |
36 | //! For simplicity, we'll use a unary lambda as our unary callable. | |
37 | //! Our lambda must accept a parameter (usually called `_`), which | |
38 | //! can be used to defer the compile-time evaluation of expressions | |
39 | //! as required. For example, | |
40 | //! @code | |
41 | //! template <typename N> | |
42 | //! auto fact(N n) { | |
43 | //! return hana::eval_if(n == hana::int_c<0>, | |
44 | //! [] { return hana::int_c<1>; }, | |
45 | //! [=](auto _) { return n * fact(_(n) - hana::int_c<1>); } | |
46 | //! ); | |
47 | //! } | |
48 | //! @endcode | |
49 | //! | |
50 | //! What happens here is that `eval_if` will call `eval` on the selected | |
51 | //! branch. In turn, `eval` will call the selected branch either with | |
52 | //! nothing -- for the _then_ branch -- or with `hana::id` -- for the | |
53 | //! _else_ branch. Hence, `_(x)` is always the same as `x`, but the | |
54 | //! compiler can't tell until the lambda has been called! Hence, the | |
55 | //! compiler has to wait before it instantiates the body of the lambda | |
56 | //! and no infinite recursion happens. However, this trick to delay the | |
57 | //! instantiation of the lambda's body can only be used when the condition | |
58 | //! is known at compile-time, because otherwise both branches have to be | |
59 | //! instantiated inside the `eval_if` anyway. | |
60 | //! | |
61 | //! There are several caveats to note with this approach to lazy branching. | |
62 | //! First, because we're using lambdas, it means that the function's | |
63 | //! result can't be used in a constant expression. This is a limitation | |
64 | //! of the current language. | |
65 | //! | |
66 | //! The second caveat is that compilers currently have several bugs | |
67 | //! regarding deeply nested lambdas with captures. So you always risk | |
68 | //! crashing the compiler, but this is a question of time before it is | |
69 | //! not a problem anymore. | |
70 | //! | |
71 | //! Finally, it means that conditionals can't be written directly inside | |
72 | //! unevaluated contexts. The reason is that a lambda can't appear in an | |
73 | //! unevaluated context, for example in `decltype`. One way to workaround | |
74 | //! this is to completely lift your type computations into variable | |
75 | //! templates instead. For example, instead of writing | |
76 | //! @code | |
77 | //! template <typename T> | |
78 | //! struct pointerize : decltype( | |
79 | //! hana::eval_if(hana::traits::is_pointer(hana::type_c<T>), | |
80 | //! [] { return hana::type_c<T>; }, | |
81 | //! [](auto _) { return _(hana::traits::add_pointer)(hana::type_c<T>); } | |
82 | //! )) | |
83 | //! { }; | |
84 | //! @endcode | |
85 | //! | |
86 | //! you could instead write | |
87 | //! | |
88 | //! @code | |
89 | //! template <typename T> | |
90 | //! auto pointerize_impl(T t) { | |
91 | //! return hana::eval_if(hana::traits::is_pointer(t), | |
92 | //! [] { return hana::type_c<T>; }, | |
93 | //! [](auto _) { return _(hana::traits::add_pointer)(hana::type_c<T>); } | |
94 | //! ); | |
95 | //! } | |
96 | //! | |
97 | //! template <typename T> | |
98 | //! using pointerize = decltype(pointerize_impl(hana::type_c<T>)); | |
99 | //! @endcode | |
100 | //! | |
101 | //! > __Note__: This example would actually be implemented more easily | |
102 | //! > with partial specializations, but my bag of good examples is empty | |
103 | //! > at the time of writing this. | |
104 | //! | |
105 | //! Now, this hoop-jumping only has to be done in one place, because | |
106 | //! you should use normal function notation everywhere else in your | |
107 | //! metaprogram to perform type computations. So the syntactic | |
108 | //! cost is amortized over the whole program. | |
109 | //! | |
110 | //! Another way to work around this limitation of the language would be | |
111 | //! to use `hana::lazy` for the branches. However, this is only suitable | |
112 | //! when the branches are not too complicated. With `hana::lazy`, you | |
113 | //! could write the previous example as | |
114 | //! @code | |
115 | //! template <typename T> | |
116 | //! struct pointerize : decltype( | |
117 | //! hana::eval_if(hana::traits::is_pointer(hana::type_c<T>), | |
118 | //! hana::make_lazy(hana::type_c<T>), | |
119 | //! hana::make_lazy(hana::traits::add_pointer)(hana::type_c<T>) | |
120 | //! )) | |
121 | //! { }; | |
122 | //! @endcode | |
123 | //! | |
124 | //! | |
125 | //! @param cond | |
126 | //! The condition determining which of the two branches is selected. | |
127 | //! | |
128 | //! @param then | |
129 | //! An expression called as `eval(then)` if `cond` is true-valued. | |
130 | //! | |
131 | //! @param else_ | |
132 | //! A function called as `eval(else_)` if `cond` is false-valued. | |
133 | //! | |
134 | //! | |
135 | //! Example | |
136 | //! ------- | |
137 | //! @include example/eval_if.cpp | |
138 | #ifdef BOOST_HANA_DOXYGEN_INVOKED | |
139 | constexpr auto eval_if = [](auto&& cond, auto&& then, auto&& else_) -> decltype(auto) { | |
140 | return tag-dispatched; | |
141 | }; | |
142 | #else | |
143 | template <typename L, typename = void> | |
144 | struct eval_if_impl : eval_if_impl<L, when<true>> { }; | |
145 | ||
146 | struct eval_if_t { | |
147 | template <typename Cond, typename Then, typename Else> | |
148 | constexpr decltype(auto) operator()(Cond&& cond, Then&& then, Else&& else_) const; | |
149 | }; | |
150 | ||
1e59de90 | 151 | BOOST_HANA_INLINE_VARIABLE constexpr eval_if_t eval_if{}; |
7c673cae | 152 | #endif |
1e59de90 | 153 | }} // end namespace boost::hana |
7c673cae FG |
154 | |
155 | #endif // !BOOST_HANA_FWD_EVAL_IF_HPP |