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 <chrono>
8 #include <cstddef>
9 #include <memory>
10 #include <tuple>
11 #include <utility>
12 
13 #ifdef __has_include
14 #  if __has_include(<optional>)
15 #    include <optional>
16 #    define CAF_HAS_STD_OPTIONAL
17 #  endif
18 #  if __has_include(<variant>)
19 #    include <variant>
20 #    define CAF_HAS_STD_VARIANT
21 #  endif
22 #endif
23 
24 #include "caf/allowed_unsafe_message_type.hpp"
25 #include "caf/detail/as_mutable_ref.hpp"
26 #include "caf/detail/parse.hpp"
27 #include "caf/detail/print.hpp"
28 #include "caf/detail/type_list.hpp"
29 #include "caf/error.hpp"
30 #include "caf/fwd.hpp"
31 #include "caf/inspector_access_base.hpp"
32 #include "caf/inspector_access_type.hpp"
33 #include "caf/intrusive_ptr.hpp"
34 #include "caf/optional.hpp"
35 #include "caf/sec.hpp"
36 #include "caf/span.hpp"
37 #include "caf/sum_type_access.hpp"
38 #include "caf/variant.hpp"
39 
40 namespace caf::detail {
41 
42 // -- predicates ---------------------------------------------------------------
43 
44 /// Utility class for predicates that always return `true`.
45 struct always_true_t {
46   template <class... Ts>
operator ()caf::detail::always_true_t47   [[nodiscard]] constexpr bool operator()(Ts&&...) const noexcept {
48     return true;
49   }
50 };
51 
52 /// Predicate that always returns `true`.
53 constexpr auto always_true = always_true_t{};
54 
55 // -- traits -------------------------------------------------------------------
56 
57 template <class>
58 constexpr bool assertion_failed_v = false;
59 
60 // TODO: remove with CAF 0.19
61 template <class T, class Inspector, class Obj>
62 class has_static_apply {
63 private:
64   template <class U>
65   static auto sfinae(Inspector* f, Obj* x) -> decltype(U::apply(*f, *x));
66 
67   template <class U>
68   static auto sfinae(...) -> std::false_type;
69 
70   using sfinae_type = decltype(sfinae<T>(nullptr, nullptr));
71 
72 public:
73   static constexpr bool value
74     = !std::is_same<sfinae_type, std::false_type>::value;
75 };
76 
77 template <class T, class Inspector, class Obj>
78 constexpr bool has_static_apply_v = has_static_apply<T, Inspector, Obj>::value;
79 
80 // -- loading ------------------------------------------------------------------
81 
82 // Converts a setter that returns void, error or bool to a sync function object
83 // taking no arguments that always returns a bool.
84 template <class Inspector, class Set, class ValueType>
bind_setter(Inspector & f,Set & set,ValueType & tmp)85 auto bind_setter(Inspector& f, Set& set, ValueType& tmp) {
86   using set_result_type = decltype(set(std::move(tmp)));
87   if constexpr (std::is_same<set_result_type, bool>::value) {
88     return [&] { return set(std::move(tmp)); };
89   } else if constexpr (std::is_same<set_result_type, error>::value) {
90     return [&] {
91       if (auto err = set(std::move(tmp)); !err) {
92         return true;
93       } else {
94         f.set_error(std::move(err));
95         return false;
96       }
97     };
98   } else {
99     static_assert(std::is_same<set_result_type, void>::value,
100                   "a setter must return caf::error, bool or void");
101     return [&] {
102       set(std::move(tmp));
103       return true;
104     };
105   }
106 }
107 
108 // TODO: remove with CAF 0.19
109 template <class Inspector, class T>
110 [[deprecated("please provide apply instead of apply_object/apply_value")]] //
111 std::enable_if_t<!has_static_apply_v<inspector_access<T>, Inspector, T>, bool>
load(Inspector & f,T & x,inspector_access_type::specialization)112 load(Inspector& f, T& x, inspector_access_type::specialization) {
113   return inspector_access<T>::apply_value(f, x);
114 }
115 
116 template <class Inspector, class T>
117 std::enable_if_t<has_static_apply_v<inspector_access<T>, Inspector, T>, bool>
load(Inspector & f,T & x,inspector_access_type::specialization)118 load(Inspector& f, T& x, inspector_access_type::specialization) {
119   return inspector_access<T>::apply(f, x);
120 }
121 
122 template <class Inspector, class T>
load(Inspector & f,T & x,inspector_access_type::inspect)123 bool load(Inspector& f, T& x, inspector_access_type::inspect) {
124   return inspect(f, x);
125 }
126 
127 template <class Inspector, class T>
load(Inspector & f,T & x,inspector_access_type::builtin)128 bool load(Inspector& f, T& x, inspector_access_type::builtin) {
129   return f.value(x);
130 }
131 
132 template <class Inspector, class T>
load(Inspector & f,T & x,inspector_access_type::builtin_inspect)133 bool load(Inspector& f, T& x, inspector_access_type::builtin_inspect) {
134   return f.builtin_inspect(x);
135 }
136 
137 template <class Inspector, class T>
load(Inspector & f,T & x,inspector_access_type::empty)138 bool load(Inspector& f, T& x, inspector_access_type::empty) {
139   return f.object(x).fields();
140 }
141 
142 template <class Inspector, class T>
load(Inspector & f,T &,inspector_access_type::unsafe)143 bool load(Inspector& f, T&, inspector_access_type::unsafe) {
144   f.emplace_error(sec::unsafe_type);
145   return false;
146 }
147 
148 template <class Inspector, class T, size_t N>
load(Inspector & f,T (& xs)[N],inspector_access_type::tuple)149 bool load(Inspector& f, T (&xs)[N], inspector_access_type::tuple) {
150   return f.tuple(xs);
151 }
152 
153 template <class Inspector, class T>
load(Inspector & f,T & xs,inspector_access_type::tuple)154 bool load(Inspector& f, T& xs, inspector_access_type::tuple) {
155   return f.tuple(xs);
156 }
157 
158 template <class Inspector, class T>
load(Inspector & f,T & x,inspector_access_type::map)159 bool load(Inspector& f, T& x, inspector_access_type::map) {
160   return f.map(x);
161 }
162 
163 template <class Inspector, class T>
load(Inspector & f,T & x,inspector_access_type::list)164 bool load(Inspector& f, T& x, inspector_access_type::list) {
165   return f.list(x);
166 }
167 
168 template <class Inspector, class T>
169 std::enable_if_t<accepts_opaque_value<Inspector, T>::value, bool>
load(Inspector & f,T & x,inspector_access_type::none)170 load(Inspector& f, T& x, inspector_access_type::none) {
171   return f.opaque_value(x);
172 }
173 
174 template <class Inspector, class T>
175 std::enable_if_t<!accepts_opaque_value<Inspector, T>::value, bool>
load(Inspector &,T &,inspector_access_type::none)176 load(Inspector&, T&, inspector_access_type::none) {
177   static_assert(
178     detail::assertion_failed_v<T>,
179     "please provide an inspect overload for T or specialize inspector_access");
180   return false;
181 }
182 
183 template <class Inspector, class T>
load(Inspector & f,T & x)184 bool load(Inspector& f, T& x) {
185   return load(f, x, inspect_access_type<Inspector, T>());
186 }
187 
188 template <class Inspector, class T, class IsValid, class SyncValue>
load_field(Inspector & f,string_view field_name,T & x,IsValid & is_valid,SyncValue & sync_value)189 bool load_field(Inspector& f, string_view field_name, T& x, IsValid& is_valid,
190                 SyncValue& sync_value) {
191   using impl = std::conditional_t<is_complete<inspector_access<T>>, // if
192                                   inspector_access<T>,              // then
193                                   inspector_access_base<T>>;        // else
194   return impl::load_field(f, field_name, x, is_valid, sync_value);
195 }
196 
197 template <class Inspector, class T, class IsValid, class SyncValue,
198           class SetFallback>
load_field(Inspector & f,string_view field_name,T & x,IsValid & is_valid,SyncValue & sync_value,SetFallback & set_fallback)199 bool load_field(Inspector& f, string_view field_name, T& x, IsValid& is_valid,
200                 SyncValue& sync_value, SetFallback& set_fallback) {
201   using impl = std::conditional_t<is_complete<inspector_access<T>>, // if
202                                   inspector_access<T>,              // then
203                                   inspector_access_base<T>>;        // else
204   return impl::load_field(f, field_name, x, is_valid, sync_value, set_fallback);
205 }
206 
207 // -- saving -------------------------------------------------------------------
208 
209 // TODO: remove with CAF 0.19
210 template <class Inspector, class T>
211 [[deprecated("please provide apply instead of apply_object/apply_value")]] //
212 std::enable_if_t<!has_static_apply_v<inspector_access<T>, Inspector, T>, bool>
save(Inspector & f,T & x,inspector_access_type::specialization)213 save(Inspector& f, T& x, inspector_access_type::specialization) {
214   return inspector_access<T>::apply_value(f, x);
215 }
216 
217 template <class Inspector, class T>
218 std::enable_if_t<has_static_apply_v<inspector_access<T>, Inspector, T>, bool>
save(Inspector & f,T & x,inspector_access_type::specialization)219 save(Inspector& f, T& x, inspector_access_type::specialization) {
220   return inspector_access<T>::apply(f, x);
221 }
222 
223 template <class Inspector, class T>
save(Inspector & f,T & x,inspector_access_type::inspect)224 bool save(Inspector& f, T& x, inspector_access_type::inspect) {
225   return inspect(f, x);
226 }
227 
228 template <class Inspector, class T>
save(Inspector & f,T & x,inspector_access_type::builtin)229 bool save(Inspector& f, T& x, inspector_access_type::builtin) {
230   return f.value(x);
231 }
232 
233 template <class Inspector, class T>
save(Inspector & f,T & x,inspector_access_type::builtin_inspect)234 bool save(Inspector& f, T& x, inspector_access_type::builtin_inspect) {
235   return f.builtin_inspect(x);
236 }
237 
238 template <class Inspector, class T>
save(Inspector & f,T & x,inspector_access_type::empty)239 bool save(Inspector& f, T& x, inspector_access_type::empty) {
240   return f.object(x).fields();
241 }
242 
243 template <class Inspector, class T>
save(Inspector & f,T &,inspector_access_type::unsafe)244 bool save(Inspector& f, T&, inspector_access_type::unsafe) {
245   f.emplace_error(sec::unsafe_type);
246   return false;
247 }
248 
249 template <class Inspector, class T, size_t N>
save(Inspector & f,T (& xs)[N],inspector_access_type::tuple)250 bool save(Inspector& f, T (&xs)[N], inspector_access_type::tuple) {
251   return f.tuple(xs);
252 }
253 
254 template <class Inspector, class T>
save(Inspector & f,const T & xs,inspector_access_type::tuple)255 bool save(Inspector& f, const T& xs, inspector_access_type::tuple) {
256   return f.tuple(xs);
257 }
258 
259 template <class Inspector, class T>
save(Inspector & f,T & x,inspector_access_type::map)260 bool save(Inspector& f, T& x, inspector_access_type::map) {
261   return f.map(x);
262 }
263 
264 template <class Inspector, class T>
save(Inspector & f,T & x,inspector_access_type::list)265 bool save(Inspector& f, T& x, inspector_access_type::list) {
266   return f.list(x);
267 }
268 
269 template <class Inspector, class T>
270 std::enable_if_t<accepts_opaque_value<Inspector, T>::value, bool>
save(Inspector & f,T & x,inspector_access_type::none)271 save(Inspector& f, T& x, inspector_access_type::none) {
272   return f.opaque_value(x);
273 }
274 
275 template <class Inspector, class T>
276 std::enable_if_t<!accepts_opaque_value<Inspector, T>::value, bool>
save(Inspector &,T &,inspector_access_type::none)277 save(Inspector&, T&, inspector_access_type::none) {
278   static_assert(
279     detail::assertion_failed_v<T>,
280     "please provide an inspect overload for T or specialize inspector_access");
281   return false;
282 }
283 
284 template <class Inspector, class T>
save(Inspector & f,T & x)285 bool save(Inspector& f, T& x) {
286   return save(f, x, inspect_access_type<Inspector, T>());
287 }
288 
289 template <class Inspector, class T>
save(Inspector & f,const T & x)290 bool save(Inspector& f, const T& x) {
291   if constexpr (!std::is_function<T>::value) {
292     return save(f, as_mutable_ref(x), inspect_access_type<Inspector, T>());
293   } else {
294     // Only inspector such as the stringification_inspector are going to accept
295     // function pointers. Most other inspectors are going to trigger a static
296     // assertion when passing `inspector_access_type::none`.
297     auto fptr = std::add_pointer_t<T>{x};
298     return save(f, fptr, inspector_access_type::none{});
299   }
300 }
301 
302 template <class Inspector, class T>
save_field(Inspector & f,string_view field_name,T & x)303 bool save_field(Inspector& f, string_view field_name, T& x) {
304   using impl = std::conditional_t<is_complete<inspector_access<T>>, // if
305                                   inspector_access<T>,              // then
306                                   inspector_access_base<T>>;        // else
307   return impl::save_field(f, field_name, x);
308 }
309 
310 template <class Inspector, class IsPresent, class Get>
save_field(Inspector & f,string_view field_name,IsPresent & is_present,Get & get)311 bool save_field(Inspector& f, string_view field_name, IsPresent& is_present,
312                 Get& get) {
313   using T = std::decay_t<decltype(get())>;
314   using impl = std::conditional_t<is_complete<inspector_access<T>>, // if
315                                   inspector_access<T>,              // then
316                                   inspector_access_base<T>>;        // else
317   return impl::save_field(f, field_name, is_present, get);
318 }
319 
320 } // namespace caf::detail
321 
322 namespace caf {
323 
324 // -- customization points -----------------------------------------------------
325 
326 /// Customization point for adding support for a custom type.
327 template <class T>
328 struct inspector_access;
329 
330 // -- inspection support for optional values -----------------------------------
331 
332 template <class T>
333 struct optional_inspector_traits;
334 
335 template <class T>
336 struct optional_inspector_traits<optional<T>> {
337   using container_type = optional<T>;
338 
339   using value_type = T;
340 
341   template <class... Ts>
emplacecaf::optional_inspector_traits342   static void emplace(container_type& container, Ts&&... xs) {
343     container.emplace(std::forward<Ts>(xs)...);
344   }
345 };
346 
347 template <class T>
348 struct optional_inspector_traits<intrusive_ptr<T>> {
349   using container_type = intrusive_ptr<T>;
350 
351   using value_type = T;
352 
353   template <class... Ts>
emplacecaf::optional_inspector_traits354   static void emplace(container_type& container, Ts&&... xs) {
355     container.reset(new T(std::forward<Ts>(xs)...));
356   }
357 };
358 
359 template <class T>
360 struct optional_inspector_traits<std::unique_ptr<T>> {
361   using container_type = std::unique_ptr<T>;
362 
363   using value_type = T;
364 
365   template <class... Ts>
emplacecaf::optional_inspector_traits366   static void emplace(container_type& container, Ts&&... xs) {
367     container = std::make_unique<T>(std::forward<Ts>(xs)...);
368   }
369 };
370 
371 template <class T>
372 struct optional_inspector_traits<std::shared_ptr<T>> {
373   using container_type = std::shared_ptr<T>;
374 
375   using value_type = T;
376 
377   template <class... Ts>
emplacecaf::optional_inspector_traits378   static void emplace(container_type& container, Ts&&... xs) {
379     container = std::make_shared<T>(std::forward<Ts>(xs)...);
380   }
381 };
382 
383 /// Provides inspector access for types that represent optional values.
384 template <class T>
385 struct optional_inspector_access {
386   using traits = optional_inspector_traits<T>;
387 
388   using container_type = typename traits::container_type;
389 
390   using value_type = typename traits::value_type;
391 
392   template <class Inspector>
applycaf::optional_inspector_access393   [[nodiscard]] static bool apply(Inspector& f, container_type& x) {
394     return f.object(x).fields(f.field("value", x));
395   }
396 
397   template <class Inspector>
398   [[deprecated("use apply instead")]] static bool
apply_objectcaf::optional_inspector_access399   apply_object(Inspector& f, container_type& x) {
400     return apply(f, x);
401   }
402 
403   template <class Inspector>
404   [[deprecated("use apply instead")]] static bool
apply_valuecaf::optional_inspector_access405   apply_value(Inspector& f, container_type& x) {
406     return apply(f, x);
407   }
408 
409   template <class Inspector>
410   static bool
save_fieldcaf::optional_inspector_access411   save_field(Inspector& f, string_view field_name, container_type& x) {
412     auto is_present = [&x] { return static_cast<bool>(x); };
413     auto get = [&x]() -> decltype(auto) { return *x; };
414     return detail::save_field(f, field_name, is_present, get);
415   }
416 
417   template <class Inspector, class IsPresent, class Get>
save_fieldcaf::optional_inspector_access418   static bool save_field(Inspector& f, string_view field_name,
419                          IsPresent& is_present, Get& get) {
420     return detail::save_field(f, field_name, is_present, get);
421   }
422 
423   template <class Inspector, class IsValid, class SyncValue>
load_fieldcaf::optional_inspector_access424   static bool load_field(Inspector& f, string_view field_name,
425                          container_type& x, IsValid& is_valid,
426                          SyncValue& sync_value) {
427     traits::emplace(x);
428     auto reset = [&x] { x.reset(); };
429     return detail::load_field(f, field_name, *x, is_valid, sync_value, reset);
430   }
431 
432   template <class Inspector, class IsValid, class SyncValue, class SetFallback>
load_fieldcaf::optional_inspector_access433   static bool load_field(Inspector& f, string_view field_name,
434                          container_type& x, IsValid& is_valid,
435                          SyncValue& sync_value, SetFallback& set_fallback) {
436     traits::emplace(x);
437     return detail::load_field(f, field_name, *x, is_valid, sync_value,
438                               set_fallback);
439   }
440 };
441 
442 // -- inspection support for optional<T> ---------------------------------------
443 
444 template <class T>
445 struct inspector_access<optional<T>> : optional_inspector_access<optional<T>> {
446   // nop
447 };
448 
449 #ifdef CAF_HAS_STD_OPTIONAL
450 
451 template <class T>
452 struct optional_inspector_traits<std::optional<T>> {
453   using container_type = std::optional<T>;
454 
455   using value_type = T;
456 
457   template <class... Ts>
emplacecaf::optional_inspector_traits458   static void emplace(container_type& container, Ts&&... xs) {
459     container.emplace(std::forward<Ts>(xs)...);
460   }
461 };
462 
463 template <class T>
464 struct inspector_access<std::optional<T>>
465   : optional_inspector_access<std::optional<T>> {
466   // nop
467 };
468 
469 #endif
470 
471 // -- inspection support for error ---------------------------------------------
472 
473 template <>
474 struct inspector_access<std::unique_ptr<error::data>>
475   : optional_inspector_access<std::unique_ptr<error::data>> {
476   // nop
477 };
478 
479 // -- inspection support for std::byte -----------------------------------------
480 
481 #ifdef __cpp_lib_byte
482 
483 template <>
484 struct inspector_access<std::byte> : inspector_access_base<std::byte> {
485   template <class Inspector>
applycaf::inspector_access486   [[nodiscard]] static bool apply(Inspector& f, std::byte& x) {
487     using integer_type = std::underlying_type_t<std::byte>;
488     auto get = [&x] { return static_cast<integer_type>(x); };
489     auto set = [&x](integer_type val) { x = static_cast<std::byte>(val); };
490     return f.apply(get, set);
491   }
492 };
493 
494 #endif
495 
496 // -- inspection support for variant<Ts...> ------------------------------------
497 
498 template <class T>
499 struct variant_inspector_traits;
500 
501 template <class... Ts>
502 struct variant_inspector_traits<variant<Ts...>> {
503   static_assert(
504     (has_type_id_v<Ts> && ...),
505     "inspectors requires that each type in a variant has a type_id");
506 
507   using value_type = variant<Ts...>;
508 
509   static constexpr type_id_t allowed_types[] = {type_id_v<Ts>...};
510 
type_indexcaf::variant_inspector_traits511   static auto type_index(const value_type& x) {
512     return x.index();
513   }
514   template <class F, class Value>
visitcaf::variant_inspector_traits515   static auto visit(F&& f, Value&& x) {
516     return caf::visit(std::forward<F>(f), std::forward<Value>(x));
517   }
518 
519   template <class U>
assigncaf::variant_inspector_traits520   static auto assign(value_type& x, U&& value) {
521     x = std::forward<U>(value);
522   }
523 
524   template <class F>
loadcaf::variant_inspector_traits525   static bool load(type_id_t, F&, detail::type_list<>) {
526     return false;
527   }
528 
529   template <class F, class U, class... Us>
530   static bool
loadcaf::variant_inspector_traits531   load(type_id_t type, F& continuation, detail::type_list<U, Us...>) {
532     if (type_id_v<U> == type) {
533       auto tmp = U{};
534       continuation(tmp);
535       return true;
536     }
537     return load(type, continuation, detail::type_list<Us...>{});
538   }
539 
540   template <class F>
loadcaf::variant_inspector_traits541   static bool load(type_id_t type, F continuation) {
542     return load(type, continuation, detail::type_list<Ts...>{});
543   }
544 };
545 
546 template <class T>
547 struct variant_inspector_access {
548   using value_type = T;
549 
550   using traits = variant_inspector_traits<T>;
551 
552   template <class Inspector>
applycaf::variant_inspector_access553   [[nodiscard]] static bool apply(Inspector& f, value_type& x) {
554     return f.object(x).fields(f.field("value", x));
555   }
556 
557   template <class Inspector>
558   [[deprecated("use apply instead")]] static bool
apply_objectcaf::variant_inspector_access559   apply_object(Inspector& f, T& x) {
560     return apply(f, x);
561   }
562 
563   template <class Inspector>
564   [[deprecated("use apply instead")]] static bool
apply_valuecaf::variant_inspector_access565   apply_value(Inspector& f, T& x) {
566     return apply(f, x);
567   }
568 
569   template <class Inspector>
save_fieldcaf::variant_inspector_access570   static bool save_field(Inspector& f, string_view field_name, value_type& x) {
571     auto g = [&f](auto& y) { return detail::save(f, y); };
572     return f.begin_field(field_name, make_span(traits::allowed_types),
573                          traits::type_index(x)) //
574            && traits::visit(g, x)               //
575            && f.end_field();
576   }
577 
578   template <class Inspector, class IsPresent, class Get>
save_fieldcaf::variant_inspector_access579   static bool save_field(Inspector& f, string_view field_name,
580                          IsPresent& is_present, Get& get) {
581     auto allowed_types = make_span(traits::allowed_types);
582     if (is_present()) {
583       auto&& x = get();
584       auto g = [&f](auto& y) { return detail::save(f, y); };
585       return f.begin_field(field_name, true, allowed_types,
586                            traits::type_index(x)) //
587              && traits::visit(g, x)               //
588              && f.end_field();
589     }
590     return f.begin_field(field_name, false, allowed_types, 0) //
591            && f.end_field();
592   }
593 
594   template <class Inspector>
load_variant_valuecaf::variant_inspector_access595   static bool load_variant_value(Inspector& f, string_view field_name,
596                                  value_type& x, type_id_t runtime_type) {
597     auto res = false;
598     auto type_found = traits::load(runtime_type, [&](auto& tmp) {
599       if (!detail::load(f, tmp))
600         return;
601       traits::assign(x, std::move(tmp));
602       res = true;
603       return;
604     });
605     if (!type_found)
606       f.emplace_error(sec::invalid_field_type, to_string(field_name));
607     return res;
608   }
609 
610   template <class Inspector, class IsValid, class SyncValue>
load_fieldcaf::variant_inspector_access611   static bool load_field(Inspector& f, string_view field_name, value_type& x,
612                          IsValid& is_valid, SyncValue& sync_value) {
613     size_t type_index = std::numeric_limits<size_t>::max();
614     auto allowed_types = make_span(traits::allowed_types);
615     if (!f.begin_field(field_name, allowed_types, type_index))
616       return false;
617     if (type_index >= allowed_types.size()) {
618       f.emplace_error(sec::invalid_field_type, to_string(field_name));
619       return false;
620     }
621     auto runtime_type = allowed_types[type_index];
622     if (!load_variant_value(f, field_name, x, runtime_type))
623       return false;
624     if (!is_valid(x)) {
625       f.emplace_error(sec::field_invariant_check_failed, to_string(field_name));
626       return false;
627     }
628     if (!sync_value()) {
629       if (!f.get_error())
630         f.emplace_error(sec::field_value_synchronization_failed,
631                         to_string(field_name));
632       return false;
633     }
634     return f.end_field();
635   }
636 
637   template <class Inspector, class IsValid, class SyncValue, class SetFallback>
load_fieldcaf::variant_inspector_access638   static bool load_field(Inspector& f, string_view field_name, value_type& x,
639                          IsValid& is_valid, SyncValue& sync_value,
640                          SetFallback& set_fallback) {
641     bool is_present = false;
642     auto allowed_types = make_span(traits::allowed_types);
643     size_t type_index = std::numeric_limits<size_t>::max();
644     if (!f.begin_field(field_name, is_present, allowed_types, type_index))
645       return false;
646     if (is_present) {
647       if (type_index >= allowed_types.size()) {
648         f.emplace_error(sec::invalid_field_type, to_string(field_name));
649         return false;
650       }
651       auto runtime_type = allowed_types[type_index];
652       if (!load_variant_value(f, field_name, x, runtime_type))
653         return false;
654       if (!is_valid(x)) {
655         f.emplace_error(sec::field_invariant_check_failed,
656                         to_string(field_name));
657         return false;
658       }
659       if (!sync_value()) {
660         if (!f.get_error())
661           f.emplace_error(sec::field_value_synchronization_failed,
662                           to_string(field_name));
663         return false;
664       }
665     } else {
666       set_fallback();
667     }
668     return f.end_field();
669   }
670 };
671 
672 template <class... Ts>
673 struct inspector_access<variant<Ts...>>
674   : variant_inspector_access<variant<Ts...>> {
675   // nop
676 };
677 
678 #ifdef CAF_HAS_STD_VARIANT
679 
680 template <class... Ts>
681 struct variant_inspector_traits<std::variant<Ts...>> {
682   static_assert(
683     (has_type_id_v<Ts> && ...),
684     "inspectors requires that each type in a variant has a type_id");
685 
686   using value_type = std::variant<Ts...>;
687 
688   static constexpr type_id_t allowed_types[] = {type_id_v<Ts>...};
689 
type_indexcaf::variant_inspector_traits690   static auto type_index(const value_type& x) {
691     return x.index();
692   }
693 
694   template <class F, class Value>
visitcaf::variant_inspector_traits695   static auto visit(F&& f, Value&& x) {
696     return std::visit(std::forward<F>(f), std::forward<Value>(x));
697   }
698 
699   template <class U>
assigncaf::variant_inspector_traits700   static auto assign(value_type& x, U&& value) {
701     x = std::forward<U>(value);
702   }
703 
704   template <class F>
loadcaf::variant_inspector_traits705   static bool load(type_id_t, F&, detail::type_list<>) {
706     return false;
707   }
708 
709   template <class F, class U, class... Us>
710   static bool
loadcaf::variant_inspector_traits711   load(type_id_t type, F& continuation, detail::type_list<U, Us...>) {
712     if (type_id_v<U> == type) {
713       auto tmp = U{};
714       continuation(tmp);
715       return true;
716     }
717     return load(type, continuation, detail::type_list<Us...>{});
718   }
719 
720   template <class F>
loadcaf::variant_inspector_traits721   static bool load(type_id_t type, F continuation) {
722     return load(type, continuation, detail::type_list<Ts...>{});
723   }
724 };
725 
726 template <class... Ts>
727 struct inspector_access<std::variant<Ts...>>
728   : variant_inspector_access<std::variant<Ts...>> {
729   // nop
730 };
731 
732 #endif
733 
734 // -- inspection support for std::chrono types ---------------------------------
735 
736 template <class Rep, class Period>
737 struct inspector_access<std::chrono::duration<Rep, Period>>
738   : inspector_access_base<std::chrono::duration<Rep, Period>> {
739   using value_type = std::chrono::duration<Rep, Period>;
740 
741   template <class Inspector>
applycaf::inspector_access742   static bool apply(Inspector& f, value_type& x) {
743     if (f.has_human_readable_format()) {
744       auto get = [&x] {
745         std::string str;
746         detail::print(str, x);
747         return str;
748       };
749       auto set = [&x](std::string str) {
750         auto err = detail::parse(str, x);
751         return !err;
752       };
753       return f.apply(get, set);
754     } else {
755       auto get = [&x] { return x.count(); };
756       auto set = [&x](Rep value) {
757         x = std::chrono::duration<Rep, Period>{value};
758         return true;
759       };
760       return f.apply(get, set);
761     }
762   }
763 
764   template <class Inspector>
765   [[deprecated("use apply instead")]] static bool
apply_objectcaf::inspector_access766   apply_object(Inspector& f, value_type& x) {
767     return apply(f, x);
768   }
769 
770   template <class Inspector>
771   [[deprecated("use apply instead")]] static bool
apply_valuecaf::inspector_access772   apply_value(Inspector& f, value_type& x) {
773     return apply(f, x);
774   }
775 };
776 
777 template <class Duration>
778 struct inspector_access<
779   std::chrono::time_point<std::chrono::system_clock, Duration>>
780   : inspector_access_base<
781       std::chrono::time_point<std::chrono::system_clock, Duration>> {
782   using value_type
783     = std::chrono::time_point<std::chrono::system_clock, Duration>;
784 
785   template <class Inspector>
applycaf::inspector_access786   static bool apply(Inspector& f, value_type& x) {
787     if (f.has_human_readable_format()) {
788       auto get = [&x] {
789         std::string str;
790         detail::print(str, x);
791         return str;
792       };
793       auto set = [&x](std::string str) { return detail::parse(str, x); };
794       return f.apply(get, set);
795     } else {
796       using rep_type = typename Duration::rep;
797       auto get = [&x] { return x.time_since_epoch().count(); };
798       auto set = [&x](rep_type value) {
799         x = value_type{Duration{value}};
800         return true;
801       };
802       return f.apply(get, set);
803     }
804   }
805 
806   template <class Inspector>
807   [[deprecated("use apply instead")]] static bool
apply_objectcaf::inspector_access808   apply_object(Inspector& f, value_type& x) {
809     return apply(f, x);
810   }
811 
812   template <class Inspector>
813   [[deprecated("use apply instead")]] static bool
apply_valuecaf::inspector_access814   apply_value(Inspector& f, value_type& x) {
815     return apply(f, x);
816   }
817 };
818 
819 // -- deprecated API -----------------------------------------------------------
820 
821 template <class T>
822 struct default_inspector_access : inspector_access_base<T> {
823   template <class Inspector>
824   [[deprecated("call f.apply(x) instead")]] static bool
apply_objectcaf::default_inspector_access825   apply_object(Inspector& f, T& x) {
826     return f.apply(x);
827   }
828 
829   template <class Inspector>
830   [[deprecated("call f.apply(x) instead")]] static bool
apply_valuecaf::default_inspector_access831   apply_value(Inspector& f, T& x) {
832     return f.apply(x);
833   }
834 };
835 
836 } // namespace caf
837