mc_rtc  2.16.0
DataStore.h
Go to the documentation of this file.
1 /*
2  * Copyright 2015-2020 CNRS-UM LIRMM, CNRS-AIST JRL
3  */
4 
5 #pragma once
6 
7 #include <mc_rtc/logging.h>
8 #include <mc_rtc/type_name.h>
9 #include <mc_rtc/utils_api.h>
10 
11 #include <functional>
12 #include <memory>
13 #include <stdexcept>
14 #include <unordered_map>
15 #include <vector>
16 
17 #include <Eigen/Core>
18 
19 namespace mc_rtc
20 {
21 
22 namespace internal
23 {
24 template<typename T>
25 bool is_valid_hash(std::size_t h)
26 { return h == typeid(T).hash_code(); }
27 
28 template<typename T, typename U, typename... Args>
29 bool is_valid_hash(std::size_t h)
30 { return is_valid_hash<T>(h) || is_valid_hash<U, Args...>(h); }
31 
32 template<typename T>
33 bool is_valid_name(const std::string & name)
34 { return name == type_name<T>(); }
35 
36 template<typename T, typename U, typename... Args>
37 bool is_valid_name(const std::string & name)
38 { return is_valid_name<T>(name) || is_valid_name<U, Args...>(name); }
39 
41 template<typename T>
42 struct lambda_traits : public lambda_traits<decltype(&T::operator())>
43 {
44 };
45 
47 template<typename C, typename RetT, typename... Args>
48 struct lambda_traits<RetT (C::*)(Args...) const>
49 {
50  using fn_t = std::function<RetT(Args...)>;
51 };
52 
54 template<typename C, typename RetT, typename... Args>
55 struct lambda_traits<RetT (C::*)(Args...)>
56 {
57  using fn_t = std::function<RetT(Args...)>;
58 };
59 
66 template<typename T>
67 struct args_t
68 {
69  using decay_t = typename std::decay<T>::type;
71  using type = typename std::conditional<is_arithmetic, decay_t, T>::type;
72 };
73 
75 template<typename T, typename = void>
76 struct Allocator : public std::allocator<T>
77 {
78 };
79 
81 template<typename T>
82 struct Allocator<T, typename T::eigen_aligned_operator_new_marker_type> : public Eigen::aligned_allocator<T>
83 {
84 };
85 
86 } // namespace internal
87 
124 struct DataStore
125 {
126  DataStore() = default;
127  DataStore(const DataStore &) = delete;
128  DataStore & operator=(const DataStore &) = delete;
129  DataStore(DataStore &&) = default;
130  DataStore & operator=(DataStore &&) = default;
131 
139  inline bool has(const std::string & name) const noexcept { return datas_.find(name) != datas_.end(); }
140 
144  inline std::vector<std::string> keys() const noexcept
145  {
146  std::vector<std::string> out;
147  out.reserve(datas_.size());
148  for(const auto & d : datas_) { out.push_back(d.first); }
149  return out;
150  }
151 
162  template<typename T>
163  T & get(const std::string & name)
164  { return const_cast<T &>(get_<T>(name)); }
165 
167  template<typename T>
168  const T & get(const std::string & name) const
169  { return get_<T>(name); }
170 
178  template<typename T>
179  void get(const std::string & name, T & data)
180  {
181  auto it = datas_.find(name);
182  if(it != datas_.end()) { data = safe_cast<T>(it->second, name); }
183  }
184 
194  template<typename T>
195  const T & get(const std::string & name, const T & defaultValue) const
196  {
197  auto it = datas_.find(name);
198  if(it != datas_.end()) { return safe_cast<T>(it->second, name); }
199  return defaultValue;
200  }
201 
210  template<typename T>
211  void assign(const std::string & name, const T & data)
212  { get<T>(name) = data; }
213 
227  template<typename T, typename... ArgsT, typename... Args>
228  T & make(const std::string & name, Args &&... args)
229  {
230  auto & data = datas_[name];
231  if(data.buffer) { log::error_and_throw("[{}] An object named {} already exists on the datastore.", name_, name); }
232  data.allocate<T>(name_, name);
233  new(data.buffer.get()) T(std::forward<Args>(args)...);
234  return data.setup<T, ArgsT...>();
235  }
236 
247  template<typename T>
248  auto make_call(const std::string & name, T fn) -> typename internal::lambda_traits<T>::fn_t &
249  {
250  using fn_t = typename internal::lambda_traits<T>::fn_t;
251  return make<fn_t>(name, fn_t(fn));
252  }
253 
262  template<typename T, typename... ArgsT, typename... Args>
263  T & make_initializer(const std::string & name, Args &&... args)
264  {
265  auto & data = datas_[name];
266  if(data.buffer) { log::error_and_throw("[{}] An object named {} already exists on the datastore.", name_, name); }
267  data.allocate<T>(name_, name);
268  new(data.buffer.get()) T{std::forward<Args>(args)...};
269  return data.setup<T, ArgsT...>();
270  }
271 
289  template<typename RetT,
290  typename... FuncArgsT,
291  typename... ArgsT,
292  typename std::enable_if<sizeof...(FuncArgsT) == sizeof...(ArgsT)
293  && !std::is_same<std::tuple<FuncArgsT...>, std::tuple<ArgsT...>>::value,
294  int>::type = 0>
295  RetT call(const std::string & name, ArgsT &&... args) const
296  { return safe_call<RetT, FuncArgsT...>(name, std::forward<ArgsT>(args)...); }
297 
316  template<typename RetT = void, typename... ArgsT>
317  RetT call(const std::string & name, ArgsT &&... args) const
318  { return safe_call<RetT, typename internal::args_t<ArgsT>::type...>(name, std::forward<ArgsT>(args)...); }
319 
324  inline void remove(const std::string & name) noexcept
325  {
326  auto it = datas_.find(name);
327  if(it == datas_.end())
328  {
329  log::error("[{}] Failed to remove element \"{}\" (element does not exist)", name_, name);
330  return;
331  }
332  datas_.erase(it);
333  }
334 
338  inline void clear() noexcept { datas_.clear(); }
339 
343  inline const std::string & name() const noexcept { return name_; }
344 
350  inline void name(const std::string & name) noexcept { name_ = name; }
351 
352 private:
353  struct Data
354  {
355  Data() = default;
356  Data(const Data &) = delete;
357  Data & operator=(const Data &) = delete;
358  Data(Data &&) = default;
359  Data & operator=(Data &&) = default;
360 
362  std::unique_ptr<uint8_t[]> buffer;
364  std::string (*type)();
366  bool (*same)(std::size_t);
368  bool (*same_name)(const std::string &);
370  void (*destroy)(Data &);
372  ~Data()
373  {
374  if(buffer) { destroy(*this); }
375  }
376 
377  template<typename T>
378  void allocate(const std::string & name_, const std::string & name)
379  {
380  if(buffer) { log::error_and_throw("[{}] An object named {} already exists on the datastore.", name_, name); }
381  buffer.reset(reinterpret_cast<uint8_t *>(internal::Allocator<T>().allocate(1)));
382  }
383 
384  template<typename T, typename... ArgsT>
385  T & setup()
386  {
387  this->type = &type_name<T>;
388  this->same = &internal::is_valid_hash<T, ArgsT...>;
389  this->same_name = &internal::is_valid_name<T, ArgsT...>;
390  this->destroy = [](Data & self)
391  {
392  T * p = reinterpret_cast<T *>(self.buffer.release());
393  p->~T();
394  internal::Allocator<T>().deallocate(p, 1);
395  };
396  return *(reinterpret_cast<T *>(buffer.get()));
397  }
398  };
399 
400  template<typename T>
401  const T & safe_cast(const Data & data, const std::string & name) const
402  {
403  if(!data.same(typeid(T).hash_code()) && !data.same_name(type_name<T>()))
404  {
406  "[{} Object for key \"{}\" does not have the same type as the stored type. Stored {} but requested {}.",
407  name_, name, data.type(), type_name<T>());
408  }
409  return *(reinterpret_cast<T *>(data.buffer.get()));
410  }
411 
412  template<typename RetT, typename... FuncArgsT, typename... ArgsT>
413  RetT safe_call(const std::string & name, ArgsT &&... args) const
414  {
415  const auto & data = get_data(name);
416  using fn_t = std::function<RetT(FuncArgsT...)>;
417  if(!data.same(typeid(fn_t).hash_code()) && !data.same_name(type_name<fn_t>()))
418  {
419  log::error_and_throw("[{}] Function for key \"{}\" does not have the same signature as the "
420  "requested one. Stored {} but requested {}",
421  name_, name, data.type(), type_name<fn_t>());
422  }
423  auto & fn = *(reinterpret_cast<fn_t *>(data.buffer.get()));
424  return fn(std::forward<ArgsT>(args)...);
425  }
426 
427  template<typename T>
428  const T & get_(const std::string & name) const
429  { return safe_cast<T>(get_data(name), name); }
430 
431  inline const Data & get_data(const std::string & name) const
432  {
433  const auto it = datas_.find(name);
434  if(it == datas_.end()) { log::error_and_throw("[{}] No key \"{}\"", name_, name); }
435  return it->second;
436  }
437 
438 private:
439  std::unordered_map<std::string, Data> datas_;
440  std::string name_ = "DataStore";
441 };
442 
443 } // namespace mc_rtc
std::optional< Eigen::VectorXd > value
bool is_valid_hash(std::size_t h)
Definition: DataStore.h:25
bool is_valid_name(const std::string &name)
Definition: DataStore.h:33
void error_and_throw(const S &format, Args &&... args)
Definition: logging.h:40
void error(const S &format, Args &&... args)
Definition: logging.h:65
Definition: Contact.h:88
Generic data store.
Definition: DataStore.h:125
DataStore & operator=(const DataStore &)=delete
DataStore()=default
DataStore(const DataStore &)=delete
bool has(const std::string &name) const noexcept
Checks whether an object is in the datastore.
Definition: DataStore.h:139
void remove(const std::string &name) noexcept
Removes an object from the datastore.
Definition: DataStore.h:324
std::vector< std::string > keys() const noexcept
Returns all objects in the datastore.
Definition: DataStore.h:144
void assign(const std::string &name, const T &data)
Copies an object to an existing datastore object.
Definition: DataStore.h:211
void get(const std::string &name, T &data)
Assign value from the datastore if it exists, leave value unchanged otherwise.
Definition: DataStore.h:179
const T & get(const std::string &name) const
const variant of get
Definition: DataStore.h:168
RetT call(const std::string &name, ArgsT &&... args) const
Calls a function that was registered in the datastore and returns this call result.
Definition: DataStore.h:295
const T & get(const std::string &name, const T &defaultValue) const
Gets a value from the datastore if it exists or a default value otherwise.
Definition: DataStore.h:195
void clear() noexcept
Remove all entries in the datastore.
Definition: DataStore.h:338
DataStore & operator=(DataStore &&)=default
T & make(const std::string &name, Args &&... args)
Creates an object on the datastore and returns a reference to it.
Definition: DataStore.h:228
DataStore(DataStore &&)=default
RetT call(const std::string &name, ArgsT &&... args) const
Calls a function that was registered in the datastore and returns this call result.
Definition: DataStore.h:317
T & make_initializer(const std::string &name, Args &&... args)
Creates an object on the datastore using list initialization and returns a reference to it.
Definition: DataStore.h:263
void name(const std::string &name) noexcept
Sets this datastore's name.
Definition: DataStore.h:350
auto make_call(const std::string &name, T fn) -> typename internal::lambda_traits< T >::fn_t &
Creates an object on the datastore and returns a reference to it.
Definition: DataStore.h:248
T & get(const std::string &name)
Get a reference to an object on the datastore.
Definition: DataStore.h:163
const std::string & name() const noexcept
Name of this datastore.
Definition: DataStore.h:343
Definition: DataStore.h:77
Definition: DataStore.h:68
typename std::conditional< is_arithmetic, decay_t, T >::type type
Definition: DataStore.h:71
typename std::decay< T >::type decay_t
Definition: DataStore.h:69
static constexpr bool is_arithmetic
Definition: DataStore.h:70
std::function< RetT(Args...)> fn_t
Definition: DataStore.h:50
std::function< RetT(Args...)> fn_t
Definition: DataStore.h:57
Definition: DataStore.h:43