Nui
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 <memory>
21 #include <map>
22 #include <variant>
23 
24 namespace Nui
25 {
26  namespace Detail
27  {
28  template <typename T>
29  struct IsOptional : std::false_type
30  {};
31  template <typename T>
32  struct IsOptional<std::optional<T>> : std::true_type
33  {};
34  }
35 
36  template <
37  typename T,
38  class Bases = boost::describe::describe_bases<T, boost::describe::mod_any_access>,
39  class Members = boost::describe::describe_members<T, boost::describe::mod_any_access>,
40  class Enable = std::enable_if_t<!std::is_union<T>::value>>
41  void convertToVal(Nui::val& val, T const& obj);
42  template <typename T>
43  Nui::val convertToVal(std::optional<T> const& option);
44  template <typename T>
45  requires Fundamental<T>
46  Nui::val convertToVal(T const& value);
47  Nui::val convertToVal(std::string const& value);
48  Nui::val convertToVal(std::filesystem::path const& value);
50  Nui::val convertToVal(char const* value);
51  template <typename T>
52  Nui::val convertToVal(std::vector<T> const& vector);
53  template <typename T>
54  Nui::val convertToVal(Observed<T> const& observed);
55  template <typename T>
56  Nui::val convertToVal(std::unordered_map<std::string, T> const& map);
57  template <typename T>
58  Nui::val convertToVal(std::map<std::string, T> const& map);
59  template <typename T>
60  Nui::val convertToVal(std::unique_ptr<T> const& map);
61  template <typename T>
62  Nui::val convertToVal(std::shared_ptr<T> const& map);
63  inline Nui::val convertToVal(long long)
64  {
65  throw std::runtime_error("Cannot convert long long to val");
66  }
67  inline Nui::val convertToVal(std::monostate)
68  {
69  return Nui::val::undefined();
70  }
71  template <typename... Ts>
72  Nui::val convertToVal(std::variant<Ts...> const& variant);
73 
74  template <typename T, class Members>
75  requires(!std::is_union_v<T>)
76  void convertFromValObjImpl(Nui::val const& val, T& obj);
77 
78  template <typename T, class Bases, class Members>
79  requires(!std::is_union_v<T>)
80  void convertFromVal(Nui::val const& val, T& obj);
81 
82  template <typename T, class Members>
83  requires(!std::is_union_v<T> && !boost::describe::has_describe_bases<T>::value)
84  void convertFromVal(Nui::val const& val, T& obj);
85 
86  template <typename T>
87  requires Fundamental<T>
88  void convertFromVal(Nui::val const& val, T& value);
89  void convertFromVal(Nui::val const& val, std::string& str);
90  template <typename T>
91  void convertFromVal(Nui::val const& val, std::optional<T>& option);
92  void convertFromVal(Nui::val const& val, std::filesystem::path& value);
93  void convertFromVal(Nui::val const& val, Nui::val& value);
94  template <typename T>
95  void convertFromVal(Nui::val const& val, std::vector<T>& vector);
96  template <typename T>
97  requires Fundamental<T>
98  void convertFromVal(Nui::val const& val, std::vector<T>& vector);
99  template <typename T>
100  void convertFromVal(Nui::val const& val, Observed<T>& observed);
101  template <typename T>
102  void convertFromVal(Nui::val const& val, std::unordered_map<std::string, T>& map);
103  inline void convertFromVal(Nui::val const&, long long)
104  {
105  throw std::invalid_argument("Cannot convert from val to long long");
106  }
107 
108  template <typename T, class Bases, class Members, class Enable>
109  void convertToVal(Nui::val& val, T const& obj)
110  {
111  if (val.typeOf().as<std::string>() != "object")
112  val = Nui::val::object();
113 
114  boost::mp11::mp_for_each<Bases>([&](auto&& base) {
115  using type = typename std::decay_t<decltype(base)>::type;
116  convertToVal(val, static_cast<type const&>(obj));
117  });
118 
119  boost::mp11::mp_for_each<Members>([&](auto&& memAccessor) {
120  val.set(memAccessor.name, convertToVal(obj.*memAccessor.pointer));
121  });
122  }
123  template <
124  typename T,
125  class Bases = boost::describe::describe_bases<T, boost::describe::mod_any_access>,
126  class Members = boost::describe::describe_members<T, boost::describe::mod_any_access>,
127  class Enable = std::enable_if_t<!std::is_union<T>::value>>
128  Nui::val convertToVal(T const& obj)
129  {
130  Nui::val val = Nui::val::object();
131  convertToVal(val, obj);
132  return val;
133  }
134 
135  template <typename T>
136  Nui::val convertToVal(std::optional<T> const& option)
137  {
138  return option ? convertToVal(*option) : Nui::val::undefined();
139  }
140  template <typename T>
141  requires Fundamental<T>
142  Nui::val convertToVal(T const& value)
143  {
144  return Nui::val{value};
145  }
146  inline Nui::val convertToVal(std::string const& value)
147  {
148  return Nui::val{value};
149  }
150  inline Nui::val convertToVal(std::filesystem::path const& value)
151  {
152  return Nui::val{value.string()};
153  }
155  {
156  return value;
157  }
158  inline Nui::val convertToVal(char const* value)
159  {
160  return Nui::val{std::string{value}};
161  }
162  template <typename T>
163  Nui::val convertToVal(std::vector<T> const& vector)
164  {
165  Nui::val result = Nui::val::array();
166  for (auto const& element : vector)
167  result.call<void>("push", convertToVal(element));
168  return result;
169  }
170  template <typename T>
172  {
173  return convertToVal(observed.value());
174  }
175  template <typename T>
176  Nui::val convertToVal(std::unordered_map<std::string, T> const& map)
177  {
178  Nui::val result = Nui::val::object();
179  for (auto const& [key, value] : map)
180  result.set(key, convertToVal(value));
181  return result;
182  }
183  template <typename T>
184  Nui::val convertToVal(std::map<std::string, T> const& map)
185  {
186  Nui::val result = Nui::val::object();
187  for (auto const& [key, value] : map)
188  result.set(key, convertToVal(value));
189  return result;
190  }
191  template <typename T>
192  Nui::val convertToVal(std::unique_ptr<T> const& ptr)
193  {
194  if (ptr)
195  return convertToVal(*ptr);
196  else
197  return Nui::val::null();
198  }
199  template <typename T>
200  Nui::val convertToVal(std::shared_ptr<T> const& ptr)
201  {
202  if (ptr)
203  return convertToVal(*ptr);
204  else
205  return Nui::val::null();
206  }
207  template <typename... Ts>
208  Nui::val convertToVal(std::variant<Ts...> const& variant)
209  {
210  return std::visit(
211  [](auto&& value) {
212  return convertToVal(value);
213  },
214  variant);
215  }
216 
217  template <typename T, class Members = boost::describe::describe_members<T, boost::describe::mod_any_access>>
218  requires(!std::is_union_v<T> && !boost::describe::has_describe_bases<T>::value)
219  void convertFromVal(Nui::val const& val, T& obj)
220  {
221  convertFromValObjImpl<T, Members>(val, obj);
222  }
223 
224  template <
225  typename T,
226  class Bases = boost::describe::describe_bases<T, boost::describe::mod_any_access>,
227  class Members = boost::describe::describe_members<T, boost::describe::mod_any_access>>
228  requires(!std::is_union_v<T> && boost::describe::has_describe_bases<T>::value)
229  void convertFromVal(Nui::val const& val, T& obj)
230  {
231  boost::mp11::mp_for_each<Bases>([&](auto&& base) {
232  using type = typename std::decay_t<decltype(base)>::type;
233  convertFromVal(val, static_cast<type&>(obj));
234  });
235  convertFromValObjImpl<T, Members>(val, obj);
236  }
237 
238  template <typename T, class Members = boost::describe::describe_members<T, boost::describe::mod_any_access>>
239  requires(!std::is_union_v<T>)
240  void convertFromValObjImpl(Nui::val const& val, T& obj)
241  {
242  boost::mp11::mp_for_each<Members>([&](auto&& memAccessor) {
243  if (val.hasOwnProperty(memAccessor.name))
244  {
245  if constexpr (!Detail::IsOptional<decltype(obj.*memAccessor.pointer)>::value)
246  {
247  if (val[memAccessor.name].isNull() || val[memAccessor.name].isUndefined())
248  Nui::val::global("console").call<void>(
249  "error",
250  std::string{"Expected member "} + memAccessor.name + " to be defined and non null");
251  else
252  convertFromVal(val[memAccessor.name], obj.*memAccessor.pointer);
253  }
254  else
255  convertFromVal(val[memAccessor.name], obj.*memAccessor.pointer);
256  }
257  });
258  }
259 
260  template <typename T>
261  requires Fundamental<T>
262  void convertFromVal(Nui::val const& val, T& value)
263  {
264  value = val.as<T>();
265  }
266  inline void convertFromVal(Nui::val const& val, std::string& str)
267  {
268  str = val.as<std::string>();
269  }
270  template <typename T>
271  void convertFromVal(Nui::val const& val, std::optional<T>& option)
272  {
273  if (val.isNull() || val.isUndefined())
274  option = std::nullopt;
275  else
276  {
277  T value;
278  convertFromVal(val, value);
279  option = value;
280  }
281  }
282  inline void convertFromVal(Nui::val const& val, std::filesystem::path& value)
283  {
284  value = val.as<std::string>();
285  }
286  inline void convertFromVal(Nui::val const& val, Nui::val& value)
287  {
288  value = val;
289  }
290  template <typename T>
291  void convertFromVal(Nui::val const& val, std::vector<T>& vector)
292  {
293  vector.clear();
294  const auto length = val["length"].as<std::size_t>();
295  vector.reserve(length);
296  for (std::size_t i = 0; i < length; ++i)
297  {
298  T value;
299  convertFromVal(val[i], value);
300  vector.push_back(std::move(value));
301  }
302  }
303  template <typename T>
304  requires Fundamental<T>
305  void convertFromVal(Nui::val const& val, std::vector<T>& vector)
306  {
307  vector = emscripten::convertJSArrayToNumberVector<T>(val);
308  }
309  template <typename T>
310  void convertFromVal(Nui::val const& val, Observed<T>& observed)
311  {
312  auto proxy = observed.modify();
313  convertFromVal(val, proxy.value());
314  }
315  template <typename T>
316  void convertFromVal(Nui::val const& val, std::unordered_map<std::string, T>& map)
317  {
318  map.clear();
319  const auto keys = Nui::val::global("Object").call<Nui::val>("keys", val);
320  const auto length = keys["length"].as<std::size_t>();
321  for (std::size_t i = 0; i < length; ++i)
322  {
323  const auto key = keys[i].as<std::string>();
324  T value;
325  convertFromVal(val[key], value);
326  map.emplace(key, std::move(value));
327  }
328  }
329 }
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:293
ContainedT const & value() const
Definition: observed_value.hpp:313
Definition: observed_value.hpp:1202
Definition: file_dialog.hpp:6
requires(!std::is_union_v< T >) void convertFromValObjImpl(Nui requires(!std::is_union_v< T >) void convertFromVal(Nui requires(!std::is_union_v< T > &&!boost::describe::has_describe_bases< T >::value) void convertFromVal(Nui requires Fundamental< T > void convertFromVal(Nui::val const &val, T &value)
Definition: val_conversion.hpp:262
requires(IsObservedLike< ObservedValues > &&...) ObservedValueCombinator< std
Definition: observed_value_combinator.hpp:191
void convertToVal(Nui::val &val, T const &obj)
Definition: val_conversion.hpp:109
requires(!std::is_union_v< T >) void convertFromValObjImpl(Nui
Definition: val_conversion.hpp:239
requires Fundamental< T > void convertFromVal(Nui::val const &val, T &value)
Definition: val_conversion.hpp:262
emscripten::val val
Definition: val.hpp:5
Definition: val_conversion.hpp:30