// sol2 // The MIT License (MIT) // Copyright (c) 2013-2022 Rapptz, ThePhD and contributors // Permission is hereby granted, free of charge, to any person obtaining a copy of // this software and associated documentation files (the "Software"), to deal in // the Software without restriction, including without limitation the rights to // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of // the Software, and to permit persons to whom the Software is furnished to do so, // subject to the following conditions: // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include "sol_test.hpp" #include #include #include #include #include #include #include #include #include #include #include inline namespace sol2_test_container_shims { class int_shim { public: int_shim() = default; int_shim(int x) : x_(x) { } int val() const { return x_; } private: int x_ = -1; }; class input_it { public: typedef std::input_iterator_tag iterator_category; typedef int_shim value_type; typedef value_type& reference; typedef const value_type& const_reference; typedef value_type* pointer; typedef std::ptrdiff_t difference_type; input_it() = default; input_it(int n, int m) : n_(n), m_(m), value_(n_) { assert(n_ >= 0); assert(m_ >= 0); assert(n_ <= m_); if (!n_ && !m_) { n_ = -1; m_ = -1; value_ = -1; } } const int_shim& operator*() const { return value_; } const int_shim* operator->() const { return &value_; } input_it& operator++() { assert(n_ >= 0); assert(m_ >= 0); if (n_ == m_ - 1) { n_ = m_ = -1; } else { ++n_; } value_ = n_; return *this; } bool operator==(const input_it& i) const { return n_ == i.n_ && m_ == i.m_; } bool operator!=(const input_it& i) const { return !(*this == i); } private: int n_ = -1; int m_ = -1; int_shim value_; }; class not_really_a_container { public: using value_type = int_shim; using iterator = input_it; using const_iterator = input_it; const_iterator begin() const { return iterator(0, 100); } const_iterator end() const { return iterator(); } value_type gcc_warning_block() { return int_shim(); } std::size_t size() const { return 100; } }; struct my_vec : public std::vector { typedef std::vector base_t; using base_t::base_t; }; struct order_suit { std::vector> objs; std::vector> objs2; order_suit(int pairs) { objs.reserve(static_cast(pairs)); objs2.reserve(static_cast(pairs * 2)); for (int i = 0; i < pairs; ++i) { objs.push_back({ i, i * 10 }); objs2.push_back({ (i + pairs) * 2, (i * 2) * 50 }); objs2.push_back({ ((i + pairs) * 2) + 1, (i * 2 + 1) * 50 }); } } }; class map_number_storage { private: std::unordered_map data; // changed to map public: map_number_storage(int i) { data[std::to_string(i)] = i; } int accumulate() const // changed for map { std::size_t sum = 0; for (const auto& [k, v] : data) { (void)k; sum += v; } return static_cast(sum); } public: typedef std::string key_type; typedef int mapped_type; using value_type = decltype(data)::value_type; using iterator = decltype(data)::iterator; using const_iterator = decltype(data)::const_iterator; using size_type = decltype(data)::size_type; // ADDED iterator find(const key_type& key) { return data.find(key); } auto insert(value_type kv) { return data.insert(kv); } auto insert(const key_type k, mapped_type v) { return data.insert({ k, v }); } mapped_type& set(key_type k, mapped_type v) { return data[k] = v; } iterator begin() { return iterator(data.begin()); } iterator end() { return iterator(data.end()); } size_type size() const noexcept { return data.size(); } size_type max_size() const noexcept { return data.max_size(); } // void push_back(int value) { data.push_back(value); } NOT APPLICABLE TO MAP bool empty() const noexcept { return data.empty(); } }; } // namespace sol2_test_container_shims namespace sol { template <> struct is_container : std::true_type { }; template <> struct is_container : std::false_type { }; template <> struct usertype_container { // Hooks Lua's syntax for #c static int size(lua_State* L) { my_vec& v = sol::stack::get(L, 1); return stack::push(L, v.size()); } // Used by default implementation static auto begin(lua_State*, my_vec& self) { return self.begin(); } static auto end(lua_State*, my_vec& self) { return self.end(); } static std::ptrdiff_t index_adjustment(lua_State*, my_vec&) { return 0; } }; } // namespace sol TEST_CASE("containers/input iterators", "test shitty input iterators that are all kinds of B L E H") { sol::state lua; lua.open_libraries(sol::lib::base, sol::lib::package); lua.new_usertype("int_shim", "new", sol::no_constructor, "val", &int_shim::val); not_really_a_container c; lua["c"] = &c; #if SOL_LUA_VERSION_I_ > 502 auto result0 = lua.safe_script(R"lua( for k, v in pairs(c) do assert((k - 1) == v:val()) end )lua", sol::script_pass_on_error); REQUIRE(result0.valid()); #endif auto result1 = lua.safe_script(R"lua( for k=1,#c do v = c[k] assert((k - 1) == v:val()) end )lua", sol::script_pass_on_error); REQUIRE(result1.valid()); } TEST_CASE("containers/custom indexing", "allow containers to set a custom indexing offset") { sol::state lua; lua.open_libraries(sol::lib::base); lua["c"] = my_vec { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; auto result1 = lua.safe_script("for i=0,9 do assert(i == c[i]) end", sol::script_pass_on_error); REQUIRE(result1.valid()); auto result2 = lua.safe_script("assert(c[10] == nil)", sol::script_pass_on_error); REQUIRE(result2.valid()); auto result3 = lua.safe_script("assert(c[-1] == nil)", sol::script_pass_on_error); REQUIRE(result3.valid()); auto result4 = lua.safe_script("assert(#c == 10)", sol::script_pass_on_error); REQUIRE(result4.valid()); } TEST_CASE("containers/containers of pointers", "containers of pointers shouldn't have their value_type's overly stripped") { sol::state lua; class MyContainer { public: typedef int** iterator; typedef int* value_type; std::vector m_vec; inline iterator begin() { return m_vec.data(); } inline iterator end() { return m_vec.data() + m_vec.size(); } inline void push_back(value_type v) { m_vec.push_back(v); } }; int a = 500; int b = 600; MyContainer ctr; ctr.push_back(&a); ctr.push_back(&b); lua["c"] = ctr; { auto result1 = lua.safe_script("ap = c[1]", sol::script_pass_on_error); REQUIRE(result1.valid()); auto result2 = lua.safe_script("bp = c[2]", sol::script_pass_on_error); REQUIRE(result2.valid()); int* ap = lua["ap"]; int* bp = lua["bp"]; REQUIRE(ap == &a); REQUIRE(bp == &b); REQUIRE(*ap == 500); REQUIRE(*bp == 600); } std::unordered_map ptrs; ptrs[5] = &a; ptrs[6] = &b; lua["c2"] = ptrs; { auto result1 = lua.safe_script("ap = c2[5]", sol::script_pass_on_error); REQUIRE(result1.valid()); auto result2 = lua.safe_script("bp = c2[6]", sol::script_pass_on_error); REQUIRE(result2.valid()); int* ap = lua["ap"]; int* bp = lua["bp"]; REQUIRE(ap == &a); REQUIRE(bp == &b); REQUIRE(*ap == 500); REQUIRE(*bp == 600); } } TEST_CASE("containers/pair container in usertypes", "make sure containers that use pairs in usertypes do not trigger compiler errors") { sol::state lua; lua.open_libraries(sol::lib::base); auto orderSuit = lua.new_usertype("order_suit", sol::constructors()); #define SET_PROP(__PROP__) orderSuit.set(#__PROP__, &order_suit::__PROP__) SET_PROP(objs); SET_PROP(objs2); #undef SET_PROP auto result1 = lua.safe_script("osobj = order_suit.new(5)", sol::script_pass_on_error); REQUIRE(result1.valid()); auto result2 = lua.safe_script("pvec = osobj.objs", sol::script_pass_on_error); REQUIRE(result2.valid()); auto result3 = lua.safe_script("pvec2 = osobj.objs2", sol::script_pass_on_error); REQUIRE(result3.valid()); using vec_t = std::remove_reference_t().objs)>; using vec2_t = std::remove_reference_t().objs2)>; vec_t& pvec = lua["pvec"]; vec2_t& pvec2 = lua["pvec2"]; REQUIRE(pvec.size() == 5); REQUIRE(pvec2.size() == 10); REQUIRE(pvec[0].first == 0); REQUIRE(pvec[0].second == 0); REQUIRE(pvec[1].first == 1); REQUIRE(pvec[1].second == 10); REQUIRE(pvec[2].first == 2); REQUIRE(pvec[2].second == 20); REQUIRE(pvec2[0].first == 10); REQUIRE(pvec2[0].second == 0); REQUIRE(pvec2[1].first == 11); REQUIRE(pvec2[1].second == 50); REQUIRE(pvec2[2].first == 12); REQUIRE(pvec2[2].second == 100); REQUIRE(pvec2[3].first == 13); REQUIRE(pvec2[3].second == 150); } TEST_CASE("containers/as_container usertype", "A usertype should be able to mark itself as a container explicitly and work with BOTH kinds of insert types") { sol::state lua; lua.open_libraries(sol::lib::base); lua.new_usertype("map_number_storage", sol::constructors(), "accumulate", &map_number_storage::accumulate, "iterable", [](map_number_storage& ns) { return sol::as_container(ns); // treat like a container, despite is_container specialization }); sol::optional maybe_error0 = lua.safe_script(R"( ns = map_number_storage.new(23) assert(ns:accumulate() == 23) -- reference original usertype like a container ns_container = ns:iterable() ns_container["24"]=24 -- now print to show effect assert(ns:accumulate() == 47) assert(#ns == 2) )", &sol::script_pass_on_error); REQUIRE_FALSE(maybe_error0.has_value()); map_number_storage& ns = lua["ns"]; map_number_storage& ns_container = lua["ns_container"]; ns.insert({ "33", 33 }); sol::optional maybe_error1 = lua.safe_script(R"( assert(ns:accumulate() == 80) assert(#ns == 3) assert(ns_container['33'] == 33) )", &sol::script_pass_on_error); REQUIRE_FALSE(maybe_error1.has_value()); REQUIRE(&ns == &ns_container); REQUIRE(ns.size() == 3); }