vptr_vector.hpp 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194
  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_POLICY_VPTR_VECTOR_HPP
  6. #define BOOST_OPENMETHOD_POLICY_VPTR_VECTOR_HPP
  7. #include <boost/openmethod/preamble.hpp>
  8. #include <variant>
  9. #include <vector>
  10. namespace boost::openmethod {
  11. namespace detail {
  12. template<class Registry>
  13. inline std::vector<vptr_type> vptr_vector_vptrs;
  14. template<class Registry>
  15. inline std::vector<const vptr_type*> vptr_vector_indirect_vptrs;
  16. } // namespace detail
  17. namespace policies {
  18. //! Stores v-table pointers in a vector.
  19. //!
  20. //! `vptr_vector` stores v-table pointers in a global vector. If `Registry`
  21. //! contains a @ref type_hash policy, it is used to convert `type_id`s to
  22. //! indices. Otherwise, `type_id`s are used directly as indices.
  23. //!
  24. //! If the registry contains the @ref indirect_vptr policy, stores pointers to
  25. //! pointers to v-tables in the vector.
  26. struct vptr_vector : vptr {
  27. public:
  28. //! A VptrFn metafunction.
  29. //!
  30. //! Keeps track of v-table pointers using a `std::vector`.
  31. //!
  32. //! If `Registry` contains a @ref type_hash policy, it is used to convert
  33. //! `type_id`s to indices; otherwise, `type_id`s are used as indices.
  34. //!
  35. //! If `Registry` contains the @ref indirect_vptr policy, stores pointers to
  36. //! pointers to v-tables in the map.
  37. //!
  38. //! @tparam Registry The registry containing this policy.
  39. template<class Registry>
  40. struct fn {
  41. using type_hash =
  42. typename Registry::template policy<policies::type_hash>;
  43. static constexpr auto has_type_hash = !std::is_same_v<type_hash, void>;
  44. //! Stores the v-table pointers.
  45. //!
  46. //! If `Registry` contains a @ref type_hash policy, its `initialize`
  47. //! function is called. Its result determines the size of the vector.
  48. //! The v-table pointers are copied into the vector.
  49. //!
  50. //! @tparam Context An @ref InitializeContext.
  51. //! @tparam Options... Zero or more option types.
  52. //! @param ctx A Context object.
  53. //! @param options A tuple of option objects.
  54. template<class Context, class... Options>
  55. static auto initialize(
  56. const Context& ctx, const std::tuple<Options...>& options) -> void {
  57. std::size_t size;
  58. (void)options;
  59. if constexpr (has_type_hash) {
  60. auto [_, max_value] = type_hash::initialize(ctx, options);
  61. size = max_value + 1;
  62. } else {
  63. size = 0;
  64. for (auto iter = ctx.classes_begin(); iter != ctx.classes_end();
  65. ++iter) {
  66. for (auto type_iter = iter->type_id_begin();
  67. type_iter != iter->type_id_end(); ++type_iter) {
  68. size = (std::max)(size, std::size_t(*type_iter));
  69. }
  70. }
  71. ++size;
  72. }
  73. if constexpr (Registry::has_indirect_vptr) {
  74. detail::vptr_vector_indirect_vptrs<Registry>.resize(size);
  75. } else {
  76. detail::vptr_vector_vptrs<Registry>.resize(size);
  77. }
  78. for (auto iter = ctx.classes_begin(); iter != ctx.classes_end();
  79. ++iter) {
  80. for (auto type_iter = iter->type_id_begin();
  81. type_iter != iter->type_id_end(); ++type_iter) {
  82. std::size_t index;
  83. if constexpr (has_type_hash) {
  84. index = type_hash::hash(*type_iter);
  85. } else {
  86. index = std::size_t(*type_iter);
  87. }
  88. if constexpr (Registry::has_indirect_vptr) {
  89. detail::vptr_vector_indirect_vptrs<Registry>[index] =
  90. &iter->vptr();
  91. } else {
  92. detail::vptr_vector_vptrs<Registry>[index] =
  93. iter->vptr();
  94. }
  95. }
  96. }
  97. }
  98. //! Returns a *reference* to a v-table pointer for an object.
  99. //!
  100. //! Acquires the dynamic @ref type_id of `arg`, using the registry's
  101. //! @ref rtti policy.
  102. //!
  103. //! If the registry has a @ref type_hash policy, uses it to convert the
  104. //! type id to an index; otherwise, uses the type_id as the index.
  105. //!
  106. //! If the registry contains the @ref runtime_checks policy, verifies
  107. //! that the index falls within the limits of the vector. If it does
  108. //! not, and if the registry contains a @ref error_handler policy, calls
  109. //! its @ref error function with a @ref missing_class value, then
  110. //! terminates the program with @ref abort.
  111. //!
  112. //! @tparam Class A registered class.
  113. //! @param arg A reference to a const object of type `Class`.
  114. //! @return A reference to a the v-table pointer for `Class`.
  115. template<class Class>
  116. static auto dynamic_vptr(const Class& arg) -> const vptr_type& {
  117. auto dynamic_type = Registry::rtti::dynamic_type(arg);
  118. std::size_t index;
  119. if constexpr (has_type_hash) {
  120. index = type_hash::hash(dynamic_type);
  121. } else {
  122. index = std::size_t(dynamic_type);
  123. if constexpr (Registry::has_runtime_checks) {
  124. std::size_t max_index = 0;
  125. if constexpr (Registry::has_indirect_vptr) {
  126. max_index =
  127. detail::vptr_vector_indirect_vptrs<Registry>.size();
  128. } else {
  129. max_index = detail::vptr_vector_vptrs<Registry>.size();
  130. }
  131. if (index >= max_index) {
  132. if constexpr (Registry::has_error_handler) {
  133. missing_class error;
  134. error.type = dynamic_type;
  135. Registry::error_handler::error(error);
  136. }
  137. abort();
  138. }
  139. }
  140. }
  141. if constexpr (Registry::has_indirect_vptr) {
  142. return *detail::vptr_vector_indirect_vptrs<Registry>[index];
  143. } else {
  144. return detail::vptr_vector_vptrs<Registry>[index];
  145. }
  146. }
  147. //! Releases the memory allocated by `initialize`.
  148. //!
  149. //! @tparam Options... Zero or more option types, deduced from the function
  150. //! arguments.
  151. //! @param options Zero or more option objects.
  152. template<class... Options>
  153. static auto finalize(const std::tuple<Options...>&) -> void {
  154. using namespace policies;
  155. if constexpr (Registry::has_indirect_vptr) {
  156. detail::vptr_vector_indirect_vptrs<Registry>.clear();
  157. } else {
  158. detail::vptr_vector_vptrs<Registry>.clear();
  159. }
  160. };
  161. };
  162. };
  163. } // namespace policies
  164. } // namespace boost::openmethod
  165. #endif