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