1 // This file is part of CAF, the C++ Actor Framework. See the file LICENSE in
2 // the main distribution directory for license terms and copyright or visit
3 // https://github.com/actor-framework/actor-framework/blob/master/LICENSE.
4 
5 #pragma once
6 
7 #include <type_traits>
8 #include <utility>
9 
10 #include "caf/detail/as_mutable_ref.hpp"
11 #include "caf/detail/core_export.hpp"
12 #include "caf/error.hpp"
13 #include "caf/inspector_access.hpp"
14 #include "caf/string_view.hpp"
15 
16 namespace caf {
17 
18 /// Base type for inspectors that load objects from some input source. Deriving
19 /// from this class enables the inspector DSL.
20 /// @note The derived type still needs to provide an `object()` member function
21 ///       for the DSL.
22 class CAF_CORE_EXPORT load_inspector {
23 public:
24   // -- member types -----------------------------------------------------------
25 
26   using result_type [[deprecated("inspectors always return bool")]] = bool;
27 
28   // -- constants --------------------------------------------------------------
29 
30   /// Enables dispatching on the inspector type.
31   static constexpr bool is_loading = true;
32 
33   // -- constructors, destructors, and assignment operators --------------------
34 
35   virtual ~load_inspector();
36 
37   // -- properties -------------------------------------------------------------
38 
set_error(error stop_reason)39   void set_error(error stop_reason) {
40     err_ = std::move(stop_reason);
41   }
42 
43   template <class... Ts>
emplace_error(Ts &&...xs)44   void emplace_error(Ts&&... xs) {
45     err_ = make_error(std::forward<Ts>(xs)...);
46   }
47 
get_error() const48   const error& get_error() const noexcept {
49     return err_;
50   }
51 
move_error()52   error&& move_error() noexcept {
53     return std::move(err_);
54   }
55 
56   // -- DSL types for regular fields -------------------------------------------
57 
58   template <class T, class U, class Predicate>
59   struct field_with_invariant_and_fallback_t {
60     string_view field_name;
61     T* val;
62     U fallback;
63     Predicate predicate;
64 
65     template <class Inspector>
operator ()caf::load_inspector::field_with_invariant_and_fallback_t66     bool operator()(Inspector& f) {
67       auto reset = [this] { *val = std::move(fallback); };
68       return detail::load_field(f, field_name, *val, predicate,
69                                 detail::always_true, reset);
70     }
71   };
72 
73   template <class T, class U>
74   struct field_with_fallback_t {
75     string_view field_name;
76     T* val;
77     U fallback;
78 
79     template <class Inspector>
operator ()caf::load_inspector::field_with_fallback_t80     bool operator()(Inspector& f) {
81       auto reset = [this] { *val = std::move(fallback); };
82       return detail::load_field(f, field_name, *val, detail::always_true,
83                                 detail::always_true, reset);
84     }
85 
86     template <class Predicate>
invariantcaf::load_inspector::field_with_fallback_t87     auto invariant(Predicate predicate) && {
88       return field_with_invariant_and_fallback_t<T, U, Predicate>{
89         field_name,
90         val,
91         std::move(fallback),
92         std::move(predicate),
93       };
94     }
95   };
96 
97   template <class T, class Predicate>
98   struct field_with_invariant_t {
99     string_view field_name;
100     T* val;
101     Predicate predicate;
102 
103     template <class Inspector>
operator ()caf::load_inspector::field_with_invariant_t104     bool operator()(Inspector& f) {
105       return detail::load_field(f, field_name, *val, predicate,
106                                 detail::always_true);
107     }
108 
109     template <class U>
fallbackcaf::load_inspector::field_with_invariant_t110     auto fallback(U value) && {
111       return field_with_invariant_and_fallback_t<T, U, Predicate>{
112         field_name,
113         val,
114         std::move(value),
115         std::move(predicate),
116       };
117     }
118   };
119 
120   template <class T>
121   struct field_t {
122     string_view field_name;
123     T* val;
124 
125     template <class Inspector>
operator ()caf::load_inspector::field_t126     bool operator()(Inspector& f) {
127       return detail::load_field(f, field_name, *val, detail::always_true,
128                                 detail::always_true);
129     }
130 
131     template <class U>
fallbackcaf::load_inspector::field_t132     auto fallback(U value) && {
133       return field_with_fallback_t<T, U>{field_name, val, std::move(value)};
134     }
135 
136     template <class Predicate>
invariantcaf::load_inspector::field_t137     auto invariant(Predicate predicate) && {
138       return field_with_invariant_t<T, Predicate>{
139         field_name,
140         val,
141         std::move(predicate),
142       };
143     }
144   };
145 
146   // -- DSL types for virtual fields (getter and setter access) ----------------
147 
148   template <class T, class Set, class U, class Predicate>
149   struct virt_field_with_invariant_and_fallback_t {
150     string_view field_name;
151     Set set;
152     U fallback;
153     Predicate predicate;
154 
155     template <class Inspector>
operator ()caf::load_inspector::virt_field_with_invariant_and_fallback_t156     bool operator()(Inspector& f) {
157       auto tmp = T{};
158       auto sync = detail::bind_setter(f, set, tmp);
159       auto reset = [this] { set(std::move(fallback)); };
160       return detail::load_field(f, field_name, tmp, predicate, sync, reset);
161     }
162   };
163 
164   template <class T, class Set, class U>
165   struct virt_field_with_fallback_t {
166     string_view field_name;
167     Set set;
168     U fallback;
169 
170     template <class Inspector>
operator ()caf::load_inspector::virt_field_with_fallback_t171     bool operator()(Inspector& f) {
172       auto tmp = T{};
173       auto sync = detail::bind_setter(f, set, tmp);
174       auto reset = [this] { set(std::move(fallback)); };
175       return detail::load_field(f, field_name, tmp, detail::always_true, sync,
176                                 reset);
177     }
178 
179     template <class Predicate>
invariantcaf::load_inspector::virt_field_with_fallback_t180     auto invariant(Predicate predicate) && {
181       return virt_field_with_invariant_and_fallback_t<T, Set, U, Predicate>{
182         field_name,
183         std::move(set),
184         std::move(fallback),
185         std::move(predicate),
186       };
187     }
188   };
189 
190   template <class T, class Set, class Predicate>
191   struct virt_field_with_invariant_t {
192     string_view field_name;
193     Set set;
194     Predicate predicate;
195 
196     template <class Inspector>
operator ()caf::load_inspector::virt_field_with_invariant_t197     bool operator()(Inspector& f) {
198       auto tmp = T{};
199       auto sync = detail::bind_setter(f, set, tmp);
200       return detail::load_field(f, field_name, tmp, predicate, sync);
201     }
202 
203     template <class U>
fallbackcaf::load_inspector::virt_field_with_invariant_t204     auto fallback(U value) && {
205       return virt_field_with_invariant_and_fallback_t<T, Set, U, Predicate>{
206         field_name,
207         std::move(set),
208         std::move(value),
209         std::move(predicate),
210       };
211     }
212   };
213 
214   template <class T, class Set>
215   struct virt_field_t {
216     string_view field_name;
217     Set set;
218 
219     template <class Inspector>
operator ()caf::load_inspector::virt_field_t220     bool operator()(Inspector& f) {
221       auto tmp = T{};
222       auto sync = detail::bind_setter(f, set, tmp);
223       return detail::load_field(f, field_name, tmp, detail::always_true, sync);
224     }
225 
226     template <class U>
fallbackcaf::load_inspector::virt_field_t227     auto fallback(U value) && {
228       return virt_field_with_fallback_t<T, Set, U>{
229         field_name,
230         std::move(set),
231         std::move(value),
232       };
233     }
234 
235     template <class Predicate>
invariantcaf::load_inspector::virt_field_t236     auto invariant(Predicate predicate) && {
237       return virt_field_with_invariant_t<T, Set, Predicate>{
238         field_name,
239         std::move(set),
240         std::move(predicate),
241       };
242     }
243   };
244 
245   template <class T, class Reset, class Set>
246   struct optional_virt_field_t {
247     string_view field_name;
248     Reset reset;
249     Set set;
250 
251     template <class Inspector>
operator ()caf::load_inspector::optional_virt_field_t252     bool operator()(Inspector& f) {
253       auto tmp = T{};
254       auto sync = detail::bind_setter(f, set, tmp);
255       return detail::load_field(f, field_name, tmp, detail::always_true, sync,
256                                 reset);
257     }
258   };
259 
260   // -- DSL type for the object ------------------------------------------------
261 
262   template <class Inspector, class LoadCallback>
263   struct object_with_load_callback_t {
264     type_id_t object_type;
265     string_view object_name;
266     Inspector* f;
267     LoadCallback load_callback;
268 
269     template <class... Fields>
fieldscaf::load_inspector::object_with_load_callback_t270     bool fields(Fields&&... fs) {
271       using load_callback_result = decltype(load_callback());
272       if (!(f->begin_object(object_type, object_name) && (fs(*f) && ...)))
273         return false;
274       if constexpr (std::is_same<load_callback_result, bool>::value) {
275         if (!load_callback()) {
276           f->set_error(sec::load_callback_failed);
277           return false;
278         }
279       } else {
280         if (auto err = load_callback()) {
281           f->set_error(std::move(err));
282           return false;
283         }
284       }
285       return f->end_object();
286     }
287 
pretty_namecaf::load_inspector::object_with_load_callback_t288     auto pretty_name(string_view name) && {
289       return object_t{object_type, name, f};
290     }
291 
292     template <class F>
on_savecaf::load_inspector::object_with_load_callback_t293     object_with_load_callback_t&& on_save(F&&) && {
294       return std::move(*this);
295     }
296   };
297 
298   template <class Inspector>
299   struct object_t {
300     type_id_t object_type;
301     string_view object_name;
302     Inspector* f;
303 
304     template <class... Fields>
fieldscaf::load_inspector::object_t305     bool fields(Fields&&... fs) {
306       return f->begin_object(object_type, object_name) //
307              && (fs(*f) && ...)                        //
308              && f->end_object();
309     }
310 
pretty_namecaf::load_inspector::object_t311     auto pretty_name(string_view name) && {
312       return object_t{object_type, name, f};
313     }
314 
315     template <class F>
on_savecaf::load_inspector::object_t316     object_t&& on_save(F&&) && {
317       return std::move(*this);
318     }
319 
320     template <class F>
on_loadcaf::load_inspector::object_t321     auto on_load(F fun) && {
322       return object_with_load_callback_t<Inspector, F>{
323         object_type,
324         object_name,
325         f,
326         std::move(fun),
327       };
328     }
329   };
330 
331   // -- factory functions ------------------------------------------------------
332 
333   template <class T>
field(string_view name,T & x)334   static auto field(string_view name, T& x) {
335     static_assert(!std::is_const<T>::value);
336     return field_t<T>{name, &x};
337   }
338 
339   template <class Get, class Set>
field(string_view name,Get get,Set set)340   static auto field(string_view name, Get get, Set set) {
341     using field_type = std::decay_t<decltype(get())>;
342     using setter_result = decltype(set(std::declval<field_type&&>()));
343     if constexpr (std::is_same<setter_result, error>::value
344                   || std::is_same<setter_result, bool>::value) {
345       return virt_field_t<field_type, Set>{name, std::move(set)};
346     } else {
347       static_assert(std::is_same<setter_result, void>::value,
348                     "a setter must return caf::error, bool or void");
349       auto set_fun = [f{std::move(set)}](field_type&& val) {
350         f(std::move(val));
351         return true;
352       };
353       return virt_field_t<field_type, decltype(set_fun)>{name,
354                                                          std::move(set_fun)};
355     }
356   }
357 
358   template <class IsPresent, class Get, class Reset, class Set>
359   static auto
field(string_view name,IsPresent &&,Get && get,Reset reset,Set set)360   field(string_view name, IsPresent&&, Get&& get, Reset reset, Set set) {
361     using field_type = std::decay_t<decltype(get())>;
362     using setter_result = decltype(set(std::declval<field_type&&>()));
363     if constexpr (std::is_same<setter_result, error>::value
364                   || std::is_same<setter_result, bool>::value) {
365       return optional_virt_field_t<field_type, Reset, Set>{name,
366                                                            std::move(reset),
367                                                            std::move(set)};
368     } else {
369       static_assert(std::is_same<setter_result, void>::value,
370                     "a setter must return caf::error, bool or void");
371       auto set_fun = [f{std::move(set)}](field_type&& val) {
372         f(std::move(val));
373         return true;
374       };
375       return optional_virt_field_t<field_type, Reset, Set>{name,
376                                                            std::move(reset),
377                                                            std::move(set_fun)};
378     }
379   }
380 
381 protected:
382   error err_;
383 };
384 
385 } // namespace caf
386