include/behaviortree_cpp/contrib/any.hpp
Go to the documentation of this file.
1 //
2 // Implementation of N4562 std::experimental::any (merged into C++17) for C++11 compilers.
3 //
4 // See also:
5 // + http://en.cppreference.com/w/cpp/any
6 // + http://en.cppreference.com/w/cpp/experimental/any
7 // + http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4562.html#any
8 // + https://cplusplus.github.io/LWG/lwg-active.html#2509
9 //
10 //
11 // Copyright (c) 2016 Denilson das Mercês Amorim
12 //
13 // Distributed under the Boost Software License, Version 1.0. (See accompanying
14 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
15 //
16 #ifndef LINB_ANY_HPP
17 #define LINB_ANY_HPP
18 #pragma once
19 #include <typeinfo>
20 #include <type_traits>
21 #include <stdexcept>
22 #include <utility>
23 #include <new>
24 
25 #if defined(PARTICLE)
26 #if !defined(__cpp_exceptions) && !defined(ANY_IMPL_NO_EXCEPTIONS) && !defined(ANY_IMPL_EXCEPTIONS)
27 # define ANY_IMPL_NO_EXCEPTIONS
28 # endif
29 #else
30 // you can opt-out of exceptions by definining ANY_IMPL_NO_EXCEPTIONS,
31 // but you must ensure not to cast badly when passing an `any' object to any_cast<T>(any)
32 #endif
33 
34 #if defined(PARTICLE)
35 #if !defined(__cpp_rtti) && !defined(ANY_IMPL_NO_RTTI) && !defined(ANY_IMPL_RTTI)
36 # define ANY_IMPL_NO_RTTI
37 # endif
38 #else
39 // you can opt-out of RTTI by defining ANY_IMPL_NO_RTTI,
40 // in order to disable functions working with the typeid of a type
41 #endif
42 
43 
44 namespace linb
45 {
46 template<typename T>
48 {
49  constexpr explicit in_place_type_t() noexcept = default;
50 };
51 
52 #if defined (__cpp_variable_templates) || defined(_MSC_VER)
53 template<typename T>
55 #endif
56 
57 class bad_any_cast : public std::bad_cast
58 {
59 public:
60  const char* what() const noexcept override
61  {
62  return "bad any cast";
63  }
64 };
65 
66 class any final
67 {
68 public:
70  any() noexcept :
71  vtable(nullptr)
72  {
73  }
74 
76  any(const any& rhs) :
77  vtable(rhs.vtable)
78  {
79  if(!rhs.empty())
80  {
81  rhs.vtable->copy(rhs.storage, this->storage);
82  }
83  }
84 
87  any(any&& rhs) noexcept :
88  vtable(rhs.vtable)
89  {
90  if(!rhs.empty())
91  {
92  rhs.vtable->move(rhs.storage, this->storage);
93  rhs.vtable = nullptr;
94  }
95  }
96 
98  ~any()
99  {
100  this->clear();
101  }
102 
107  template<typename ValueType, typename = typename std::enable_if<!std::is_same<typename std::decay<ValueType>::type, any>::value>::type>
108  any(ValueType&& value)
109  {
110  static_assert(std::is_copy_constructible<typename std::decay<ValueType>::type>::value,
111  "T shall satisfy the CopyConstructible requirements.");
112  this->construct(std::forward<ValueType>(value));
113  }
114 
115  template <typename ValueType, typename... Args>
116  explicit any(in_place_type_t<ValueType>, Args&&... args)
117  {
118  this->emplace_construct<ValueType>(std::forward<Args>(args)...);
119  }
120 
121  template <typename ValueType, typename U, typename... Args>
122  explicit any(in_place_type_t<ValueType>, std::initializer_list<U> il, Args&&... args)
123  {
124  this->emplace_construct<ValueType>(il, std::forward<Args>(args)...);
125  }
127  any& operator=(const any& rhs)
128  {
129  any(rhs).swap(*this);
130  return *this;
131  }
132 
137  any& operator=(any&& rhs) noexcept
138  {
139  std::move(rhs).swap(*this);
140  return *this;
141  }
142 
147  template<typename ValueType, typename = typename std::enable_if<!std::is_same<typename std::decay<ValueType>::type, any>::value>::type>
148  any& operator=(ValueType&& value)
149  {
150  static_assert(std::is_copy_constructible<typename std::decay<ValueType>::type>::value,
151  "T shall satisfy the CopyConstructible requirements.");
152  any(std::forward<ValueType>(value)).swap(*this);
153  return *this;
154  }
155 
157  void clear() noexcept
158  {
159  if(!empty())
160  {
161  this->vtable->destroy(storage);
162  this->vtable = nullptr;
163  }
164  }
165 
167  bool empty() const noexcept
168  {
169  return this->vtable == nullptr;
170  }
171 
172 #ifndef ANY_IMPL_NO_RTTI
173  const std::type_info& type() const noexcept
175  {
176  return empty()? typeid(void) : this->vtable->type();
177  }
178 #endif
179 
181  void swap(any& rhs) noexcept
182  {
183  if(this->vtable != rhs.vtable)
184  {
185  any tmp(std::move(rhs));
186 
187  // move from *this to rhs.
188  rhs.vtable = this->vtable;
189  if(this->vtable != nullptr)
190  {
191  this->vtable->move(this->storage, rhs.storage);
192  //this->vtable = nullptr; -- unneeded, see below
193  }
194 
195  // move from tmp (previously rhs) to *this.
196  this->vtable = tmp.vtable;
197  if(tmp.vtable != nullptr)
198  {
199  tmp.vtable->move(tmp.storage, this->storage);
200  tmp.vtable = nullptr;
201  }
202  }
203  else // same types
204  {
205  if(this->vtable != nullptr)
206  this->vtable->swap(this->storage, rhs.storage);
207  }
208  }
209 
210 private: // Storage and Virtual Method Table
211 
213  {
214  using stack_storage_t = typename std::aligned_storage<2 * sizeof(void*), std::alignment_of<void*>::value>::type;
215 
216  void* dynamic;
217  stack_storage_t stack; // 2 words for e.g. shared_ptr
218  };
219 
221  struct vtable_type
222  {
223  // Note: The caller is responsible for doing .vtable = nullptr after destructful operations
224  // such as destroy() and/or move().
225 
226 #ifndef ANY_IMPL_NO_RTTI
227  const std::type_info& (*type)() noexcept;
229 #endif
230 
233  void(*destroy)(storage_union&) noexcept;
234 
237  void(*copy)(const storage_union& src, storage_union& dest);
238 
241  void(*move)(storage_union& src, storage_union& dest) noexcept;
242 
244  void(*swap)(storage_union& lhs, storage_union& rhs) noexcept;
245  };
246 
248  template<typename T>
250  {
251 #ifndef ANY_IMPL_NO_RTTI
252  static const std::type_info& type() noexcept
253  {
254  return typeid(T);
255  }
256 #endif
257 
258  static void destroy(storage_union& storage) noexcept
259  {
260  //assert(reinterpret_cast<T*>(storage.dynamic));
261  delete reinterpret_cast<T*>(storage.dynamic);
262  }
263 
264  static void copy(const storage_union& src, storage_union& dest)
265  {
266  dest.dynamic = new T(*reinterpret_cast<const T*>(src.dynamic));
267  }
268 
269  static void move(storage_union& src, storage_union& dest) noexcept
270  {
271  dest.dynamic = src.dynamic;
272  src.dynamic = nullptr;
273  }
274 
275  static void swap(storage_union& lhs, storage_union& rhs) noexcept
276  {
277  // just exchange the storage pointers.
278  std::swap(lhs.dynamic, rhs.dynamic);
279  }
280  };
281 
283  template<typename T>
285  {
286 #ifndef ANY_IMPL_NO_RTTI
287  static const std::type_info& type() noexcept
288  {
289  return typeid(T);
290  }
291 #endif
292 
293  static void destroy(storage_union& storage) noexcept
294  {
295  reinterpret_cast<T*>(&storage.stack)->~T();
296  }
297 
298  static void copy(const storage_union& src, storage_union& dest)
299  {
300  new (&dest.stack) T(reinterpret_cast<const T&>(src.stack));
301  }
302 
303  static void move(storage_union& src, storage_union& dest) noexcept
304  {
305  // one of the conditions for using vtable_stack is a nothrow move constructor,
306  // so this move constructor will never throw a exception.
307  new (&dest.stack) T(std::move(reinterpret_cast<T&>(src.stack)));
308  destroy(src);
309  }
310 
311  static void swap(storage_union& lhs, storage_union& rhs) noexcept
312  {
313  storage_union tmp_storage;
314  move(rhs, tmp_storage);
315  move(lhs, rhs);
316  move(tmp_storage, lhs);
317  }
318  };
319 
321  template<typename T>
323  std::integral_constant<bool,
324  !(std::is_nothrow_move_constructible<T>::value // N4562 §6.3/3 [any.class]
325  && sizeof(T) <= sizeof(storage_union::stack)
326  && std::alignment_of<T>::value <= std::alignment_of<storage_union::stack_storage_t>::value)>
327  {};
328 
330  template<typename T>
331  static vtable_type* vtable_for_type()
332  {
333  using VTableType = typename std::conditional<requires_allocation<T>::value, vtable_dynamic<T>, vtable_stack<T>>::type;
334  static vtable_type table = {
335 #ifndef ANY_IMPL_NO_RTTI
336  VTableType::type,
337 #endif
338  VTableType::destroy,
339  VTableType::copy, VTableType::move,
340  VTableType::swap,
341  };
342  return &table;
343  }
344 
345 protected:
346  template<typename T>
347  friend const T* any_cast(const any* operand) noexcept;
348  template<typename T>
349  friend T* any_cast(any* operand) noexcept;
350 
351 #ifndef ANY_IMPL_NO_RTTI
353  bool is_typed(const std::type_info& t) const
354  {
355  return is_same(this->type(), t);
356  }
357 #endif
358 
359 #ifndef ANY_IMPL_NO_RTTI
366  static bool is_same(const std::type_info& a, const std::type_info& b)
367  {
368 #ifdef ANY_IMPL_FAST_TYPE_INFO_COMPARE
369  return &a == &b;
370 #else
371  return a == b;
372 #endif
373  }
374 #endif
375 
377  template<typename T>
378  const T* cast() const noexcept
379  {
380  return requires_allocation<typename std::decay<T>::type>::value?
381  reinterpret_cast<const T*>(storage.dynamic) :
382  reinterpret_cast<const T*>(&storage.stack);
383  }
384 
386  template<typename T>
387  T* cast() noexcept
388  {
389  return requires_allocation<typename std::decay<T>::type>::value?
390  reinterpret_cast<T*>(storage.dynamic) :
391  reinterpret_cast<T*>(&storage.stack);
392  }
393 
394 private:
395  storage_union storage; // on offset(0) so no padding for align
396  vtable_type* vtable;
397 
398  template <typename T, typename... Args>
399  typename std::enable_if<requires_allocation<T>::value>::type do_emplace(Args&&... args)
400  {
401  storage.dynamic = new T(std::forward<Args>(args)...);
402  }
403 
404  template <typename T, typename... Args>
405  typename std::enable_if<!requires_allocation<T>::value>::type do_emplace(Args&&... args)
406  {
407  new (&storage.stack) T(std::forward<Args>(args)...);
408  }
409 
410  template <typename ValueType, typename... Args>
411  void emplace_construct(Args&&... args)
412  {
413  using T = typename std::decay<ValueType>::type;
414 
415  this->vtable = vtable_for_type<T>();
416 
417  do_emplace<T>(std::forward<Args>(args)...);
418  }
419 
420  template<typename ValueType, typename T>
421  typename std::enable_if<requires_allocation<T>::value>::type
422  do_construct(ValueType&& value)
423  {
424  storage.dynamic = new T(std::forward<ValueType>(value));
425  }
426 
427  template<typename ValueType, typename T>
429  do_construct(ValueType&& value)
430  {
431  new (&storage.stack) T(std::forward<ValueType>(value));
432  }
433 
436  template<typename ValueType>
437  void construct(ValueType&& value)
438  {
439  using T = typename std::decay<ValueType>::type;
440 
441  this->vtable = vtable_for_type<T>();
442 
443  do_construct<ValueType,T>(std::forward<ValueType>(value));
444  }
445 };
446 
447 
448 
449 namespace detail
450 {
451  template<typename ValueType>
452  inline ValueType any_cast_move_if_true(typename std::remove_reference<ValueType>::type* p, std::true_type)
453  {
454  return std::move(*p);
455  }
456 
457  template<typename ValueType>
458  inline ValueType any_cast_move_if_true(typename std::remove_reference<ValueType>::type* p, std::false_type)
459  {
460  return *p;
461  }
462 }
463 
465 template<typename ValueType>
466 inline ValueType any_cast(const any& operand)
467 {
468  auto p = any_cast<typename std::add_const<typename std::remove_reference<ValueType>::type>::type>(&operand);
469 #ifndef ANY_IMPL_NO_EXCEPTIONS
470  if(p == nullptr) throw bad_any_cast();
471 #endif
472  return *p;
473 }
474 
476 template<typename ValueType>
477 inline ValueType any_cast(any& operand)
478 {
479  auto p = any_cast<typename std::remove_reference<ValueType>::type>(&operand);
480 #ifndef ANY_IMPL_NO_EXCEPTIONS
481  if(p == nullptr) throw bad_any_cast();
482 #endif
483  return *p;
484 }
485 
491 template<typename ValueType>
492 inline ValueType any_cast(any&& operand)
493 {
494  using can_move = std::integral_constant<bool,
497 
498  auto p = any_cast<typename std::remove_reference<ValueType>::type>(&operand);
499 #ifndef ANY_IMPL_NO_EXCEPTIONS
500  if(p == nullptr) throw bad_any_cast();
501 #endif
502  return detail::any_cast_move_if_true<ValueType>(p, can_move());
503 }
504 
507 template<typename ValueType>
508 inline const ValueType* any_cast(const any* operand) noexcept
509 {
510  using T = typename std::decay<ValueType>::type;
511 
512 #ifndef ANY_IMPL_NO_RTTI
513  if (operand && operand->is_typed(typeid(T)))
514 #else
515  if (operand && operand->vtable == any::vtable_for_type<T>())
516 #endif
517  return operand->cast<ValueType>();
518  else
519  return nullptr;
520 }
521 
524 template<typename ValueType>
525 inline ValueType* any_cast(any* operand) noexcept
526 {
527  using T = typename std::decay<ValueType>::type;
528 
529 #ifndef ANY_IMPL_NO_RTTI
530  if (operand && operand->is_typed(typeid(T)))
531 #else
532  if (operand && operand->vtable == any::vtable_for_type<T>())
533 #endif
534  return operand->cast<ValueType>();
535  else
536  return nullptr;
537 }
538 
539 inline void swap(any& lhs, any& rhs) noexcept
540 {
541  lhs.swap(rhs);
542 }
543 
544 template <typename T, typename... Args>
545 any make_any(Args&&... args)
546 {
547  return any(in_place_type_t<T>{}, std::forward<Args>(args)...);
548 }
549 
550 template <typename T, typename U, typename... Args>
551 any make_any(std::initializer_list<U> il, Args&&... args)
552 {
553  return any(in_place_type_t<T>{}, il, std::forward<Args>(args)...);
554 }
555 }
556 
557 #endif
linb::any::any
any(in_place_type_t< ValueType >, Args &&... args)
Definition: include/behaviortree_cpp/contrib/any.hpp:116
linb::any_cast
ValueType any_cast(const any &operand)
Performs *any_cast<add_const_t<remove_reference_t<ValueType>>>(&operand), or throws bad_any_cast on f...
Definition: include/behaviortree_cpp/contrib/any.hpp:466
linb::any::vtable_stack::destroy
static void destroy(storage_union &storage) noexcept
Definition: include/behaviortree_cpp/contrib/any.hpp:293
BT::enable_if
typename std::enable_if< Predicate::value >::type * enable_if
Definition: basic_types.h:305
linb::any::operator=
any & operator=(const any &rhs)
Has the same effect as any(rhs).swap(*this). No effects if an exception is thrown.
Definition: include/behaviortree_cpp/contrib/any.hpp:127
linb::any::any
any(in_place_type_t< ValueType >, std::initializer_list< U > il, Args &&... args)
Definition: include/behaviortree_cpp/contrib/any.hpp:122
linb::any::vtable_stack::type
static const std::type_info & type() noexcept
Definition: include/behaviortree_cpp/contrib/any.hpp:287
linb::detail::any_cast_move_if_true
ValueType any_cast_move_if_true(typename std::remove_reference< ValueType >::type *p, std::true_type)
Definition: include/behaviortree_cpp/contrib/any.hpp:452
linb::any::storage
storage_union storage
Definition: include/behaviortree_cpp/contrib/any.hpp:395
linb::any::storage_union
Definition: include/behaviortree_cpp/contrib/any.hpp:212
linb::any::storage_union::stack_storage_t
typename std::aligned_storage< 2 *sizeof(void *), std::alignment_of< void * >::value >::type stack_storage_t
Definition: include/behaviortree_cpp/contrib/any.hpp:214
linb::any::any
any(const any &rhs)
Constructs an object of type any with an equivalent state as other.
Definition: include/behaviortree_cpp/contrib/any.hpp:76
linb::swap
void swap(any &lhs, any &rhs) noexcept
Definition: include/behaviortree_cpp/contrib/any.hpp:539
linb
Definition: include/behaviortree_cpp/contrib/any.hpp:44
linb::any::empty
bool empty() const noexcept
Returns true if *this has no contained object, otherwise false.
Definition: include/behaviortree_cpp/contrib/any.hpp:167
template
string template
linb::any::type
const std::type_info & type() const noexcept
If *this has a contained object of type T, typeid(T); otherwise typeid(void).
Definition: include/behaviortree_cpp/contrib/any.hpp:174
detail
detail namespace with internal helper functions
Definition: json.hpp:248
linb::any::vtable_stack::swap
static void swap(storage_union &lhs, storage_union &rhs) noexcept
Definition: include/behaviortree_cpp/contrib/any.hpp:311
linb::any::vtable_type::swap
void(* swap)(storage_union &lhs, storage_union &rhs) noexcept
Exchanges the storage between lhs and rhs.
Definition: include/behaviortree_cpp/contrib/any.hpp:244
linb::any::do_construct
std::enable_if< requires_allocation< T >::value >::type do_construct(ValueType &&value)
Definition: include/behaviortree_cpp/contrib/any.hpp:422
magic_enum::detail::value
constexpr E value(std::size_t i) noexcept
Definition: magic_enum.hpp:664
linb::any::construct
void construct(ValueType &&value)
Definition: include/behaviortree_cpp/contrib/any.hpp:437
detail::void
j template void())
Definition: json.hpp:4893
linb::any::vtable_dynamic::destroy
static void destroy(storage_union &storage) noexcept
Definition: include/behaviortree_cpp/contrib/any.hpp:258
linb::any::vtable_dynamic
VTable for dynamically allocated storage.
Definition: include/behaviortree_cpp/contrib/any.hpp:249
linb::any::vtable_type::destroy
void(* destroy)(storage_union &) noexcept
Definition: include/behaviortree_cpp/contrib/any.hpp:233
linb::any::any
any(ValueType &&value)
Definition: include/behaviortree_cpp/contrib/any.hpp:108
linb::any::do_construct
std::enable_if<!requires_allocation< T >::value >::type do_construct(ValueType &&value)
Definition: include/behaviortree_cpp/contrib/any.hpp:429
linb::bad_any_cast::what
const char * what() const noexcept override
Definition: include/behaviortree_cpp/contrib/any.hpp:60
linb::any::~any
~any()
Same effect as this->clear().
Definition: include/behaviortree_cpp/contrib/any.hpp:98
nonstd::in_place_type
in_place_t in_place_type(detail::in_place_type_tag< T >=detail::in_place_type_tag< T >())
Definition: expected.hpp:197
linb::any::do_emplace
std::enable_if< requires_allocation< T >::value >::type do_emplace(Args &&... args)
Definition: include/behaviortree_cpp/contrib/any.hpp:399
linb::any::vtable_type::move
void(* move)(storage_union &src, storage_union &dest) noexcept
Definition: include/behaviortree_cpp/contrib/any.hpp:241
linb::any::any
any() noexcept
Constructs an object of type any with an empty state.
Definition: include/behaviortree_cpp/contrib/any.hpp:70
linb::any::vtable_stack
VTable for stack allocated storage.
Definition: include/behaviortree_cpp/contrib/any.hpp:284
linb::any::operator=
any & operator=(ValueType &&value)
Definition: include/behaviortree_cpp/contrib/any.hpp:148
linb::bad_any_cast
Definition: include/behaviortree_cpp/contrib/any.hpp:57
linb::in_place_type_t
Definition: include/behaviortree_cpp/contrib/any.hpp:47
linb::any::vtable_stack::move
static void move(storage_union &src, storage_union &dest) noexcept
Definition: include/behaviortree_cpp/contrib/any.hpp:303
linb::any::storage_union::dynamic
void * dynamic
Definition: include/behaviortree_cpp/contrib/any.hpp:216
linb::any
Definition: include/behaviortree_cpp/contrib/any.hpp:66
linb::any::operator=
any & operator=(any &&rhs) noexcept
Definition: include/behaviortree_cpp/contrib/any.hpp:137
linb::any::vtable_type
Base VTable specification.
Definition: include/behaviortree_cpp/contrib/any.hpp:221
linb::any::swap
void swap(any &rhs) noexcept
Exchange the states of *this and rhs.
Definition: include/behaviortree_cpp/contrib/any.hpp:181
linb::any::storage_union::stack
stack_storage_t stack
Definition: include/behaviortree_cpp/contrib/any.hpp:217
linb::make_any
any make_any(Args &&... args)
Definition: include/behaviortree_cpp/contrib/any.hpp:545
linb::any::vtable_dynamic::copy
static void copy(const storage_union &src, storage_union &dest)
Definition: include/behaviortree_cpp/contrib/any.hpp:264
linb::any::vtable_for_type
static vtable_type * vtable_for_type()
Returns the pointer to the vtable of the type T.
Definition: include/behaviortree_cpp/contrib/any.hpp:331
linb::any::requires_allocation
Whether the type T must be dynamically allocated or can be stored on the stack.
Definition: include/behaviortree_cpp/contrib/any.hpp:322
linb::any::vtable
vtable_type * vtable
Definition: include/behaviortree_cpp/contrib/any.hpp:396
std::swap
NLOHMANN_BASIC_JSON_TPL_DECLARATION void swap(nlohmann::NLOHMANN_BASIC_JSON_TPL &j1, nlohmann::NLOHMANN_BASIC_JSON_TPL &j2) noexcept(//NOLINT(readability-inconsistent-declaration-parameter-name, cert-dcl58-cpp) is_nothrow_move_constructible< nlohmann::NLOHMANN_BASIC_JSON_TPL >::value &&//NOLINT(misc-redundant-expression, cppcoreguidelines-noexcept-swap, performance-noexcept-swap) is_nothrow_move_assignable< nlohmann::NLOHMANN_BASIC_JSON_TPL >::value)
exchanges the values of two JSON objects
Definition: json.hpp:24537
linb::any::clear
void clear() noexcept
If not empty, destroys the contained object.
Definition: include/behaviortree_cpp/contrib/any.hpp:157
linb::any::vtable_stack::copy
static void copy(const storage_union &src, storage_union &dest)
Definition: include/behaviortree_cpp/contrib/any.hpp:298
linb::any::vtable_type::copy
void(* copy)(const storage_union &src, storage_union &dest)
Definition: include/behaviortree_cpp/contrib/any.hpp:237
lexyd::any
constexpr auto any
Matches anything and consumes all remaining characters.
Definition: 3rdparty/lexy/include/lexy/dsl/any.hpp:42
linb::any::vtable_dynamic::swap
static void swap(storage_union &lhs, storage_union &rhs) noexcept
Definition: include/behaviortree_cpp/contrib/any.hpp:275
linb::any::vtable_type::type
const std::type_info &(* type)() noexcept
The type of the object this vtable is for.
Definition: include/behaviortree_cpp/contrib/any.hpp:228
lexyd::p
constexpr auto p
Parses the production.
Definition: production.hpp:127
linb::any::vtable_dynamic::type
static const std::type_info & type() noexcept
Definition: include/behaviortree_cpp/contrib/any.hpp:252
linb::any::any
any(any &&rhs) noexcept
Definition: include/behaviortree_cpp/contrib/any.hpp:87
linb::in_place_type_t::in_place_type_t
constexpr in_place_type_t() noexcept=default
linb::any::vtable_dynamic::move
static void move(storage_union &src, storage_union &dest) noexcept
Definition: include/behaviortree_cpp/contrib/any.hpp:269


behaviortree_cpp_v4
Author(s): Davide Faconti
autogenerated on Fri Jun 28 2024 02:20:07