mc_rtc  2.14.0
Schema.h
Go to the documentation of this file.
1 #pragma once
2 
3 #include <mc_rtc/Configuration.h>
4 #include <mc_rtc/Default.h>
5 #include <mc_rtc/gui/Form.h>
7 
8 namespace mc_rtc::schema
9 {
10 
11 namespace details
12 {
13 
15 template<auto ptr>
17 {
18 };
19 
21 template<bool HasChoices>
22 struct Choices
23 {
24  Choices() = default;
25  Choices(const std::vector<std::string> & choices) : choices(choices) {}
26  std::vector<std::string> choices;
27 };
28 
29 template<>
30 struct Choices<false>
31 {
32  Choices() = default;
33 };
34 
36 struct is_schema
37 {
38  template<typename T>
39  static auto test(T *) -> typename T::is_schema_t;
40 
41  template<typename T>
42  static std::false_type test(...);
43 };
44 
45 template<typename T>
46 inline constexpr bool is_schema_v = decltype(is_schema::test<T>(nullptr))::value;
47 
49 template<typename T>
50 struct is_std_vector : std::false_type
51 {
52 };
53 
54 template<typename T, typename Allocator>
55 struct is_std_vector<std::vector<T, Allocator>> : std::true_type
56 {
57 };
58 
59 template<typename T>
60 inline constexpr bool is_std_vector_v = is_std_vector<T>::value;
61 
63 template<typename T>
64 inline constexpr bool is_std_vector_schema_v = []()
65 {
66  if constexpr(is_std_vector_v<T>) { return is_schema_v<typename T::value_type>; }
67  else
68  {
69  return false;
70  }
71 }();
72 
74 template<typename T>
75 struct is_std_map : std::false_type
76 {
77 };
78 
79 template<typename T>
80 struct is_std_map<std::map<std::string, T>> : std::true_type
81 {
82 };
83 
84 template<typename T>
85 inline constexpr bool is_std_map_v = is_std_map<T>::value;
86 
88 template<typename T>
89 inline constexpr bool is_std_map_schema_v = []()
90 {
91  if constexpr(is_std_map_v<T>) { return is_schema_v<typename T::value_type>; }
92  else
93  {
94  return false;
95  }
96 }();
97 
99 template<typename T>
100 struct is_eigen_vector : public std::false_type
101 {
102 };
103 
104 template<typename Scalar, int Rows, int Options, int MaxRows>
105 struct is_eigen_vector<Eigen::Matrix<Scalar, Rows, 1, Options, MaxRows, 1>> : public std::true_type
106 {
107 };
108 
109 template<typename T>
111 
113 template<typename T>
114 struct is_eigen_matrixxd : public std::false_type
115 {
116 };
117 
118 template<>
119 struct is_eigen_matrixxd<Eigen::MatrixXd> : public std::true_type
120 {
121 };
122 
123 template<typename T>
125 
126 template<typename T, bool IsRequired, bool IsInteractive, bool HasChoices = false, bool IsStatic = false>
127 void addValueToForm(const T & value,
128  const std::string & description,
129  const details::Choices<HasChoices> & choices,
131 
132 template<bool IsRequired, bool IsInteractive, bool HasChoices, typename... Args>
133 void variantToForm(const std::variant<Args...> &,
135  const Choices<HasChoices> & choices)
136 {
137  auto get_choice = [&](size_t idx)
138  {
139  if constexpr(HasChoices)
140  {
141  if(idx < choices.choices.size()) { return choices.choices[idx]; }
142  return std::to_string(idx);
143  }
144  else
145  {
146  return std::to_string(idx);
147  }
148  };
149  size_t i = 0;
150  (addValueToForm<Args, IsRequired, IsInteractive, false, true>(Default<Args>::value, get_choice(i++), {}, form), ...);
151 }
152 
153 template<typename T, bool IsRequired, bool IsInteractive, bool HasChoices, bool IsStatic>
154 void addValueToForm(const T & value,
155  const std::string & description,
156  const details::Choices<HasChoices> & choices,
158 {
159  const auto & get_value = [&value]() -> decltype(auto)
160  {
161  if constexpr(IsStatic) { return value; }
162  else
163  {
164  return [&value]() -> const T & { return value; };
165  }
166  }();
167  if constexpr(details::is_schema_v<T>)
168  {
169  mc_rtc::gui::FormObjectInput input(description, IsRequired);
170  value.buildForm(input);
171  form.addElement(input);
172  }
173  else if constexpr(details::is_std_vector_v<T>)
174  {
175  mc_rtc::gui::FormGenericArrayInput input(description, IsRequired, get_value);
176  using value_type = typename T::value_type;
177  static value_type default_{};
178  addValueToForm<value_type, true, IsInteractive>(default_, description, {}, input);
179  form.addElement(input);
180  }
181  else if constexpr(details::is_std_map_v<T>)
182  {
183  // We currently do not support map in forms
184  }
185  else if constexpr(gui::details::is_variant_v<T>)
186  {
187  auto input = mc_rtc::gui::FormOneOfInput(description, IsRequired, get_value);
188  variantToForm<IsRequired, IsInteractive>(value, input, choices);
189  form.addElement(input);
190  }
191  else if constexpr(std::is_same_v<T, bool>)
192  {
193  form.addElement(mc_rtc::gui::FormCheckbox(description, IsRequired, get_value));
194  }
195  else if constexpr(std::is_integral_v<T>)
196  {
197  form.addElement(mc_rtc::gui::FormIntegerInput(description, IsRequired, get_value));
198  }
199  else if constexpr(std::is_floating_point_v<T>)
200  {
201  form.addElement(mc_rtc::gui::FormNumberInput(description, IsRequired, get_value));
202  }
203  else if constexpr(std::is_same_v<T, std::string>)
204  {
205  if constexpr(HasChoices)
206  {
207  auto it = std::find(choices.choices.begin(), choices.choices.end(), value);
208  long int idx = it != choices.choices.end() ? std::distance(choices.choices.begin(), it) : -1;
209  form.addElement(
210  mc_rtc::gui::FormComboInput(description, IsRequired, choices.choices, false, static_cast<int>(idx)));
211  }
212  else
213  {
214  form.addElement(mc_rtc::gui::FormStringInput(description, IsRequired, get_value));
215  }
216  }
217  else if constexpr(std::is_same_v<T, Eigen::Vector3d>)
218  {
219  form.addElement(mc_rtc::gui::FormPoint3DInput(description, IsRequired, get_value, IsInteractive));
220  }
221  else if constexpr(std::is_same_v<T, sva::PTransformd>)
222  {
223  form.addElement(mc_rtc::gui::FormTransformInput(description, IsRequired, get_value, IsInteractive));
224  }
225  else if constexpr(std::is_same_v<T, sva::ForceVecd> || details::is_eigen_vector_v<T>)
226  {
227  form.addElement(mc_rtc::gui::FormArrayInput(description, IsRequired, get_value));
228  }
229  else if constexpr(details::is_eigen_matrixxd_v<T>)
230  {
231  // do nothing, we don't support MatrixXd in forms for now but allow MatrixXd in schemas
232  }
233  else
234  {
235  static_assert(!std::is_same_v<T, T>, "addValueToForm must be implemented for this value type");
236  }
237 }
238 
239 } // namespace details
240 
242 enum class ValueFlag
243 {
245  None = 0,
247  Required = 2 << 0,
249  Interactive = 2 << 1,
252 };
253 
254 inline constexpr ValueFlag operator|(ValueFlag lhs, ValueFlag rhs) noexcept
255 {
256  using int_t = std::underlying_type_t<ValueFlag>;
257  return static_cast<ValueFlag>(static_cast<int_t>(lhs) | static_cast<int_t>(rhs));
258 }
259 
260 inline constexpr ValueFlag operator&(ValueFlag lhs, ValueFlag rhs) noexcept
261 {
262  using int_t = std::underlying_type_t<ValueFlag>;
263  return static_cast<ValueFlag>(static_cast<int_t>(lhs) & static_cast<int_t>(rhs));
264 }
265 
266 inline constexpr bool HasFeature(ValueFlag flag, ValueFlag feature) noexcept
267 {
268  using int_t = std::underlying_type_t<ValueFlag>;
269  return (static_cast<int_t>(flag) & static_cast<int_t>(feature)) != 0;
270 }
271 
287 {
289  size_t values_count = 0;
290 
292  std::function<void(const void * self, Configuration & out)> save = [](const void *, Configuration &) {};
293 
295  std::function<void(const void * self, MessagePackBuilder & builder)> write = [](const void *,
296  MessagePackBuilder &) {};
297 
299  std::function<void(void * self, const Configuration & in)> load = [](void *, const Configuration &) {};
300 
302 
304  std::function<void(const void * self, FormElements & form)> buildForm = [](const void *, FormElements &) {};
305 
307  std::function<void(const Configuration & in, Configuration & out)> formToStd = [](const Configuration &,
308  Configuration &) {};
309 
311  std::function<bool(const void * lhs, const void * rhs)> areEqual = [](const void *, const void *) { return true; };
312 
333  template<typename T, typename Schema, T Schema::* ptr, ValueFlag Flags = ValueFlag::All, bool HasChoices = false>
335  const std::string & name,
336  const std::string & description,
337  const std::integral_constant<ValueFlag, Flags> & = {},
338  const details::Choices<HasChoices> & choices = {})
339  {
340  values_count += 1;
341  save = [save = save, name](const void * self, mc_rtc::Configuration & out)
342  {
343  save(self, out);
344  const T & value = static_cast<const Schema *>(self)->*ptr;
345  out.add(name, value);
346  };
347  write = [write = write, description](const void * self, mc_rtc::MessagePackBuilder & builder)
348  {
349  write(self, builder);
350  const T & value = static_cast<const Schema *>(self)->*ptr;
351  builder.write(description);
352  builder.write(value);
353  };
354  load = [load = load, name](void * self, const mc_rtc::Configuration & in)
355  {
356  constexpr bool IsRequired = HasFeature(Flags, ValueFlag::Required);
357  load(self, in);
358  T & value = static_cast<Schema *>(self)->*ptr;
359  if(in.has(name)) { value = in(name).operator T(); }
360  else if constexpr(IsRequired) { mc_rtc::log::error_and_throw("{} is required", name); }
361  };
362  formToStd = [formToStd = formToStd, name, description](const Configuration & in, Configuration & out)
363  {
364  constexpr bool IsRequired = HasFeature(Flags, ValueFlag::Required);
365  formToStd(in, out);
366  if(IsRequired || in.has(description))
367  {
368  if constexpr(details::is_schema_v<T>)
369  {
370  auto out_ = out.add(name);
371  T::formToStd(in(description), out_);
372  }
373  else if constexpr(details::is_std_vector_schema_v<T>)
374  {
375  using SchemaT = typename T::value_type;
376  std::vector<Configuration> in_ = in(description);
377  auto out_ = out.array(name, in_.size());
378  for(size_t i = 0; i < in_.size(); ++i)
379  {
380  auto out_i = out_.object();
381  SchemaT::formToStd(in_[i], out_i);
382  }
383  }
384  else if constexpr(details::is_std_map_schema_v<T>) {}
385  else
386  {
387  out.add(name, in(description));
388  }
389  }
390  };
391  buildForm = [buildForm = buildForm, description, choices](const void * self, Operations::FormElements & form)
392  {
393  constexpr bool IsRequired = HasFeature(Flags, ValueFlag::Required);
394  constexpr bool IsInteractive = HasFeature(Flags, ValueFlag::Interactive);
395  buildForm(self, form);
396  const T & value = static_cast<const Schema *>(self)->*ptr;
397  details::addValueToForm<T, IsRequired, IsInteractive>(value, description, choices, form);
398  };
399  areEqual = [areEqual = areEqual](const void * lhs, const void * rhs)
400  {
401  const T & lhs_value = static_cast<const Schema *>(lhs)->*ptr;
402  const T & rhs_value = static_cast<const Schema *>(rhs)->*ptr;
403  return areEqual(lhs, rhs) && (lhs_value == rhs_value);
404  };
405  return true;
406  }
407 };
408 
409 namespace details
410 {
411 
416 template<typename T, ValueFlag Flags = ValueFlag::All, bool HasChoices = false>
417 const T & get_default(const T & default_,
418  const std::integral_constant<ValueFlag, Flags> & = {},
419  const details::Choices<HasChoices> & choices = {})
420 {
421  constexpr bool IsRequired = HasFeature(Flags, ValueFlag::Required);
422  if constexpr(IsRequired && HasChoices && std::is_same_v<T, std::string>)
423  {
424  if(choices.choices.size()) { return choices.choices[0]; }
425  }
426  return default_;
427 }
428 
431 {
434 };
435 
436 } // namespace details
437 
439 static inline constexpr ValueFlag Required = ValueFlag::Required;
440 
441 static inline constexpr ValueFlag Interactive = ValueFlag::Interactive;
442 
443 static inline constexpr ValueFlag None = ValueFlag::None;
444 
446 
447 } // namespace mc_rtc::schema
448 
449 namespace mc_rtc
450 {
451 template<typename T>
452 struct Default<T, std::enable_if_t<schema::details::is_schema_v<T>>>
453 {
454  inline static const T value = {};
455 };
456 
457 template<typename T>
458 struct Default<T, std::enable_if_t<schema::details::is_std_vector_v<T>>>
459 {
460  inline static const T value = {};
461 };
462 
463 template<typename T>
464 struct Default<T, typename std::enable_if_t<schema::details::is_std_map_v<T>>>
465 {
466  inline static const T value = {};
467 };
468 } // namespace mc_rtc
469 
470 #include <mc_rtc/SchemaMacros.h>
std::conditional< std::is_same< std::string, T >::value, const std::string &, std::string >::type to_string(const T &value)
Definition: RobotLoader.h:51
auto Schema(const std::string &name, const std::string &schema, Callback cb)
Definition: Schema.h:48
details::FormArrayInput< T > FormArrayInput(const std::string &name, bool required, bool fixed_size=false)
Definition: Form.h:388
void error_and_throw(Args &&... args)
Definition: logging.h:47
constexpr bool is_std_map_schema_v
Definition: Schema.h:89
constexpr bool is_std_vector_schema_v
Definition: Schema.h:64
void variantToForm(const std::variant< Args... > &, gui::details::FormElements &form, const Choices< HasChoices > &choices)
Definition: Schema.h:133
constexpr bool is_eigen_matrixxd_v
Definition: Schema.h:124
constexpr bool is_std_vector_v
Definition: Schema.h:60
constexpr bool is_eigen_vector_v
Definition: Schema.h:110
const T & get_default(const T &default_, const std::integral_constant< ValueFlag, Flags > &={}, const details::Choices< HasChoices > &choices={})
Definition: Schema.h:417
constexpr bool is_schema_v
Definition: Schema.h:46
void addValueToForm(const T &value, const std::string &description, const details::Choices< HasChoices > &choices, gui::details::FormElements &form)
Definition: Schema.h:154
constexpr bool is_std_map_v
Definition: Schema.h:85
Definition: Schema.h:9
constexpr bool HasFeature(ValueFlag flag, ValueFlag feature) noexcept
Definition: Schema.h:266
constexpr ValueFlag operator|(ValueFlag lhs, ValueFlag rhs) noexcept
Definition: Schema.h:254
ValueFlag
Definition: Schema.h:243
constexpr ValueFlag operator&(ValueFlag lhs, ValueFlag rhs) noexcept
Definition: Schema.h:260
Definition: Contact.h:88
struct MC_RTC_UTILS_DLLAPI Configuration
Definition: Configuration.h:46
MC_RBDYN_DLLAPI double distance(CD_Pair &pair, Eigen::Vector3d &p1, Eigen::Vector3d &p2)
Definition: Contact.h:67
Simplify access to values hold within a JSON file.
Definition: Configuration.h:166
Definition: Default.h:20
Definition: MessagePackBuilder.h:87
Definition: Form.h:412
Definition: Form.h:473
Definition: Form.h:542
void addElement(T &&element)
Definition: Form.h:31
Definition: Schema.h:287
gui::details::FormElements FormElements
Definition: Schema.h:301
std::function< void(const Configuration &in, Configuration &out)> formToStd
Definition: Schema.h:307
std::function< void(const void *self, FormElements &form)> buildForm
Definition: Schema.h:304
bool registerValue(details::MemberPointerWrapper< ptr >, const std::string &name, const std::string &description, const std::integral_constant< ValueFlag, Flags > &={}, const details::Choices< HasChoices > &choices={})
Definition: Schema.h:334
std::function< void(void *self, const Configuration &in)> load
Definition: Schema.h:299
std::function< bool(const void *lhs, const void *rhs)> areEqual
Definition: Schema.h:311
size_t values_count
Definition: Schema.h:289
std::function< void(const void *self, MessagePackBuilder &builder)> write
Definition: Schema.h:295
std::function< void(const void *self, Configuration &out)> save
Definition: Schema.h:292
Definition: Schema.h:23
std::vector< std::string > choices
Definition: Schema.h:26
Choices(const std::vector< std::string > &choices)
Definition: Schema.h:25
Definition: Schema.h:431
static mc_rtc::schema::Operations ops_
Definition: Schema.h:433
Definition: Schema.h:37
static std::false_type test(...)
static auto test(T *) -> typename T::is_schema_t