2121namespace lager {
2222namespace detail {
2323
24+ template <typename T>
25+ struct should_move :
26+ std::integral_constant<bool ,
27+ !std::is_array_v<T> &&
28+ !std::is_lvalue_reference_v<T>> {};
29+
30+ template <typename T>
31+ constexpr bool should_move_v = should_move<T>::value;
32+
2433template <typename T>
2534struct const_functor ;
2635
@@ -42,6 +51,15 @@ struct const_functor
4251 }
4352};
4453
54+ template <typename T>
55+ struct is_const_functor : public std ::false_type {};
56+
57+ template <typename T>
58+ struct is_const_functor <const_functor<T>> : public std::true_type {};
59+
60+ template <typename T>
61+ constexpr bool is_const_functor_v = is_const_functor<T>::value;
62+
4563template <typename T>
4664struct identity_functor ;
4765
@@ -59,40 +77,111 @@ struct identity_functor
5977 template <typename Fn>
6078 auto operator ()(Fn&& f) &&
6179 {
62- return make_identity_functor (
63- std::forward<Fn>(f)(std::forward<T>(value)));
80+
81+ if constexpr (!should_move_v<T>) {
82+ return make_identity_functor (
83+ std::forward<Fn>(f)(value));
84+ } else {
85+ return make_identity_functor (
86+ std::forward<Fn>(f)(std::move (value)));
87+ }
6488 }
6589};
6690
6791template <typename Part>
6892struct identity_functor_skip_first
6993{
7094 template <typename T>
71- auto operator () (T&&) const {
95+ auto operator () (T&&) const & {
7296 return make_identity_functor (part);
7397 }
7498
75- Part ∂
99+ template <typename T>
100+ auto operator () (T&&) && {
101+ if constexpr (!should_move_v<Part>) {
102+ return make_identity_functor (part);
103+ } else {
104+ return make_identity_functor (std::move (part));
105+ }
106+ }
107+
108+ Part part;
76109};
77110
78111template <typename T>
79- auto make_identity_functor_skip_first (T&& x) -> identity_functor_skip_first<std::remove_reference_t<T> >
112+ auto make_identity_functor_skip_first (T&& x) -> identity_functor_skip_first<T >
80113{
81114 return {std::forward<T>(x)};
82115}
83116
84- template <typename Func, typename Getter, typename Whole>
85- auto call_getter_or_skip (const Func& func, Getter&& getter, Whole&& whole) {
86- return func (LAGER_FWD (getter)(LAGER_FWD (whole)));
87- }
117+ template <typename F, typename Getter, typename Setter>
118+ struct getset_t
119+ {
120+ template <typename Whole>
121+ auto operator () (Whole &&w) {
122+ if constexpr (is_const_functor_v<decltype (f (getter (LAGER_FWD (w))))>) {
123+ /* *
124+ * We don't have a setter here, so it is safe to
125+ * jus pass `w` as an rvalue.
126+ *
127+ * We also know that we are calling the const_functor,
128+ * and it discards the passed argument, so just pass a
129+ * noop to it.
130+ *
131+ * This branch is taken when viewing through the lens.
132+ */
133+ return f (getter (LAGER_FWD (w))) // pass `w` into the getter as an rvalue!
134+ (zug::noop);
135+ } else {
136+ /* *
137+ * Here we have both, getter and setter, so we pass
138+ * `w` to getter as an lvalue, and to setter as an rvalue.
139+ * The setter has a chance to reuse the resources of
140+ * the passed value.
141+ *
142+ * This branch is taken on all the levels of setting the
143+ * value through except of the tompost level.
144+ */
145+ return f (getter (w)) // pass `w` into the getter as an lvalue!
146+ ([&](auto && x) {
147+ return setter (LAGER_FWD (w), LAGER_FWD (x));
148+ });
149+ }
150+ }
151+
152+ F f;
153+ Getter getter;
154+ Setter setter;
155+ };
156+
157+ /* *
158+ * This specialization is called when a set() method is called over
159+ * the lens. In such a case we can skip calling the getter branch
160+ * of the lens.
161+ *
162+ * This branch is taken on the topmost level of setting the value
163+ * through the lens.
164+ */
165+ template <typename T, typename Getter, typename Setter>
166+ struct getset_t <identity_functor_skip_first<T>, Getter, Setter>
167+ {
168+ template <typename Part>
169+ auto operator () (Part &&p) {
170+ return std::move (f)(zug::noop)
171+ ([&](auto && x) {
172+ return setter (LAGER_FWD (p), LAGER_FWD (x));
173+ });
174+ }
175+
176+ identity_functor_skip_first<T> &&f;
177+ Getter getter;
178+ Setter setter;
179+ };
88180
89- template <typename Getter, typename Part, typename Whole>
90- auto call_getter_or_skip (const identity_functor_skip_first<Part>& func, Getter&&, Whole&&) {
91- /* *
92- * When set() call is being executed, we should not execute the setter,
93- * the value will be dropped later anyway
94- */
95- return func (*static_cast <Part*>(nullptr ));
181+ template <typename F, typename Getter, typename Setter>
182+ auto make_getset_t (F &&f, const Getter &getter, const Setter &setter)
183+ {
184+ return getset_t <F, Getter, Setter>{std::forward<F>(f), getter, setter};
96185}
97186
98187} // namespace detail
@@ -112,7 +201,7 @@ decltype(auto) view(LensT&& lens, T&& x)
112201template <typename LensT, typename T, typename U>
113202decltype (auto ) set(LensT&& lens, T&& x, U&& v)
114203{
115- return lens (detail::make_identity_functor_skip_first (v )) (
204+ return lens (detail::make_identity_functor_skip_first (std::forward< decltype (v)>(v) )) (
116205 std::forward<T>(x))
117206 .value ;
118207}
@@ -135,14 +224,10 @@ namespace lenses {
135224// ! @{
136225
137226template <typename Getter, typename Setter>
138- auto getset (Getter&& getter, Setter& & setter)
227+ auto getset (const Getter& getter, const Setter& setter)
139228{
140229 return zug::comp ([=](auto && f) {
141- return [&, f = LAGER_FWD (f)](auto && p) {
142- return detail::call_getter_or_skip (f, getter, LAGER_FWD (p))([&](auto && x) {
143- return setter (LAGER_FWD (p), LAGER_FWD (x));
144- });
145- };
230+ return detail::make_getset_t (LAGER_FWD (f), getter, setter);
146231 });
147232}
148233
0 commit comments