std_shared_ptr.hpp 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
  1. // Copyright (c) 2018-2025 Jean-Louis Leroy
  2. // Distributed under the Boost Software License, Version 1.0.
  3. // See accompanying file LICENSE_1_0.txt
  4. // or copy at http://www.boost.org/LICENSE_1_0.txt)
  5. #ifndef BOOST_OPENMETHOD_INTEROP_SHARED_PTR_HPP
  6. #define BOOST_OPENMETHOD_INTEROP_SHARED_PTR_HPP
  7. #include <boost/openmethod/core.hpp>
  8. #include <memory>
  9. namespace boost::openmethod {
  10. namespace detail {
  11. template<class T>
  12. struct shared_ptr_cast_traits {
  13. static_assert(false_t<T>, "cast only allowed to a shared_ptr");
  14. };
  15. template<class T>
  16. struct shared_ptr_cast_traits<std::shared_ptr<T>> {
  17. using virtual_type = T;
  18. };
  19. template<class T>
  20. struct shared_ptr_cast_traits<const std::shared_ptr<T>&> {
  21. using virtual_type = T;
  22. };
  23. template<class T>
  24. struct shared_ptr_cast_traits<std::shared_ptr<T>&&> {
  25. using virtual_type = T;
  26. };
  27. template<typename T, class Registry>
  28. struct validate_method_parameter<std::shared_ptr<T>&, Registry, void>
  29. : std::false_type {
  30. static_assert(
  31. false_t<T>,
  32. "std::shared_ptr cannot be passed by non-const lvalue reference");
  33. };
  34. template<typename T, class Registry>
  35. struct validate_method_parameter<
  36. virtual_ptr<std::shared_ptr<T>, Registry>&, Registry, void>
  37. : std::false_type {
  38. static_assert(
  39. false_t<T>,
  40. "std::shared_ptr cannot be passed by non-const lvalue reference");
  41. };
  42. } // namespace detail
  43. //! Specialize virtual_traits for std::shared_ptr by value.
  44. //!
  45. //! @tparam Class A class type, possibly cv-qualified.
  46. //! @tparam Registry A @ref registry.
  47. template<typename Class, class Registry>
  48. struct virtual_traits<std::shared_ptr<Class>, Registry> {
  49. //! Rebind to a different element type.
  50. //!
  51. //! @tparam Other The new element type.
  52. template<class Other>
  53. using rebind = std::shared_ptr<Other>;
  54. //! `Class`, stripped from cv-qualifiers.
  55. using virtual_type = std::remove_cv_t<Class>;
  56. //! Return a reference to a non-modifiable `Class` object.
  57. //! @param arg A reference to a `std::shared_ptr<Class>`.
  58. //! @return A reference to the object pointed to.
  59. static auto peek(const std::shared_ptr<Class>& arg) -> const Class& {
  60. return *arg;
  61. }
  62. //! Cast to another type.
  63. //!
  64. //! Cast to a `std::shared_ptr` to another type. If possible, use
  65. //! `std::static_pointer_cast`. Otherwise, use `std::dynamic_pointer_cast`.
  66. //!
  67. //! @tparam Derived A lvalue reference type to a `std::shared_ptr`.
  68. //! @param obj A reference to a `const shared_ptr<Class>`.
  69. //! @return A `std::shared_ptr` to the same object, cast to
  70. //! `Derived::element_type`.
  71. template<class Derived>
  72. static auto cast(const std::shared_ptr<Class>& obj) -> decltype(auto) {
  73. using namespace boost::openmethod::detail;
  74. if constexpr (requires_dynamic_cast<
  75. Class*, typename Derived::element_type*>) {
  76. return std::dynamic_pointer_cast<
  77. typename shared_ptr_cast_traits<Derived>::virtual_type>(obj);
  78. } else {
  79. return std::static_pointer_cast<
  80. typename shared_ptr_cast_traits<Derived>::virtual_type>(obj);
  81. }
  82. }
  83. #if __cplusplus >= 202002L
  84. //! Move-cast to another type (since c++20)
  85. //!
  86. //! Cast to a `std::shared_ptr` xvalue reference to another type. If
  87. //! possible, use `std::static_pointer_cast`. Otherwise, use
  88. //! `std::dynamic_pointer_cast`.
  89. //!
  90. //! @note This overload is only available for C++20 and above, because
  91. //! rvalue references overloads of `std::static_pointer_cast` and
  92. //! `std::dynamic_pointer_cast` were not available before.
  93. //!
  94. //! @tparam Derived A xvalue reference to a `std::shared_ptr`.
  95. //! @param obj A xvalue reference to a `shared_ptr<Class>`.
  96. //! @return A `std::shared_ptr&&` to the same object, cast to
  97. //! `Derived::element_type`.
  98. template<class Derived>
  99. static auto cast(std::shared_ptr<Class>&& obj) -> decltype(auto) {
  100. using namespace boost::openmethod::detail;
  101. if constexpr (requires_dynamic_cast<
  102. Class*, decltype(std::declval<Derived>().get())>) {
  103. return std::dynamic_pointer_cast<
  104. typename shared_ptr_cast_traits<Derived>::virtual_type>(
  105. std::move(obj));
  106. } else {
  107. return std::static_pointer_cast<
  108. typename shared_ptr_cast_traits<Derived>::virtual_type>(
  109. std::move(obj));
  110. }
  111. }
  112. #endif
  113. };
  114. //! Specialize virtual_traits for std::shared_ptr by reference.
  115. //!
  116. //! @note Passing a `std::shared_ptr` in a method call by const reference
  117. //! creates a temporary `std::shared_ptr` and passes it by const reference to
  118. //! the overrider. This is necessary because virtual arguments need to be cast
  119. //! to the type expected by the overrider.
  120. //!
  121. //! @tparam Class A class type, possibly cv-qualified.
  122. //! @tparam Registry A @ref registry.
  123. template<class Class, class Registry>
  124. struct virtual_traits<const std::shared_ptr<Class>&, Registry> {
  125. public:
  126. //! Rebind to a different element type.
  127. //!
  128. //! @tparam Other The new element type.
  129. template<class Other>
  130. using rebind = std::shared_ptr<Other>;
  131. //! `Class`, stripped from cv-qualifiers.
  132. using virtual_type = std::remove_cv_t<Class>;
  133. //! Return a reference to a non-modifiable `Class` object.
  134. //! @param arg A reference to a `std::shared_ptr<Class>`.
  135. //! @return A reference to the object pointed to.
  136. static auto peek(const std::shared_ptr<Class>& arg) -> const Class& {
  137. return *arg;
  138. }
  139. //! Cast to another type.
  140. //!
  141. //! Cast to a `std::shared_ptr` to another type. If possible, use
  142. //! `std::static_pointer_cast`. Otherwise, use `std::dynamic_pointer_cast`.
  143. //!
  144. //! @tparam Derived A lvalue reference type to a `std::shared_ptr`.
  145. //! @param obj A reference to a `const shared_ptr<Class>`.
  146. //! @return A `std::shared_ptr` to the same object, cast to
  147. //! `Derived::element_type`.
  148. template<class Other>
  149. static auto cast(const std::shared_ptr<Class>& obj) {
  150. using namespace boost::openmethod::detail;
  151. if constexpr (requires_dynamic_cast<Class*, Other>) {
  152. return std::dynamic_pointer_cast<
  153. typename shared_ptr_cast_traits<Other>::virtual_type>(obj);
  154. } else {
  155. return std::static_pointer_cast<
  156. typename shared_ptr_cast_traits<Other>::virtual_type>(obj);
  157. }
  158. }
  159. };
  160. //! Alias for a `virtual_ptr<std::shared_ptr<T>>`.
  161. template<class Class, class Registry = BOOST_OPENMETHOD_DEFAULT_REGISTRY>
  162. using shared_virtual_ptr = virtual_ptr<std::shared_ptr<Class>, Registry>;
  163. //! Create a new object and return a `shared_virtual_ptr` to it.
  164. //!
  165. //! Create an object using `std::make_shared`, and return a @ref
  166. //! shared_virtual_ptr pointing to it. Since the exact class of the object is
  167. //! known, the `virtual_ptr` is created using @ref final_virtual_ptr.
  168. //!
  169. //! `Class` is _not_ required to be a polymorphic class.
  170. //!
  171. //! @tparam Class The class of the object to create.
  172. //! @tparam Registry A @ref registry.
  173. //! @tparam T Types of the arguments to pass to the constructor of `Class`.
  174. //! @param args Arguments to pass to the constructor of `Class`.
  175. //! @return A `shared_virtual_ptr<Class, Registry>` pointing to a newly
  176. //! created object of type `Class`.
  177. template<
  178. class Class, class Registry = BOOST_OPENMETHOD_DEFAULT_REGISTRY,
  179. typename... T>
  180. inline auto make_shared_virtual(T&&... args) {
  181. return final_virtual_ptr<Registry>(
  182. std::make_shared<Class>(std::forward<T>(args)...));
  183. }
  184. namespace aliases {
  185. using boost::openmethod::make_shared_virtual;
  186. using boost::openmethod::shared_virtual_ptr;
  187. } // namespace aliases
  188. } // namespace boost::openmethod
  189. #endif