// sol3 // The MIT License (MIT) // Copyright (c) 2013-2020 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 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; }; namespace sol { template <> struct is_container : std::true_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 struct order_suit { std::vector> objs; std::vector> objs2; order_suit(int pairs) { objs.reserve(pairs); objs2.reserve(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 }); } } }; 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 > 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); }