Ginkgo  Generated from pipelines/2837190956 branch based on develop. Ginkgo version 2.0.0
A numerical linear algebra library targeting many-core architectures
polymorphic_object.hpp
1 // SPDX-FileCopyrightText: 2017 - 2026 The Ginkgo authors
2 //
3 // SPDX-License-Identifier: BSD-3-Clause
4 
5 #ifndef GKO_PUBLIC_CORE_BASE_POLYMORPHIC_OBJECT_HPP_
6 #define GKO_PUBLIC_CORE_BASE_POLYMORPHIC_OBJECT_HPP_
7 
8 
9 #include <memory>
10 #include <type_traits>
11 
12 #include <ginkgo/core/base/executor.hpp>
13 #include <ginkgo/core/base/utils.hpp>
14 #include <ginkgo/core/log/logger.hpp>
15 
16 
17 namespace gko {
18 namespace experimental {
19 namespace distributed {
20 
21 
22 class DistributedBase;
23 
24 
25 } // namespace distributed
26 } // namespace experimental
27 
28 
46 class PolymorphicObject : public log::EnableLogging<PolymorphicObject> {
47 public:
48  virtual ~PolymorphicObject()
49  {
50  this->template log<log::Logger::polymorphic_object_deleted>(exec_.get(),
51  this);
52  }
53 
59  virtual void validate_data() const {}
60 
61  // preserve the executor of the object
62  PolymorphicObject& operator=(const PolymorphicObject&) { return *this; }
63 
69  std::shared_ptr<const Executor> get_executor() const noexcept
70  {
71  return exec_;
72  }
73 
74 protected:
75  // This method is defined as protected since a polymorphic object should not
76  // be created using their constructor directly, but by creating an
77  // std::unique_ptr to it. Defining the constructor as protected keeps these
78  // access rights when inheriting the constructor.
84  explicit PolymorphicObject(std::shared_ptr<const Executor> exec)
85  : exec_{std::move(exec)}
86  {}
87 
88  // preserve the executor of the object
89  explicit PolymorphicObject(const PolymorphicObject& other)
90  {
91  *this = other;
92  }
93 
94 private:
95  std::shared_ptr<const Executor> exec_;
96 };
97 
98 
107 #define GKO_ENABLE_SELF(_type) \
108  _type* self() noexcept { return static_cast<_type*>(this); } \
109  \
110  const _type* self() const noexcept \
111  { \
112  return static_cast<const _type*>(this); \
113  }
114 
115 
146 template <typename ResultType>
148 public:
149  using result_type = ResultType;
150 
151  virtual ~ConvertibleTo() = default;
152 
158  virtual void convert_to(result_type* result) const = 0;
159 
160  void convert_to(ptr_param<result_type> result) const
161  {
162  convert_to(result.get());
163  }
164 
179  virtual void move_to(result_type* result) = 0;
180 
181  void move_to(ptr_param<result_type> result) { move_to(result.get()); }
182 };
183 
184 
185 namespace detail {
186 
187 
188 template <typename R, typename T>
189 std::unique_ptr<R, std::function<void(R*)>> copy_and_convert_to_impl(
190  std::shared_ptr<const Executor> exec, T* obj)
191 {
192  auto obj_as_r = dynamic_cast<R*>(obj);
193  if (obj_as_r != nullptr && obj->get_executor() == exec) {
194  // FIXME: this breaks lifetimes
195  return {obj_as_r, [](R*) {}};
196  } else {
197  auto copy = R::create(exec);
198  as<ConvertibleTo<std::decay_t<R>>>(obj)->convert_to(copy);
199  return {copy.release(), std::default_delete<R>{}};
200  }
201 }
202 
203 
204 template <typename R, typename T>
205 std::shared_ptr<R> copy_and_convert_to_impl(
206  std::shared_ptr<const Executor> exec, std::shared_ptr<T> obj)
207 {
208  auto obj_as_r = std::dynamic_pointer_cast<R>(obj);
209  if (obj_as_r != nullptr && obj->get_executor() == exec) {
210  return obj_as_r;
211  } else {
212  auto copy = R::create(exec);
213  as<ConvertibleTo<std::decay_t<R>>>(obj.get())->convert_to(copy);
214  return {std::move(copy)};
215  }
216 }
217 
218 
219 } // namespace detail
220 
221 
238 template <typename R, typename T>
239 std::unique_ptr<R, std::function<void(R*)>> copy_and_convert_to(
240  std::shared_ptr<const Executor> exec, T* obj)
241 {
242  return detail::copy_and_convert_to_impl<R>(std::move(exec), obj);
243 }
244 
245 
252 template <typename R, typename T>
253 std::unique_ptr<const R, std::function<void(const R*)>> copy_and_convert_to(
254  std::shared_ptr<const Executor> exec, const T* obj)
255 {
256  return detail::copy_and_convert_to_impl<const R>(std::move(exec), obj);
257 }
258 
259 
277 template <typename R, typename T>
278 std::shared_ptr<R> copy_and_convert_to(std::shared_ptr<const Executor> exec,
279  std::shared_ptr<T> obj)
280 {
281  return detail::copy_and_convert_to_impl<R>(std::move(exec), obj);
282 }
283 
284 
292 template <typename R, typename T>
293 std::shared_ptr<const R> copy_and_convert_to(
294  std::shared_ptr<const Executor> exec, std::shared_ptr<const T> obj)
295 {
296  return detail::copy_and_convert_to_impl<const R>(std::move(exec), obj);
297 }
298 
299 
305 class Cloneable {
306 public:
307  virtual ~Cloneable() = default;
308 
309  [[nodiscard]] std::unique_ptr<Cloneable> clone(
310  std::shared_ptr<const Executor> exec) const
311  {
312  return this->clone_impl(std::move(exec));
313  }
314 
315  [[nodiscard]] std::unique_ptr<Cloneable> clone() const
316  {
317  return this->clone_impl();
318  }
319 
320  Cloneable* copy_from(ptr_param<const Cloneable> other)
321  {
322  auto copied = this->copy_from_impl(other.get());
323  return copied;
324  }
325 
326  Cloneable* move_from(ptr_param<Cloneable> other)
327  {
328  auto moved = this->move_from_impl(other.get());
329  return moved;
330  }
331 
332  [[nodiscard]] std::unique_ptr<Cloneable> create_default(
333  std::shared_ptr<const Executor> exec) const
334  {
335  return this->create_default_impl(std::move(exec));
336  }
337 
338  [[nodiscard]] std::unique_ptr<Cloneable> create_default() const
339  {
340  return this->create_default_impl();
341  }
342 
343 protected:
344  virtual Cloneable* copy_from_impl(const Cloneable* other) = 0;
345 
346  virtual Cloneable* copy_from_impl(std::unique_ptr<Cloneable> other) = 0;
347 
348  virtual Cloneable* move_from_impl(Cloneable* other) = 0;
349 
350  virtual Cloneable* move_from_impl(std::unique_ptr<Cloneable> other) = 0;
351 
352  [[nodiscard]] virtual std::unique_ptr<Cloneable> clone_impl(
353  std::shared_ptr<const Executor> exec) const = 0;
354 
355  [[nodiscard]] virtual std::unique_ptr<Cloneable> clone_impl() const = 0;
356 
357  [[nodiscard]] virtual std::unique_ptr<Cloneable> create_default_impl()
358  const = 0;
359 
360  [[nodiscard]] virtual std::unique_ptr<Cloneable> create_default_impl(
361  std::shared_ptr<const Executor> exec) const = 0;
362 };
363 
364 
375 template <typename ConcreteType>
376 class EnableCloneable : public ConvertibleTo<ConcreteType>, public Cloneable {
377 public:
378  using result_type = ConcreteType;
381 
392  [[nodiscard]] std::unique_ptr<ConcreteType> clone(
393  std::shared_ptr<const Executor> exec) const
394  {
395  return as<ConcreteType>(this->clone_impl(std::move(exec)));
396  }
397 
406  [[nodiscard]] std::unique_ptr<ConcreteType> clone() const
407  {
408  return as<ConcreteType>(this->clone_impl());
409  }
410 
411  void convert_to(result_type* result) const override { *result = *self(); }
412 
413  void move_to(result_type* result) override { *result = std::move(*self()); }
414 
415  [[nodiscard]] std::unique_ptr<ConcreteType> create_default() const
416  {
417  return as<ConcreteType>(this->create_default_impl());
418  }
419 
420  [[nodiscard]] std::unique_ptr<ConcreteType> create_default(
421  std::shared_ptr<const Executor> exec) const
422  {
423  return as<ConcreteType>(this->create_default_impl(std::move(exec)));
424  }
425 
426 protected:
427  Cloneable* copy_from_impl(const Cloneable* other) override
428  {
429  self()->template log<log::Logger::polymorphic_object_copy_started>(
430  self()->get_executor().get(),
431  dynamic_cast<const PolymorphicObject*>(other),
432  dynamic_cast<const PolymorphicObject*>(this));
433  as<ConvertibleTo<ConcreteType>>(other)->convert_to(self());
434  self()->template log<log::Logger::polymorphic_object_copy_completed>(
435  self()->get_executor().get(),
436  dynamic_cast<const PolymorphicObject*>(other),
437  dynamic_cast<const PolymorphicObject*>(this));
438  return this;
439  }
440 
441  Cloneable* copy_from_impl(std::unique_ptr<Cloneable> other) override
442  {
443  self()->template log<log::Logger::polymorphic_object_copy_started>(
444  self()->get_executor().get(),
445  dynamic_cast<const PolymorphicObject*>(other.get()),
446  dynamic_cast<const PolymorphicObject*>(this));
447  as<ConvertibleTo<ConcreteType>>(other.get())->convert_to(self());
448  self()->template log<log::Logger::polymorphic_object_copy_completed>(
449  self()->get_executor().get(),
450  dynamic_cast<const PolymorphicObject*>(other.get()),
451  dynamic_cast<const PolymorphicObject*>(this));
452  return this;
453  }
454 
455  Cloneable* move_from_impl(Cloneable* other) override
456  {
457  self()->template log<log::Logger::polymorphic_object_move_started>(
458  self()->get_executor().get(),
459  dynamic_cast<const PolymorphicObject*>(other),
460  dynamic_cast<const PolymorphicObject*>(this));
461  as<ConvertibleTo<ConcreteType>>(other)->move_to(self());
462  self()->template log<log::Logger::polymorphic_object_move_completed>(
463  self()->get_executor().get(),
464  dynamic_cast<const PolymorphicObject*>(other),
465  dynamic_cast<const PolymorphicObject*>(this));
466  return this;
467  }
468 
469  Cloneable* move_from_impl(std::unique_ptr<Cloneable> other) override
470  {
471  self()->template log<log::Logger::polymorphic_object_move_started>(
472  self()->get_executor().get(),
473  dynamic_cast<const PolymorphicObject*>(other.get()),
474  dynamic_cast<const PolymorphicObject*>(this));
475  as<ConvertibleTo<ConcreteType>>(other.get())->move_to(self());
476  self()->template log<log::Logger::polymorphic_object_move_completed>(
477  self()->get_executor().get(),
478  dynamic_cast<const PolymorphicObject*>(other.get()),
479  dynamic_cast<const PolymorphicObject*>(this));
480  return this;
481  }
482 
483  [[nodiscard]] std::unique_ptr<Cloneable> clone_impl(
484  std::shared_ptr<const Executor> exec) const override
485  {
486  auto new_op = self()->create_default(exec);
487  new_op->copy_from(this);
488  return new_op;
489  }
490 
491  [[nodiscard]] std::unique_ptr<Cloneable> clone_impl() const override
492  {
493  return this->clone_impl(self()->get_executor());
494  }
495 
496  [[nodiscard]] std::unique_ptr<Cloneable> create_default_impl()
497  const override
498  {
499  return this->create_default_impl(self()->get_executor());
500  }
501 
502  [[nodiscard]] std::unique_ptr<Cloneable> create_default_impl(
503  std::shared_ptr<const Executor> exec) const override
504  {
505  if constexpr (std::is_base_of_v<
506  experimental::distributed::DistributedBase,
507  ConcreteType>) {
508  return std::unique_ptr<ConcreteType>(
509  new ConcreteType(std::move(exec), self()->get_communicator()));
510  } else {
511  return std::unique_ptr<ConcreteType>(
512  new ConcreteType(std::move(exec)));
513  }
514  }
515 
516  GKO_ENABLE_SELF(ConcreteType);
517 };
518 
519 
528 template <typename ConcreteType>
530 public:
531  template <typename... Args>
532  static std::unique_ptr<ConcreteType> create(Args&&... args)
533  {
534  return std::unique_ptr<ConcreteType>(
535  new ConcreteType(std::forward<Args>(args)...));
536  }
537 };
538 
539 
540 } // namespace gko
541 
542 
543 #endif // GKO_PUBLIC_CORE_BASE_POLYMORPHIC_OBJECT_HPP_
gko::PolymorphicObject::validate_data
virtual void validate_data() const
Throws gko::InvalidData exception if we found the data inside the object does not fulfill certain pro...
Definition: polymorphic_object.hpp:59
gko::EnableCreateMethod
This mixin implements a static create() method on ConcreteType that dynamically allocates the memory,...
Definition: polymorphic_object.hpp:529
gko::PolymorphicObject
A PolymorphicObject is the abstract base for all "heavy" objects in Ginkgo that behave polymorphicall...
Definition: polymorphic_object.hpp:46
gko::EnableCloneable::clone
std::unique_ptr< ConcreteType > clone() const
Creates a clone of the object.
Definition: polymorphic_object.hpp:406
gko::copy_and_convert_to
std::unique_ptr< R, std::function< void(R *)> > copy_and_convert_to(std::shared_ptr< const Executor > exec, T *obj)
Converts the object to R and places it on Executor exec.
Definition: polymorphic_object.hpp:239
gko::ptr_param::get
T * get() const
Definition: utils_helper.hpp:77
gko
The Ginkgo namespace.
Definition: abstract_factory.hpp:19
gko::log::EnableLogging
EnableLogging is a mixin which should be inherited by any class which wants to enable logging.
Definition: logger.hpp:790
gko::ptr_param
This class is used for function parameters in the place of raw pointers.
Definition: utils_helper.hpp:43
gko::EnableCloneable::convert_to
void convert_to(result_type *result) const override
Converts the implementer to an object of type result_type.
Definition: polymorphic_object.hpp:411
gko::Cloneable
Interface for objects that can be cloned.
Definition: polymorphic_object.hpp:305
gko::EnableCloneable
This mixin is used to enable a default Cloneable::clone() implementation and similar for objects that...
Definition: polymorphic_object.hpp:376
gko::ConvertibleTo
ConvertibleTo interface is used to mark that the implementer can be converted to the object of Result...
Definition: polymorphic_object.hpp:147
gko::experimental::distributed::DistributedBase
A base class for distributed objects.
Definition: base.hpp:32
gko::EnableCloneable::move_to
void move_to(result_type *result) override
Converts the implementer to an object of type result_type by moving data from this object.
Definition: polymorphic_object.hpp:413
gko::PolymorphicObject::get_executor
std::shared_ptr< const Executor > get_executor() const noexcept
Returns the Executor of the object.
Definition: polymorphic_object.hpp:69
gko::EnableCloneable::clone
std::unique_ptr< ConcreteType > clone(std::shared_ptr< const Executor > exec) const
Creates a clone of the object.
Definition: polymorphic_object.hpp:392