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 {
27  return h == typeid(T).hash_code();
28 }
29 
30 template<typename T, typename U, typename... Args>
31 bool is_valid_hash(std::size_t h)
32 {
33  return is_valid_hash<T>(h) || is_valid_hash<U, Args...>(h);
34 }
35 
36 template<typename T>
37 bool is_valid_name(const std::string & name)
38 {
39  return name == type_name<T>();
40 }
41 
42 template<typename T, typename U, typename... Args>
43 bool is_valid_name(const std::string & name)
44 {
45  return is_valid_name<T>(name) || is_valid_name<U, Args...>(name);
46 }
47 
49 template<typename T>
50 struct lambda_traits : public lambda_traits<decltype(&T::operator())>
51 {
52 };
53 
55 template<typename C, typename RetT, typename... Args>
56 struct lambda_traits<RetT (C::*)(Args...) const>
57 {
58  using fn_t = std::function<RetT(Args...)>;
59 };
60 
62 template<typename C, typename RetT, typename... Args>
63 struct lambda_traits<RetT (C::*)(Args...)>
64 {
65  using fn_t = std::function<RetT(Args...)>;
66 };
67 
74 template<typename T>
75 struct args_t
76 {
77  using decay_t = typename std::decay<T>::type;
78  static constexpr bool is_arithmetic = std::is_arithmetic<decay_t>::value;
79  using type = typename std::conditional<is_arithmetic, decay_t, T>::type;
80 };
81 
83 template<typename T, typename = void>
84 struct Allocator : public std::allocator<T>
85 {
86 };
87 
89 template<typename T>
90 struct Allocator<T, typename T::eigen_aligned_operator_new_marker_type> : public Eigen::aligned_allocator<T>
91 {
92 };
93 
94 } // namespace internal
95 
132 struct DataStore
133 {
134  DataStore() = default;
135  DataStore(const DataStore &) = delete;
136  DataStore & operator=(const DataStore &) = delete;
137  DataStore(DataStore &&) = default;
138  DataStore & operator=(DataStore &&) = default;
139 
147  inline bool has(const std::string & name) const noexcept { return datas_.find(name) != datas_.end(); }
148 
152  inline std::vector<std::string> keys() const noexcept
153  {
154  std::vector<std::string> out;
155  out.reserve(datas_.size());
156  for(const auto & d : datas_) { out.push_back(d.first); }
157  return out;
158  }
159 
170  template<typename T>
171  T & get(const std::string & name)
172  {
173  return const_cast<T &>(get_<T>(name));
174  }
175 
177  template<typename T>
178  const T & get(const std::string & name) const
179  {
180  return get_<T>(name);
181  }
182 
190  template<typename T>
191  void get(const std::string & name, T & data)
192  {
193  auto it = datas_.find(name);
194  if(it != datas_.end()) { data = safe_cast<T>(it->second, name); }
195  }
196 
206  template<typename T>
207  const T & get(const std::string & name, const T & defaultValue) const
208  {
209  auto it = datas_.find(name);
210  if(it != datas_.end()) { return safe_cast<T>(it->second, name); }
211  return defaultValue;
212  }
213 
222  template<typename T>
223  void assign(const std::string & name, const T & data)
224  {
225  get<T>(name) = data;
226  }
227 
241  template<typename T, typename... ArgsT, typename... Args>
242  T & make(const std::string & name, Args &&... args)
243  {
244  auto & data = datas_[name];
245  if(data.buffer) { log::error_and_throw("[{}] An object named {} already exists on the datastore.", name_, name); }
246  data.allocate<T>(name_, name);
247  new(data.buffer.get()) T(std::forward<Args>(args)...);
248  return data.setup<T, ArgsT...>();
249  }
250 
261  template<typename T>
262  auto make_call(const std::string & name, T fn) -> typename internal::lambda_traits<T>::fn_t &
263  {
264  using fn_t = typename internal::lambda_traits<T>::fn_t;
265  return make<fn_t>(name, fn_t(fn));
266  }
267 
276  template<typename T, typename... ArgsT, typename... Args>
277  T & make_initializer(const std::string & name, Args &&... args)
278  {
279  auto & data = datas_[name];
280  if(data.buffer) { log::error_and_throw("[{}] An object named {} already exists on the datastore.", name_, name); }
281  data.allocate<T>(name_, name);
282  new(data.buffer.get()) T{std::forward<Args>(args)...};
283  return data.setup<T, ArgsT...>();
284  }
285 
303  template<typename RetT,
304  typename... FuncArgsT,
305  typename... ArgsT,
306  typename std::enable_if<sizeof...(FuncArgsT) == sizeof...(ArgsT)
307  && !std::is_same<std::tuple<FuncArgsT...>, std::tuple<ArgsT...>>::value,
308  int>::type = 0>
309  RetT call(const std::string & name, ArgsT &&... args) const
310  {
311  return safe_call<RetT, FuncArgsT...>(name, std::forward<ArgsT>(args)...);
312  }
313 
332  template<typename RetT = void, typename... ArgsT>
333  RetT call(const std::string & name, ArgsT &&... args) const
334  {
335  return safe_call<RetT, typename internal::args_t<ArgsT>::type...>(name, std::forward<ArgsT>(args)...);
336  }
337 
342  inline void remove(const std::string & name) noexcept
343  {
344  auto it = datas_.find(name);
345  if(it == datas_.end())
346  {
347  log::error("[{}] Failed to remove element \"{}\" (element does not exist)", name_, name);
348  return;
349  }
350  datas_.erase(it);
351  }
352 
356  inline void clear() noexcept { datas_.clear(); }
357 
361  inline const std::string & name() const noexcept { return name_; }
362 
368  inline void name(const std::string & name) noexcept { name_ = name; }
369 
370 private:
371  struct Data
372  {
373  Data() = default;
374  Data(const Data &) = delete;
375  Data & operator=(const Data &) = delete;
376  Data(Data &&) = default;
377  Data & operator=(Data &&) = default;
378 
380  std::unique_ptr<uint8_t[]> buffer;
382  std::string (*type)();
384  bool (*same)(std::size_t);
386  bool (*same_name)(const std::string &);
388  void (*destroy)(Data &);
390  ~Data()
391  {
392  if(buffer) { destroy(*this); }
393  }
394 
395  template<typename T>
396  void allocate(const std::string & name_, const std::string & name)
397  {
398  if(buffer) { log::error_and_throw("[{}] An object named {} already exists on the datastore.", name_, name); }
399  buffer.reset(reinterpret_cast<uint8_t *>(internal::Allocator<T>().allocate(1)));
400  }
401 
402  template<typename T, typename... ArgsT>
403  T & setup()
404  {
405  this->type = &type_name<T>;
406  this->same = &internal::is_valid_hash<T, ArgsT...>;
407  this->same_name = &internal::is_valid_name<T, ArgsT...>;
408  this->destroy = [](Data & self)
409  {
410  T * p = reinterpret_cast<T *>(self.buffer.release());
411  p->~T();
412  internal::Allocator<T>().deallocate(p, 1);
413  };
414  return *(reinterpret_cast<T *>(buffer.get()));
415  }
416  };
417 
418  template<typename T>
419  const T & safe_cast(const Data & data, const std::string & name) const
420  {
421  if(!data.same(typeid(T).hash_code()) && !data.same_name(type_name<T>()))
422  {
424  "[{} Object for key \"{}\" does not have the same type as the stored type. Stored {} but requested {}.",
425  name_, name, data.type(), type_name<T>());
426  }
427  return *(reinterpret_cast<T *>(data.buffer.get()));
428  }
429 
430  template<typename RetT, typename... FuncArgsT, typename... ArgsT>
431  RetT safe_call(const std::string & name, ArgsT &&... args) const
432  {
433  const auto & data = get_data(name);
434  using fn_t = std::function<RetT(FuncArgsT...)>;
435  if(!data.same(typeid(fn_t).hash_code()) && !data.same_name(type_name<fn_t>()))
436  {
437  log::error_and_throw("[{}] Function for key \"{}\" does not have the same signature as the "
438  "requested one. Stored {} but requested {}",
439  name_, name, data.type(), type_name<fn_t>());
440  }
441  auto & fn = *(reinterpret_cast<fn_t *>(data.buffer.get()));
442  return fn(std::forward<ArgsT>(args)...);
443  }
444 
445  template<typename T>
446  const T & get_(const std::string & name) const
447  {
448  return safe_cast<T>(get_data(name), name);
449  }
450 
451  inline const Data & get_data(const std::string & name) const
452  {
453  const auto it = datas_.find(name);
454  if(it == datas_.end()) { log::error_and_throw("[{}] No key \"{}\"", name_, name); }
455  return it->second;
456  }
457 
458 private:
459  std::unordered_map<std::string, Data> datas_;
460  std::string name_ = "DataStore";
461 };
462 
463 } // namespace mc_rtc
mc_rtc::DataStore::call
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:309
mc_rtc::DataStore::has
bool has(const std::string &name) const noexcept
Checks whether an object is in the datastore.
Definition: DataStore.h:147
mc_rtc::DataStore::clear
void clear() noexcept
Remove all entries in the datastore.
Definition: DataStore.h:356
mc_rtc::internal::is_valid_name
bool is_valid_name(const std::string &name)
Definition: DataStore.h:37
mc_rtc::internal::lambda_traits< RetT(C::*)(Args...)>::fn_t
std::function< RetT(Args...)> fn_t
Definition: DataStore.h:65
mc_rtc::DataStore::make_initializer
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:277
type_name.h
mc_rtc::internal::args_t::decay_t
typename std::decay< T >::type decay_t
Definition: DataStore.h:77
mc_rtc::DataStore::make_call
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:262
mc_rtc::DataStore::get
const T & get(const std::string &name) const
const variant of get
Definition: DataStore.h:178
mc_rtc::DataStore
Generic data store.
Definition: DataStore.h:132
mc_rtc::internal::lambda_traits< RetT(C::*)(Args...) const >::fn_t
std::function< RetT(Args...)> fn_t
Definition: DataStore.h:58
mc_rtc::internal::args_t::type
typename std::conditional< is_arithmetic, decay_t, T >::type type
Definition: DataStore.h:79
mc_rtc::DataStore::make
T & make(const std::string &name, Args &&... args)
Creates an object on the datastore and returns a reference to it.
Definition: DataStore.h:242
mc_rtc::internal::args_t::is_arithmetic
static constexpr bool is_arithmetic
Definition: DataStore.h:78
mc_rtc::DataStore::keys
std::vector< std::string > keys() const noexcept
Returns all objects in the datastore.
Definition: DataStore.h:152
mc_rtc::internal::lambda_traits
Definition: DataStore.h:50
mc_rtc::DataStore::assign
void assign(const std::string &name, const T &data)
Copies an object to an existing datastore object.
Definition: DataStore.h:223
mc_rtc::internal::args_t
Definition: DataStore.h:75
mc_rtc::DataStore::get
T & get(const std::string &name)
Get a reference to an object on the datastore.
Definition: DataStore.h:171
mc_rtc::DataStore::call
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:333
mc_rtc::log::error_and_throw
void error_and_throw(Args &&... args)
Definition: logging.h:47
utils_api.h
mc_rtc::DataStore::operator=
DataStore & operator=(const DataStore &)=delete
mc_rtc::DataStore::get
void get(const std::string &name, T &data)
Assign value from the datastore if it exists, leave value unchanged otherwise.
Definition: DataStore.h:191
mc_rtc::DataStore::remove
void remove(const std::string &name) noexcept
Removes an object from the datastore.
Definition: DataStore.h:342
mc_rtc::DataStore::name
void name(const std::string &name) noexcept
Sets this datastore's name.
Definition: DataStore.h:368
mc_rtc::DataStore::DataStore
DataStore()=default
logging.h
mc_rtc::log::error
void error(Args &&... args)
Definition: logging.h:63
mc_rtc::DataStore::name
const std::string & name() const noexcept
Name of this datastore.
Definition: DataStore.h:361
mc_rtc::internal::Allocator
Definition: DataStore.h:84
mc_rtc::DataStore::get
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:207
mc_rtc::internal::is_valid_hash
bool is_valid_hash(std::size_t h)
Definition: DataStore.h:25
mc_rtc
Definition: Contact.h:87