inplace_vptr.hpp 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284
  1. #ifndef BOOST_OPENMETHOD_INPLACE_VPTR_HPP
  2. #define BOOST_OPENMETHOD_INPLACE_VPTR_HPP
  3. #include <boost/openmethod/core.hpp>
  4. // =============================================================================
  5. // inplace_vptr
  6. namespace boost::openmethod {
  7. namespace detail {
  8. void boost_openmethod_registry(...);
  9. void boost_openmethod_bases(...);
  10. template<class Class>
  11. using inplace_vptr_registry =
  12. decltype(boost_openmethod_registry(std::declval<Class*>()));
  13. template<class>
  14. struct update_vptr_bases;
  15. template<class To, class Class>
  16. void boost_openmethod_update_vptr(Class* obj);
  17. template<class... Bases>
  18. struct update_vptr_bases<mp11::mp_list<Bases...>> {
  19. template<class To, class Class>
  20. static void fn(Class* obj) {
  21. (boost_openmethod_update_vptr<To>(static_cast<Bases*>(obj)), ...);
  22. }
  23. };
  24. template<class To, class Class>
  25. void boost_openmethod_update_vptr(Class* obj) {
  26. using registry = inplace_vptr_registry<Class>;
  27. using bases = decltype(boost_openmethod_bases(obj));
  28. if constexpr (mp11::mp_size<bases>::value == 0) {
  29. if constexpr (registry::has_indirect_vptr) {
  30. obj->boost_openmethod_vptr = &registry::template static_vptr<To>;
  31. } else {
  32. obj->boost_openmethod_vptr = registry::template static_vptr<To>;
  33. }
  34. } else {
  35. update_vptr_bases<bases>::template fn<To, Class>(obj);
  36. }
  37. }
  38. #if defined(__GNUC__) && !defined(__clang__)
  39. #pragma GCC diagnostic ignored "-Wnon-template-friend"
  40. #endif
  41. template<class... Classes>
  42. inline use_classes<Classes...> inplace_vptr_use_classes;
  43. class inplace_vptr_base_tag {};
  44. } // namespace detail
  45. //! Embed a v-table pointer in a class.
  46. //!
  47. //! `inplace_vptr_base` is a [CRTP
  48. //! mixin](https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern)
  49. //! that embeds a v-table pointer at the root of a class hierarchy. It also
  50. //! declares a @ref boost_openmethod_vptr free function that returns the v-table
  51. //! pointer stored in the object.
  52. //!
  53. //! `inplace_vptr_base` registers the class in `Registry`. It is not necessary
  54. //! to register the class with @ref use_class or
  55. //! {{BOOST_OPENMETHOD_REGISTER}}
  56. //!
  57. //! The v-table pointer is obtained directly from the `Registry`\'s @ref
  58. //! static_vptr variable. No hashing is involved. If all the classes in
  59. //! `Registry` inherit from `inplace_vptr_base`, the registry needs not have a
  60. //! @ref policies::vptr policy, nor any policy it depends on (like @ref
  61. //! policies::type_hash).
  62. //!
  63. //! If `Registry` contains the @ref has_indirect_vptr policy, the v-table
  64. //! pointer is stored as a pointer to a pointer, and remains valid after a call
  65. //! to @ref initialize.
  66. //!
  67. //! The default value of `Registry` can be changed by defining
  68. //! {{BOOST_OPENMETHOD_DEFAULT_REGISTRY}}
  69. //!
  70. //! @tparam Class The class in which to embed the v-table pointer.
  71. //! @tparam Registry The @ref registry in which `Class` and its derived classes
  72. //! are registered.
  73. //!
  74. //! @par Example
  75. //! @code
  76. //! #include <boost/openmethod.hpp>
  77. //! #include <boost/openmethod/inplace_vptr.hpp>
  78. //! #include <boost/openmethod/initialize.hpp>
  79. //!
  80. //! using namespace boost::openmethod;
  81. //!
  82. //! struct Animal : inplace_vptr_base<Animal> {};
  83. //!
  84. //! struct Cat : Animal, inplace_vptr_derived<Cat, Animal> {};
  85. //!
  86. //! struct Dog : Animal, inplace_vptr_derived<Dog, Animal> {};
  87. //!
  88. //! BOOST_OPENMETHOD(
  89. //! poke, (virtual_<Animal&> animal, std::ostream& os), void);
  90. //!
  91. //! BOOST_OPENMETHOD_OVERRIDE(poke, (Cat&, std::ostream& os), void) {
  92. //! os << "hiss\n";
  93. //! }
  94. //!
  95. //! BOOST_OPENMETHOD_OVERRIDE(poke, (Dog&, std::ostream& os), void) {
  96. //! os << "bark\n";
  97. //! }
  98. //!
  99. //! int main() {
  100. //! initialize();
  101. //!
  102. //! std::unique_ptr<Animal> a = std::make_unique<Cat>();
  103. //! std::unique_ptr<Animal> b = std::make_unique<Dog>();
  104. //!
  105. //! poke(*a, std::cout); // hiss
  106. //! poke(*b, std::cout); // bark
  107. //!
  108. //! return 0;
  109. //! }
  110. //! @endcode
  111. template<class Class, class Registry = BOOST_OPENMETHOD_DEFAULT_REGISTRY>
  112. class inplace_vptr_base : protected detail::inplace_vptr_base_tag {
  113. template<class To, class Other>
  114. friend void detail::boost_openmethod_update_vptr(Other*);
  115. friend auto boost_openmethod_registry(Class*) -> Registry;
  116. friend auto boost_openmethod_bases(Class*) -> mp11::mp_list<>;
  117. std::conditional_t<Registry::has_indirect_vptr, const vptr_type*, vptr_type>
  118. boost_openmethod_vptr = nullptr;
  119. friend auto
  120. boost_openmethod_vptr(const Class& obj, Registry*) noexcept -> vptr_type {
  121. if constexpr (Registry::has_indirect_vptr) {
  122. return *obj.boost_openmethod_vptr;
  123. } else {
  124. return obj.boost_openmethod_vptr;
  125. }
  126. }
  127. protected:
  128. //! Set the vptr to `Class`\'s v-table.
  129. inplace_vptr_base() noexcept {
  130. (void)&detail::inplace_vptr_use_classes<Class, Registry>;
  131. detail::boost_openmethod_update_vptr<Class>(static_cast<Class*>(this));
  132. }
  133. //! Set the vptr to `nullptr`.
  134. ~inplace_vptr_base() noexcept {
  135. boost_openmethod_vptr = nullptr;
  136. }
  137. };
  138. #ifdef __MRDOCS__
  139. //! Adjust the v-table pointer embedded in a class.
  140. //!
  141. //! `inplace_vptr_derived` is a [CRTP
  142. //! mixin](https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern)
  143. //! that adjusts the v-table pointer in a @ref inplace_vptr_base. It can be used
  144. //! only with classes that have @ref inplace_vptr_base as a direct or indirect
  145. //! base class.
  146. //!
  147. //! `inplace_vptr_derived` registers the class and its bases in `Registry`. It
  148. //! is not necessary to register them with @ref use_class or
  149. //! {{BOOST_OPENMETHOD_REGISTER}}
  150. //!
  151. //! The v-table pointer is obtained directly from the `Registry`\'s @ref
  152. //! static_vptr variable. No hashing is involved. If all the classes in
  153. //! `Registry` inherit from `inplace_vptr_derived`, the registry needs not have a
  154. //! @ref policies::vptr policy, nor any policy it depends on (like @ref
  155. //! policies::type_hash).
  156. //!
  157. //! @see @ref inplace_vptr_base for an example.
  158. //!
  159. //! @tparam Class The class in which to embed the v-table pointer.
  160. //! @tparam Base A direct base class of `Class`.
  161. //! @tparam MoreBases More direct base classes of `Class`.
  162. template<class Class, class Base, class... MoreBases>
  163. class inplace_vptr_derived {
  164. protected:
  165. //! Set the vptr to `Class`\'s v-table.
  166. inplace_vptr_derived() noexcept;
  167. //! Set the vptr in each base class.
  168. //!
  169. //! For each base, set its vptr to the base's v-table.
  170. ~inplace_vptr_derived() noexcept;
  171. };
  172. #else
  173. template<class Class, class Base, class... MoreBases>
  174. class inplace_vptr_derived;
  175. #endif
  176. //! Specialization for a single base class.
  177. //!
  178. //!
  179. //! @see The main template for documentation.
  180. template<class Class, class Base>
  181. class inplace_vptr_derived<Class, Base> {
  182. static_assert(
  183. !detail::is_registry<Base>,
  184. "registry can be specified only for root classes");
  185. static_assert(
  186. std::is_base_of_v<detail::inplace_vptr_base_tag, Base>,
  187. "class must inherit from inplace_vptr_base");
  188. template<class To, class Other>
  189. friend void detail::boost_openmethod_update_vptr(Other*);
  190. friend auto boost_openmethod_bases(Class*) -> mp11::mp_list<Base>;
  191. protected:
  192. //! Set the vptr to `Class`\'s v-table.
  193. inplace_vptr_derived() noexcept {
  194. using namespace detail;
  195. (void)&detail::inplace_vptr_use_classes<
  196. Class, Base, detail::inplace_vptr_registry<Class>>;
  197. boost_openmethod_update_vptr<Class>(static_cast<Class*>(this));
  198. }
  199. //! Set the vptr in base class.
  200. ~inplace_vptr_derived() noexcept {
  201. detail::boost_openmethod_update_vptr<Base>(
  202. static_cast<Base*>(static_cast<Class*>(this)));
  203. }
  204. };
  205. //! Specialization for multiple base classes.
  206. //!
  207. //! @see The main template for documentation.
  208. template<class Class, class Base1, class Base2, class... MoreBases>
  209. class inplace_vptr_derived<Class, Base1, Base2, MoreBases...> {
  210. static_assert(
  211. !detail::is_registry<Base1> && !detail::is_registry<Base2> &&
  212. (!detail::is_registry<MoreBases> && ...),
  213. "registry can be specified only for root classes");
  214. friend auto
  215. boost_openmethod_registry(Class*) -> detail::inplace_vptr_registry<Base1>;
  216. friend auto
  217. boost_openmethod_bases(Class*) -> mp11::mp_list<Base1, Base2, MoreBases...>;
  218. friend auto boost_openmethod_vptr(
  219. const Class& obj,
  220. detail::inplace_vptr_registry<Base1>* registry) -> vptr_type {
  221. return boost_openmethod_vptr(static_cast<const Base1&>(obj), registry);
  222. }
  223. protected:
  224. //! Set the vptr to `Class`\'s v-table.
  225. inplace_vptr_derived() noexcept {
  226. (void)&detail::inplace_vptr_use_classes<
  227. Class, Base1, Base2, MoreBases...,
  228. detail::inplace_vptr_registry<Base1>>;
  229. detail::boost_openmethod_update_vptr<Class>(static_cast<Class*>(this));
  230. }
  231. //! Set the vptr in each base class.
  232. //!
  233. //! For each base, set its vptr to the base's v-table.
  234. ~inplace_vptr_derived() noexcept {
  235. auto obj = static_cast<Class*>(this);
  236. detail::boost_openmethod_update_vptr<Base1>(static_cast<Base1*>(obj));
  237. detail::boost_openmethod_update_vptr<Base2>(static_cast<Base2*>(obj));
  238. (detail::boost_openmethod_update_vptr<MoreBases>(
  239. static_cast<MoreBases*>(obj)),
  240. ...);
  241. }
  242. };
  243. namespace aliases {
  244. using boost::openmethod::inplace_vptr_base;
  245. using boost::openmethod::inplace_vptr_derived;
  246. } // namespace aliases
  247. } // namespace boost::openmethod
  248. #endif // BOOST_OPENMETHOD_inplace_vptr_HPP