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 { return false; }
68 }();
69 
71 template<typename T>
72 struct is_std_map : std::false_type
73 {
74 };
75 
76 template<typename T>
77 struct is_std_map<std::map<std::string, T>> : std::true_type
78 {
79 };
80 
81 template<typename T>
82 inline constexpr bool is_std_map_v = is_std_map<T>::value;
83 
85 template<typename T>
86 inline constexpr bool is_std_map_schema_v = []()
87 {
88  if constexpr(is_std_map_v<T>) { return is_schema_v<typename T::value_type>; }
89  else { return false; }
90 }();
91 
93 template<typename T>
94 struct is_eigen_vector : public std::false_type
95 {
96 };
97 
98 template<typename Scalar, int Rows, int Options, int MaxRows>
99 struct is_eigen_vector<Eigen::Matrix<Scalar, Rows, 1, Options, MaxRows, 1>> : public std::true_type
100 {
101 };
102 
103 template<typename T>
105 
106 template<typename T, bool IsRequired, bool IsInteractive, bool HasChoices = false, bool IsStatic = false>
107 void addValueToForm(const T & value,
108  const std::string & description,
109  const details::Choices<HasChoices> & choices,
111 
112 template<bool IsRequired, bool IsInteractive, bool HasChoices, typename... Args>
113 void variantToForm(const std::variant<Args...> &,
115  const Choices<HasChoices> & choices)
116 {
117  auto get_choice = [&](size_t idx)
118  {
119  if constexpr(HasChoices)
120  {
121  if(idx < choices.choices.size()) { return choices.choices[idx]; }
122  return std::to_string(idx);
123  }
124  else { return std::to_string(idx); }
125  };
126  size_t i = 0;
127  (addValueToForm<Args, IsRequired, IsInteractive, false, true>(Default<Args>::value, get_choice(i++), {}, form), ...);
128 }
129 
130 template<typename T, bool IsRequired, bool IsInteractive, bool HasChoices, bool IsStatic>
131 void addValueToForm(const T & value,
132  const std::string & description,
133  const details::Choices<HasChoices> & choices,
135 {
136  const auto & get_value = [&value]() -> decltype(auto)
137  {
138  if constexpr(IsStatic) { return value; }
139  else
140  {
141  return [&value]() -> const T & { return value; };
142  }
143  }();
144  if constexpr(details::is_schema_v<T>)
145  {
146  mc_rtc::gui::FormObjectInput input(description, IsRequired);
147  value.buildForm(input);
148  form.addElement(input);
149  }
150  else if constexpr(details::is_std_vector_v<T>)
151  {
152  mc_rtc::gui::FormGenericArrayInput input(description, IsRequired, get_value);
153  using value_type = typename T::value_type;
154  static value_type default_{};
155  addValueToForm<value_type, true, IsInteractive>(default_, description, {}, input);
156  form.addElement(input);
157  }
158  else if constexpr(details::is_std_map_v<T>)
159  {
160  // We currently do not support map in forms
161  }
162  else if constexpr(gui::details::is_variant_v<T>)
163  {
164  auto input = mc_rtc::gui::FormOneOfInput(description, IsRequired, get_value);
165  variantToForm<IsRequired, IsInteractive>(value, input, choices);
166  form.addElement(input);
167  }
168  else if constexpr(std::is_same_v<T, bool>)
169  {
170  form.addElement(mc_rtc::gui::FormCheckbox(description, IsRequired, get_value));
171  }
172  else if constexpr(std::is_integral_v<T>)
173  {
174  form.addElement(mc_rtc::gui::FormIntegerInput(description, IsRequired, get_value));
175  }
176  else if constexpr(std::is_floating_point_v<T>)
177  {
178  form.addElement(mc_rtc::gui::FormNumberInput(description, IsRequired, get_value));
179  }
180  else if constexpr(std::is_same_v<T, std::string>)
181  {
182  if constexpr(HasChoices)
183  {
184  auto it = std::find(choices.choices.begin(), choices.choices.end(), value);
185  long int idx = it != choices.choices.end() ? std::distance(choices.choices.begin(), it) : -1;
186  form.addElement(
187  mc_rtc::gui::FormComboInput(description, IsRequired, choices.choices, false, static_cast<int>(idx)));
188  }
189  else { form.addElement(mc_rtc::gui::FormStringInput(description, IsRequired, get_value)); }
190  }
191  else if constexpr(std::is_same_v<T, Eigen::Vector3d>)
192  {
193  form.addElement(mc_rtc::gui::FormPoint3DInput(description, IsRequired, get_value, IsInteractive));
194  }
195  else if constexpr(std::is_same_v<T, sva::PTransformd>)
196  {
197  form.addElement(mc_rtc::gui::FormTransformInput(description, IsRequired, get_value, IsInteractive));
198  }
199  else if constexpr(std::is_same_v<T, sva::ForceVecd> || details::is_eigen_vector_v<T>)
200  {
201  form.addElement(mc_rtc::gui::FormArrayInput(description, IsRequired, get_value));
202  }
203  else { static_assert(!std::is_same_v<T, T>, "addValueToForm must be implemented for this value type"); }
204 }
205 
206 } // namespace details
207 
209 enum class ValueFlag
210 {
212  None = 0,
214  Required = 2 << 0,
216  Interactive = 2 << 1,
219 };
220 
221 inline constexpr ValueFlag operator|(ValueFlag lhs, ValueFlag rhs) noexcept
222 {
223  using int_t = std::underlying_type_t<ValueFlag>;
224  return static_cast<ValueFlag>(static_cast<int_t>(lhs) | static_cast<int_t>(rhs));
225 }
226 
227 inline constexpr ValueFlag operator&(ValueFlag lhs, ValueFlag rhs) noexcept
228 {
229  using int_t = std::underlying_type_t<ValueFlag>;
230  return static_cast<ValueFlag>(static_cast<int_t>(lhs) & static_cast<int_t>(rhs));
231 }
232 
233 inline constexpr bool HasFeature(ValueFlag flag, ValueFlag feature) noexcept
234 {
235  using int_t = std::underlying_type_t<ValueFlag>;
236  return (static_cast<int_t>(flag) & static_cast<int_t>(feature)) != 0;
237 }
238 
254 {
256  size_t values_count = 0;
257 
259  std::function<void(const void * self, Configuration & out)> save = [](const void *, Configuration &) {};
260 
262  std::function<void(const void * self, MessagePackBuilder & builder)> write = [](const void *, MessagePackBuilder &) {
263  };
264 
266  std::function<void(void * self, const Configuration & in)> load = [](void *, const Configuration &) {};
267 
269 
271  std::function<void(const void * self, FormElements & form)> buildForm = [](const void *, FormElements &) {};
272 
274  std::function<void(const Configuration & in, Configuration & out)> formToStd = [](const Configuration &,
275  Configuration &) {};
276 
278  std::function<bool(const void * lhs, const void * rhs)> areEqual = [](const void *, const void *) { return true; };
279 
300  template<typename T, typename Schema, T Schema::*ptr, ValueFlag Flags = ValueFlag::All, bool HasChoices = false>
302  const std::string & name,
303  const std::string & description,
304  const std::integral_constant<ValueFlag, Flags> & = {},
305  const details::Choices<HasChoices> & choices = {})
306  {
307  values_count += 1;
308  save = [save = save, name](const void * self, mc_rtc::Configuration & out)
309  {
310  save(self, out);
311  const T & value = static_cast<const Schema *>(self)->*ptr;
312  out.add(name, value);
313  };
314  write = [write = write, description](const void * self, mc_rtc::MessagePackBuilder & builder)
315  {
316  write(self, builder);
317  const T & value = static_cast<const Schema *>(self)->*ptr;
318  builder.write(description);
319  builder.write(value);
320  };
321  load = [load = load, name](void * self, const mc_rtc::Configuration & in)
322  {
323  constexpr bool IsRequired = HasFeature(Flags, ValueFlag::Required);
324  load(self, in);
325  T & value = static_cast<Schema *>(self)->*ptr;
326  if(in.has(name)) { value = in(name).operator T(); }
327  else if constexpr(IsRequired) { mc_rtc::log::error_and_throw("{} is required"); }
328  };
329  formToStd = [formToStd = formToStd, name, description](const Configuration & in, Configuration & out)
330  {
331  constexpr bool IsRequired = HasFeature(Flags, ValueFlag::Required);
332  formToStd(in, out);
333  if(IsRequired || in.has(description))
334  {
335  if constexpr(details::is_schema_v<T>)
336  {
337  auto out_ = out.add(name);
338  T::formToStd(in(description), out_);
339  }
340  else if constexpr(details::is_std_vector_schema_v<T>)
341  {
342  using SchemaT = typename T::value_type;
343  std::vector<Configuration> in_ = in(description);
344  auto out_ = out.array(name, in_.size());
345  for(size_t i = 0; i < in_.size(); ++i)
346  {
347  auto out_i = out_.object();
348  SchemaT::formToStd(in_[i], out_i);
349  }
350  }
351  else if constexpr(details::is_std_map_schema_v<T>) {}
352  else { out.add(name, in(description)); }
353  }
354  };
355  buildForm = [buildForm = buildForm, description, choices](const void * self, Operations::FormElements & form)
356  {
357  constexpr bool IsRequired = HasFeature(Flags, ValueFlag::Required);
358  constexpr bool IsInteractive = HasFeature(Flags, ValueFlag::Interactive);
359  buildForm(self, form);
360  const T & value = static_cast<const Schema *>(self)->*ptr;
361  details::addValueToForm<T, IsRequired, IsInteractive>(value, description, choices, form);
362  };
363  areEqual = [areEqual = areEqual](const void * lhs, const void * rhs)
364  {
365  const T & lhs_value = static_cast<const Schema *>(lhs)->*ptr;
366  const T & rhs_value = static_cast<const Schema *>(rhs)->*ptr;
367  return areEqual(lhs, rhs) && (lhs_value == rhs_value);
368  };
369  return true;
370  }
371 };
372 
373 namespace details
374 {
375 
380 template<typename T, ValueFlag Flags = ValueFlag::All, bool HasChoices = false>
381 const T & get_default(const T & default_,
382  const std::integral_constant<ValueFlag, Flags> & = {},
383  const details::Choices<HasChoices> & choices = {})
384 {
385  constexpr bool IsRequired = HasFeature(Flags, ValueFlag::Required);
386  if constexpr(IsRequired && HasChoices && std::is_same_v<T, std::string>)
387  {
388  if(choices.choices.size()) { return choices.choices[0]; }
389  }
390  return default_;
391 }
392 
395 {
398 };
399 
400 } // namespace details
401 
403 static inline constexpr ValueFlag Required = ValueFlag::Required;
404 
405 static inline constexpr ValueFlag Interactive = ValueFlag::Interactive;
406 
407 static inline constexpr ValueFlag None = ValueFlag::None;
408 
410 
411 } // namespace mc_rtc::schema
412 
413 namespace mc_rtc
414 {
415 template<typename T>
416 struct Default<T, std::enable_if_t<schema::details::is_schema_v<T>>>
417 {
418  inline static const T value = {};
419 };
420 
421 template<typename T>
422 struct Default<T, std::enable_if_t<schema::details::is_std_vector_v<T>>>
423 {
424  inline static const T value = {};
425 };
426 
427 template<typename T>
428 struct Default<T, typename std::enable_if_t<schema::details::is_std_map_v<T>>>
429 {
430  inline static const T value = {};
431 };
432 } // namespace mc_rtc
433 
434 #include <mc_rtc/SchemaMacros.h>
mc_rtc::Configuration
Simplify access to values hold within a JSON file.
Definition: Configuration.h:165
mc_rtc::MessagePackBuilder
Definition: MessagePackBuilder.h:86
mc_rtc::schema::Operations::buildForm
std::function< void(const void *self, FormElements &form)> buildForm
Definition: Schema.h:271
mc_rtc::gui::Schema
auto Schema(const std::string &name, const std::string &schema, Callback cb)
Definition: Schema.h:48
mc_rtc::schema::details::Choices
Definition: Schema.h:22
mc_rtc::schema::details::is_std_vector_v
constexpr bool is_std_vector_v
Definition: Schema.h:60
mc_rtc::schema::details::EmptySchema
Definition: Schema.h:394
mc_rtc::schema::details::variantToForm
void variantToForm(const std::variant< Args... > &, gui::details::FormElements &form, const Choices< HasChoices > &choices)
Definition: Schema.h:113
Form.h
mc_rtc::gui::FormArrayInput
details::FormArrayInput< T > FormArrayInput(const std::string &name, bool required, bool fixed_size=false)
Definition: Form.h:379
mc_rtc::Default
Definition: Default.h:19
mc_rtc::schema::Operations::values_count
size_t values_count
Definition: Schema.h:256
mc_rtc::schema::Operations::save
std::function< void(const void *self, Configuration &out)> save
Definition: Schema.h:259
mc_rtc::schema::details::is_std_vector_schema_v
constexpr bool is_std_vector_schema_v
Definition: Schema.h:64
mc_rtc::schema::details::is_schema_v
constexpr bool is_schema_v
Definition: Schema.h:46
mc_rtc::schema::details::is_schema::test
static auto test(T *) -> typename T::is_schema_t
mc_rtc::schema::operator|
constexpr ValueFlag operator|(ValueFlag lhs, ValueFlag rhs) noexcept
Definition: Schema.h:221
mc_rtc::schema::details::is_schema
Definition: Schema.h:36
mc_rtc::gui::details::FormElements::addElement
void addElement(T &&element)
Definition: Form.h:31
mc_rtc::schema::details::MemberPointerWrapper
Definition: Schema.h:16
StateBuilder.h
mc_rtc::gui::FormGenericArrayInput
Definition: Form.h:490
mc_rtc::schema::details::is_std_map
Definition: Schema.h:72
mc_rtc::gui::FormOneOfInput
Definition: Form.h:532
mc_rtc::schema::operator&
constexpr ValueFlag operator&(ValueFlag lhs, ValueFlag rhs) noexcept
Definition: Schema.h:227
mc_rtc::schema::details::Choices::choices
std::vector< std::string > choices
Definition: Schema.h:26
sch::mc_rbdyn::distance
MC_RBDYN_DLLAPI double distance(CD_Pair &pair, Eigen::Vector3d &p1, Eigen::Vector3d &p2)
mc_rtc::log::error_and_throw
void error_and_throw(Args &&... args)
Definition: logging.h:47
mc_rtc::schema::details::is_eigen_vector
Definition: Schema.h:94
mc_rtc::schema::Operations::areEqual
std::function< bool(const void *lhs, const void *rhs)> areEqual
Definition: Schema.h:278
mc_rtc::schema::ValueFlag
ValueFlag
Definition: Schema.h:209
mc_rtc::schema::ValueFlag::Interactive
@ Interactive
mc_rtc::schema::ValueFlag::Required
@ Required
mc_rtc::schema::Operations::write
std::function< void(const void *self, MessagePackBuilder &builder)> write
Definition: Schema.h:262
mc_rtc::schema::details::is_std_map_schema_v
constexpr bool is_std_map_schema_v
Definition: Schema.h:86
Configuration.h
mc_rtc::schema::details::addValueToForm
void addValueToForm(const T &value, const std::string &description, const details::Choices< HasChoices > &choices, gui::details::FormElements &form)
Definition: Schema.h:131
mc_rtc::schema::Operations::FormElements
gui::details::FormElements FormElements
Definition: Schema.h:268
mc_rbdyn::details::to_string
std::conditional< std::is_same< std::string, T >::value, const std::string &, std::string >::type to_string(const T &value)
Definition: RobotLoader.h:51
std
Definition: Contact.h:66
mc_rtc::schema::details::EmptySchema::ops_
static mc_rtc::schema::Operations ops_
Definition: Schema.h:397
mc_rtc::schema
Definition: Schema.h:8
mc_rtc::schema::Operations::load
std::function< void(void *self, const Configuration &in)> load
Definition: Schema.h:266
mc_rtc::schema::HasFeature
constexpr bool HasFeature(ValueFlag flag, ValueFlag feature) noexcept
Definition: Schema.h:233
mc_rtc::schema::details::is_std_vector
Definition: Schema.h:50
mc_rtc::schema::Operations
Definition: Schema.h:253
mc_rtc::Configuration
struct MC_RTC_UTILS_DLLAPI Configuration
Definition: Configuration.h:46
mc_rtc::schema::details::Choices::Choices
Choices()=default
mc_rtc::schema::details::is_std_map_v
constexpr bool is_std_map_v
Definition: Schema.h:82
mc_rtc::schema::Operations::registerValue
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:301
mc_rtc::schema::ValueFlag::None
@ None
mc_rtc::gui::details::FormElements
Definition: Form.h:19
mc_rtc::schema::Operations::formToStd
std::function< void(const Configuration &in, Configuration &out)> formToStd
Definition: Schema.h:274
mc_rtc::gui::FormComboInput
Definition: Form.h:402
mc_rtc::schema::details::Choices::Choices
Choices(const std::vector< std::string > &choices)
Definition: Schema.h:25
Default.h
mc_rtc::schema::details::is_eigen_vector_v
constexpr bool is_eigen_vector_v
Definition: Schema.h:104
mc_rtc::gui::FormObjectInput
Definition: Form.h:463
SchemaMacros.h
mc_rtc
Definition: Contact.h:87
mc_rtc::schema::details::get_default
const T & get_default(const T &default_, const std::integral_constant< ValueFlag, Flags > &={}, const details::Choices< HasChoices > &choices={})
Definition: Schema.h:381
mc_rtc::schema::ValueFlag::All
@ All