pairs_iterator.hpp 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275
  1. // sol2
  2. // The MIT License (MIT)
  3. // Copyright (c) 2013-2022 Rapptz, ThePhD and contributors
  4. // Permission is hereby granted, free of charge, to any person obtaining a copy of
  5. // this software and associated documentation files (the "Software"), to deal in
  6. // the Software without restriction, including without limitation the rights to
  7. // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
  8. // the Software, and to permit persons to whom the Software is furnished to do so,
  9. // subject to the following conditions:
  10. // The above copyright notice and this permission notice shall be included in all
  11. // copies or substantial portions of the Software.
  12. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  13. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
  14. // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
  15. // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
  16. // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
  17. // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  18. #ifndef SOL_PAIRS_ITERATOR_HPP
  19. #define SOL_PAIRS_ITERATOR_HPP
  20. #include <sol/version.hpp>
  21. #include <sol/reference.hpp>
  22. #include <sol/stack_reference.hpp>
  23. #include <sol/table_iterator.hpp>
  24. #include <sol/protected_function.hpp>
  25. #include <sol/stack/detail/pairs.hpp>
  26. namespace sol {
  27. struct pairs_sentinel { };
  28. class pairs_iterator {
  29. private:
  30. inline static constexpr int empty_key_index = -1;
  31. public:
  32. using key_type = object;
  33. using mapped_type = object;
  34. using value_type = std::pair<object, object>;
  35. using iterator_category = std::input_iterator_tag;
  36. using difference_type = std::ptrdiff_t;
  37. using pointer = value_type*;
  38. using const_pointer = value_type const*;
  39. using reference = value_type&;
  40. using const_reference = const value_type&;
  41. pairs_iterator() noexcept
  42. : m_L(nullptr)
  43. , m_next_function_ref(lua_nil)
  44. , m_table_ref(lua_nil)
  45. , m_cached_key_value_pair({ lua_nil, lua_nil })
  46. , m_key_index(empty_key_index)
  47. , m_iteration_index(0) {
  48. }
  49. pairs_iterator(const pairs_iterator&) = delete;
  50. pairs_iterator& operator=(const pairs_iterator&) = delete;
  51. pairs_iterator(pairs_iterator&& right) noexcept
  52. : m_L(right.m_L)
  53. , m_next_function_ref(std::move(right.m_next_function_ref))
  54. , m_table_ref(std::move(right.m_table_ref))
  55. , m_cached_key_value_pair(std::move(right.m_cached_key_value_pair))
  56. , m_key_index(right.m_key_index)
  57. , m_iteration_index(right.m_iteration_index) {
  58. right.m_key_index = empty_key_index;
  59. }
  60. pairs_iterator& operator=(pairs_iterator&& right) noexcept {
  61. m_L = right.m_L;
  62. m_next_function_ref = std::move(right.m_next_function_ref);
  63. m_table_ref = std::move(right.m_table_ref);
  64. m_cached_key_value_pair = std::move(right.m_cached_key_value_pair);
  65. m_key_index = right.m_key_index;
  66. m_iteration_index = right.m_iteration_index;
  67. right.m_key_index = empty_key_index;
  68. return *this;
  69. }
  70. template <typename Source>
  71. pairs_iterator(const Source& source_) noexcept : m_L(source_.lua_state()), m_key_index(empty_key_index), m_iteration_index(0) {
  72. if (m_L == nullptr || !source_.valid()) {
  73. m_key_index = empty_key_index;
  74. return;
  75. }
  76. int source_index = -source_.push(m_L);
  77. int abs_source_index = lua_absindex(m_L, source_index);
  78. int metatable_exists = lua_getmetatable(m_L, abs_source_index);
  79. lua_remove(m_L, abs_source_index);
  80. if (metatable_exists == 1) {
  81. // just has a metatable, but does it have __pairs ?
  82. stack_reference metatable(m_L, raw_index(abs_source_index));
  83. stack::get_field<is_global_table_v<Source>, true>(m_L, meta_function::pairs, metatable.stack_index());
  84. optional<protected_function> maybe_pairs_function = stack::pop<optional<protected_function>>(m_L);
  85. if (maybe_pairs_function.has_value()) {
  86. protected_function& pairs_function = *maybe_pairs_function;
  87. protected_function_result next_fn_and_table_and_first_key = pairs_function(source_);
  88. if (next_fn_and_table_and_first_key.valid()) {
  89. m_next_function_ref = next_fn_and_table_and_first_key.get<protected_function>(0);
  90. m_table_ref = next_fn_and_table_and_first_key.get<sol::reference>(1);
  91. m_key_index = next_fn_and_table_and_first_key.stack_index() - 1;
  92. // remove next function and table
  93. lua_remove(m_L, m_key_index);
  94. lua_remove(m_L, m_key_index);
  95. next_fn_and_table_and_first_key.abandon();
  96. lua_remove(m_L, abs_source_index);
  97. this->operator++();
  98. m_iteration_index = 0;
  99. return;
  100. }
  101. }
  102. }
  103. {
  104. auto maybe_next = stack::stack_detail::find_lua_next_function(m_L);
  105. if (maybe_next.has_value()) {
  106. m_next_function_ref = std::move(*maybe_next);
  107. m_table_ref = source_;
  108. stack::push(m_L, lua_nil);
  109. m_key_index = lua_gettop(m_L);
  110. this->operator++();
  111. m_iteration_index = 0;
  112. return;
  113. }
  114. }
  115. // okay, so none of the above worked and now we need to create
  116. // a shim / polyfill instead
  117. stack::push(m_L, &stack::stack_detail::c_lua_next);
  118. m_next_function_ref = stack::pop<protected_function>(m_L);
  119. m_table_ref = source_;
  120. stack::push(m_L, lua_nil);
  121. m_key_index = lua_gettop(m_L);
  122. this->operator++();
  123. m_iteration_index = 0;
  124. }
  125. pairs_iterator& operator++() {
  126. if (m_key_index == empty_key_index) {
  127. return *this;
  128. }
  129. {
  130. sol::protected_function_result next_results = m_next_function_ref(m_table_ref, stack_reference(m_L, m_key_index));
  131. if (!next_results.valid()) {
  132. // TODO: abort, or throw an error?
  133. m_clear();
  134. m_key_index = empty_key_index;
  135. return *this;
  136. }
  137. int next_results_count = next_results.return_count();
  138. if (next_results_count < 2) {
  139. // iteration is over!
  140. next_results.abandon();
  141. lua_settop(m_L, m_key_index - 1);
  142. m_key_index = empty_key_index;
  143. ++m_iteration_index;
  144. return *this;
  145. }
  146. else {
  147. lua_remove(m_L, m_key_index);
  148. m_key_index = next_results.stack_index() - 1;
  149. m_cached_key_value_pair.first = stack::get<object>(m_L, m_key_index);
  150. m_cached_key_value_pair.second = stack::get<object>(m_L, m_key_index + 1);
  151. lua_settop(m_L, m_key_index);
  152. next_results.abandon();
  153. }
  154. }
  155. ++m_iteration_index;
  156. return *this;
  157. }
  158. std::ptrdiff_t index() const {
  159. return static_cast<std::ptrdiff_t>(m_iteration_index);
  160. }
  161. const_reference operator*() const noexcept {
  162. return m_cached_key_value_pair;
  163. }
  164. reference operator*() noexcept {
  165. return m_cached_key_value_pair;
  166. }
  167. friend bool operator==(const pairs_iterator& left, const pairs_iterator& right) noexcept {
  168. return left.m_table_ref == right.m_table_ref && left.m_iteration_index == right.m_iteration_index;
  169. }
  170. friend bool operator!=(const pairs_iterator& left, const pairs_iterator& right) noexcept {
  171. return left.m_table_ref != right.m_table_ref || left.m_iteration_index != right.m_iteration_index;
  172. }
  173. friend bool operator==(const pairs_iterator& left, const pairs_sentinel&) noexcept {
  174. return left.m_key_index == empty_key_index;
  175. }
  176. friend bool operator!=(const pairs_iterator& left, const pairs_sentinel&) noexcept {
  177. return left.m_key_index != empty_key_index;
  178. }
  179. friend bool operator==(const pairs_sentinel&, const pairs_iterator& left) noexcept {
  180. return left.m_key_index == empty_key_index;
  181. }
  182. friend bool operator!=(const pairs_sentinel&, const pairs_iterator& left) noexcept {
  183. return left.m_key_index != empty_key_index;
  184. }
  185. ~pairs_iterator() {
  186. if (m_key_index != empty_key_index) {
  187. m_clear();
  188. }
  189. }
  190. private:
  191. void m_clear() noexcept {
  192. lua_remove(m_L, m_key_index);
  193. }
  194. lua_State* m_L;
  195. protected_function m_next_function_ref;
  196. sol::reference m_table_ref;
  197. std::pair<object, object> m_cached_key_value_pair;
  198. int m_key_index;
  199. int m_iteration_index;
  200. };
  201. template <typename Source>
  202. class basic_pairs_range {
  203. private:
  204. using source_t = std::add_lvalue_reference_t<Source>;
  205. source_t m_source;
  206. public:
  207. using iterator = pairs_iterator;
  208. using const_iterator = pairs_iterator;
  209. basic_pairs_range(source_t source_) noexcept : m_source(source_) {
  210. }
  211. iterator begin() noexcept {
  212. return iterator(m_source);
  213. }
  214. iterator begin() const noexcept {
  215. return iterator(m_source);
  216. }
  217. const_iterator cbegin() const noexcept {
  218. return const_iterator(m_source);
  219. }
  220. pairs_sentinel end() noexcept {
  221. return {};
  222. }
  223. pairs_sentinel end() const noexcept {
  224. return {};
  225. }
  226. pairs_sentinel cend() const noexcept {
  227. return {};
  228. }
  229. };
  230. } // namespace sol
  231. #endif // SOL_PAIRS_ITERATOR_HPP