Nui
Loading...
Searching...
No Matches
val_conversion.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <nui/concepts.hpp>
5
6#pragma clang diagnostic push
7#pragma clang diagnostic ignored "-Wgnu-zero-variadic-macro-arguments"
8#pragma clang diagnostic ignored "-Wold-style-cast"
9#include <boost/describe.hpp>
10#include <boost/mp11/algorithm.hpp>
11#pragma clang diagnostic pop
12
13#include <nui/frontend/val.hpp>
14
15#include <optional>
16#include <vector>
17#include <filesystem>
18#include <utility>
19#include <unordered_map>
20#include <string_view>
21#include <memory>
22#include <map>
23#include <variant>
24
25namespace Nui
26{
27 namespace Detail
28 {
29 template <typename T>
30 struct IsOptional : std::false_type
31 {};
32 template <typename T>
33 struct IsOptional<std::optional<T>> : std::true_type
34 {};
35
41 template <typename T>
42 concept HasToVal = requires(T const& t, Nui::val& v) { to_val(v, t); };
43
49 template <typename T>
50 concept HasFromVal = requires(Nui::val const& v, T& t) { from_val(v, t); };
51 }
52
63 template <
64 typename T,
65 class Bases = boost::describe::describe_bases<T, boost::describe::mod_any_access>,
66 class Members = boost::describe::describe_members<T, boost::describe::mod_any_access>,
67 class Enable = std::enable_if_t<!std::is_union_v<T>>>
68 Nui::val convertToVal(T const& obj);
69
77 template <typename T>
78 Nui::val convertToVal(std::optional<T> const& option);
79
87 template <typename T>
88 requires Fundamental<T>
89 Nui::val convertToVal(T const& value);
90
97 Nui::val convertToVal(std::string const& value);
98
105 Nui::val convertToVal(std::filesystem::path const& value);
106
114
121 Nui::val convertToVal(char const* value);
122
129 Nui::val convertToVal(std::string_view value);
130
138 template <typename T>
139 Nui::val convertToVal(std::vector<T> const& vector);
140
148 template <typename T>
149 Nui::val convertToVal(Observed<T> const& observed);
150
158 template <typename T>
159 Nui::val convertToVal(std::unordered_map<std::string, T> const& map);
160
168 template <typename T>
169 Nui::val convertToVal(std::map<std::string, T> const& map);
170
178 template <typename T>
179 Nui::val convertToVal(std::unique_ptr<T> const& ptr);
180
188 template <typename T>
189 Nui::val convertToVal(std::shared_ptr<T> const& ptr);
190
198 template <typename T>
199 requires Detail::HasToVal<T>
200 Nui::val convertToVal(T const& value);
201
202#if __POINTER_WIDTH__ == 64
209 Nui::val convertToVal(long long value);
210
217 Nui::val convertToVal(unsigned long long value);
218#else
219 inline Nui::val convertToVal(long long)
220 {
221 throw std::runtime_error("Use -sMEMORY64 to convert long long to val");
222 }
223 inline Nui::val convertToVal(unsigned long long)
224 {
225 throw std::runtime_error("Use -sMEMORY64 to convert unsigned long long to val");
226 }
227#endif
233 inline Nui::val convertToVal(std::monostate)
234 {
235 return Nui::val::undefined();
236 }
237
245 template <typename... Ts>
246 Nui::val convertToVal(std::variant<Ts...> const& variant);
247
248 namespace Detail
249 {
250 template <typename T, class Members>
251 requires(!std::is_union_v<T>)
252 void convertFromValObjImpl(Nui::val const& val, T& obj);
253 }
254
262 template <typename T, class Bases, class Members>
263 requires(!std::is_union_v<T>)
264 void convertFromVal(Nui::val const& val, T& obj);
265
272 template <typename T, class Members>
273 requires(!std::is_union_v<T> && !boost::describe::has_describe_bases<T>::value)
274 void convertFromVal(Nui::val const& val, T& obj);
275
283 template <typename T>
284 requires Fundamental<T>
285 void convertFromVal(Nui::val const& val, T& value);
286
293 void convertFromVal(Nui::val const& val, std::string& str);
294
302 template <typename T>
303 void convertFromVal(Nui::val const& val, std::optional<T>& option);
304
311 void convertFromVal(Nui::val const& val, std::filesystem::path& value);
312
319 void convertFromVal(Nui::val const& val, Nui::val& value);
320
328 template <typename T>
329 void convertFromVal(Nui::val const& val, std::vector<T>& vector);
330
338 template <typename T>
339 requires Fundamental<T>
340 void convertFromVal(Nui::val const& val, std::vector<T>& vector);
341
349 template <typename T>
350 void convertFromVal(Nui::val const& val, Observed<T>& observed);
351
359 template <typename T>
360 void convertFromVal(Nui::val const& val, std::unordered_map<std::string, T>& map);
361
369 template <typename T>
370 requires Detail::HasFromVal<T>
371 void convertFromVal(Nui::val const& val, T& value);
372
373#if __POINTER_WIDTH__ == 64
380 void convertFromVal(Nui::val const& val, long long& value);
381
388 void convertFromVal(Nui::val const& val, unsigned long long& value);
389#else
390 inline void convertFromVal(Nui::val const&, long long)
391 {
392 throw std::invalid_argument("Use -sMEMORY64 to convert from val to long long");
393 }
394
395 inline void convertFromVal(Nui::val const& val, unsigned long long& value)
396 {
397 throw std::invalid_argument("Use -sMEMORY64 to convert from val to unsigned long long");
398 }
399#endif
400
401 template <typename T, class Bases, class Members, class Enable>
402 Nui::val convertToVal(T const& obj)
403 {
404 Nui::val val = Nui::val::object();
405
406 boost::mp11::mp_for_each<Bases>([&](auto&& base) {
407 using type = typename std::decay_t<decltype(base)>::type;
408 convertToVal(val, static_cast<type const&>(obj));
409 });
410
411 boost::mp11::mp_for_each<Members>([&](auto&& memAccessor) {
412 val.set(memAccessor.name, convertToVal(obj.*memAccessor.pointer));
413 });
414
415 return val;
416 }
417
418 template <typename T>
419 Nui::val convertToVal(std::optional<T> const& option)
420 {
421 return option ? convertToVal(*option) : Nui::val::undefined();
422 }
423 template <typename T>
424 requires Fundamental<T>
425 Nui::val convertToVal(T const& value)
426 {
427 return Nui::val{value};
428 }
429 inline Nui::val convertToVal(std::string const& value)
430 {
431 return Nui::val{value};
432 }
433 inline Nui::val convertToVal(std::string_view value)
434 {
435 return convertToVal(std::string{value});
436 }
437 inline Nui::val convertToVal(std::filesystem::path const& value)
438 {
439 return Nui::val{value.string()};
440 }
442 {
443 return value;
444 }
445 inline Nui::val convertToVal(char const* value)
446 {
447 return Nui::val{std::string{value}};
448 }
449 template <typename T>
450 Nui::val convertToVal(std::vector<T> const& vector)
451 {
452 Nui::val result = Nui::val::array();
453 for (auto const& element : vector)
454 result.call<void>("push", convertToVal(element));
455 return result;
456 }
457 template <typename T>
459 {
460 return convertToVal(observed.value());
461 }
462 template <typename T>
463 Nui::val convertToVal(std::unordered_map<std::string, T> const& map)
464 {
465 Nui::val result = Nui::val::object();
466 for (auto const& [key, value] : map)
467 result.set(key, convertToVal(value));
468 return result;
469 }
470 template <typename T>
471 Nui::val convertToVal(std::map<std::string, T> const& map)
472 {
473 Nui::val result = Nui::val::object();
474 for (auto const& [key, value] : map)
475 result.set(key, convertToVal(value));
476 return result;
477 }
478 template <typename T>
479 Nui::val convertToVal(std::unique_ptr<T> const& ptr)
480 {
481 if (ptr)
482 return convertToVal(*ptr);
483 return Nui::val::null();
484 }
485 template <typename T>
486 Nui::val convertToVal(std::shared_ptr<T> const& ptr)
487 {
488 if (ptr)
489 return convertToVal(*ptr);
490 return Nui::val::null();
491 }
492 template <typename... Ts>
493 Nui::val convertToVal(std::variant<Ts...> const& variant)
494 {
495 return std::visit(
496 [](auto&& value) {
497 return convertToVal(value);
498 },
499 variant);
500 }
501
502 template <typename T>
503 requires Detail::HasToVal<T>
504 Nui::val convertToVal(T const& value)
505 {
507 to_val(val, value);
508 return val;
509 }
510
511#if __POINTER_WIDTH__ == 64
512 inline Nui::val convertToVal(long long value)
513 {
514 return Nui::val{value};
515 }
516 inline Nui::val convertToVal(unsigned long long value)
517 {
518 return Nui::val{value};
519 }
520#endif
521
522 template <typename T, class Members = boost::describe::describe_members<T, boost::describe::mod_any_access>>
523 requires(!std::is_union_v<T> && !boost::describe::has_describe_bases<T>::value)
524 void convertFromVal(Nui::val const& val, T& obj)
525 {
527 }
528
529 template <
530 typename T,
531 class Bases = boost::describe::describe_bases<T, boost::describe::mod_any_access>,
532 class Members = boost::describe::describe_members<T, boost::describe::mod_any_access>>
533 requires(!std::is_union_v<T> && boost::describe::has_describe_bases<T>::value)
534 void convertFromVal(Nui::val const& val, T& obj)
535 {
536 boost::mp11::mp_for_each<Bases>([&](auto&& base) {
537 using type = typename std::decay_t<decltype(base)>::type;
538 convertFromVal(val, static_cast<type&>(obj));
539 });
541 }
542
543 namespace Detail
544 {
545 template <typename T, class Members = boost::describe::describe_members<T, boost::describe::mod_any_access>>
546 requires(!std::is_union_v<T>)
548 {
549 boost::mp11::mp_for_each<Members>([&](auto&& memAccessor) {
550 if (val.hasOwnProperty(memAccessor.name))
551 {
552 if constexpr (!Detail::IsOptional<std::decay_t<decltype(obj.*memAccessor.pointer)>>::value)
553 {
554 if (val[memAccessor.name].isNull() || val[memAccessor.name].isUndefined())
555 {
556 throw std::invalid_argument(
557 std::string{"Expected member "} + memAccessor.name + " to be non nullish");
558 }
559 convertFromVal(val[memAccessor.name], obj.*memAccessor.pointer);
560 }
561 else
563 }
564 else
565 {
566 if constexpr (Detail::IsOptional<std::decay_t<decltype(obj.*memAccessor.pointer)>>::value)
567 {
568 // If the member is optional and not present, set to nullopt:
569 obj.*memAccessor.pointer = std::nullopt;
570 }
571 else
572 {
573 throw std::invalid_argument(std::string{"Missing required member "} + memAccessor.name);
574 }
575 }
576 });
577 }
578 }
579
580 template <typename T>
581 requires Fundamental<T>
582 void convertFromVal(Nui::val const& val, T& value)
583 {
584 value = val.as<T>();
585 }
586 inline void convertFromVal(Nui::val const& val, std::string& str)
587 {
588 str = val.as<std::string>();
589 }
590 template <typename T>
591 void convertFromVal(Nui::val const& val, std::optional<T>& option)
592 {
593 if (val.isNull() || val.isUndefined())
594 option = std::nullopt;
595 else
596 {
597 T value;
598 convertFromVal(val, value);
599 option = value;
600 }
601 }
602 inline void convertFromVal(Nui::val const& val, std::filesystem::path& value)
603 {
604 value = val.as<std::string>();
605 }
606 inline void convertFromVal(Nui::val const& val, Nui::val& value)
607 {
608 value = val;
609 }
610 template <typename T>
611 void convertFromVal(Nui::val const& val, std::vector<T>& vector)
612 {
613 vector.clear();
614 const auto length = val["length"].as<std::size_t>();
615 vector.reserve(length);
616 for (std::size_t i = 0; i < length; ++i)
617 {
618 T value;
619 convertFromVal(val[i], value);
620 vector.push_back(std::move(value));
621 }
622 }
623 template <typename T>
624 requires Fundamental<T>
625 void convertFromVal(Nui::val const& val, std::vector<T>& vector)
626 {
627 vector = emscripten::convertJSArrayToNumberVector<T>(val);
628 }
629 template <typename T>
630 void convertFromVal(Nui::val const& val, Observed<T>& observed)
631 {
632 auto proxy = observed.modify();
633 convertFromVal(val, proxy.value());
634 }
635 template <typename T>
636 void convertFromVal(Nui::val const& val, std::unordered_map<std::string, T>& map)
637 {
638 map.clear();
639 const auto keys = Nui::val::global("Object").call<Nui::val>("keys", val);
640 const auto length = keys["length"].as<std::size_t>();
641 for (std::size_t i = 0; i < length; ++i)
642 {
643 const auto key = keys[i].as<std::string>();
644 T value;
645 convertFromVal(val[key], value);
646 map.emplace(key, std::move(value));
647 }
648 }
649
650 template <typename T>
651 requires Detail::HasFromVal<T>
652 void convertFromVal(Nui::val const& val, T& value)
653 {
654 from_val(val, value);
655 }
656
657#if __POINTER_WIDTH__ == 64
658 inline void convertFromVal(Nui::val const& val, long long& value)
659 {
660 value = val.as<long long>();
661 }
662 inline void convertFromVal(Nui::val const& val, unsigned long long& value)
663 {
664 value = val.as<unsigned long long>();
665 }
666#endif
667}
ModificationProxy modify()
Can be used to make mutations to the underlying class that get commited when the returned proxy is de...
Definition observed_value.hpp:356
ContainedT & value()
Definition observed_value.hpp:372
Definition observed_value.hpp:1283
Concept detecting if a from_val function is provided for a type.
Definition val_conversion.hpp:50
Concept detecting if a to_val function is provided for a type.
Definition val_conversion.hpp:42
Definition concepts.hpp:11
void convertFromValObjImpl(Nui::val const &val, T &obj)
Definition val_conversion.hpp:547
static constexpr auto extractJsonMember(nlohmann::json const &json) -> decltype(auto)
Definition rpc_hub.hpp:29
Definition file_dialog.hpp:6
Nui::val convertToVal(T const &obj)
Converts a boost described class/struct to a Nui::val object.
Definition val_conversion.hpp:402
void convertFromVal(Nui::val const &val, T &obj)
Converts a Nui::val to a class/struct object.
Definition val_conversion.hpp:534
emscripten::val val
Definition val.hpp:5
Definition val_conversion.hpp:31