From 68660a1bed2f0d6579c563b34201bd9aed6f1d63 Mon Sep 17 00:00:00 2001 From: ThePhD Date: Fri, 25 Mar 2016 05:27:19 -0400 Subject: [PATCH] Allow for checking whether or not a given proxy is valid. --- docs/source/index.rst | 2 +- sol/proxy.hpp | 26 +++++++++++++++++ sol/proxy_base.hpp | 2 -- sol/stack.hpp | 1 + sol/stack_core.hpp | 26 +++++++++++++++++ sol/stack_field.hpp | 2 +- sol/stack_probe.hpp | 65 +++++++++++++++++++++++++++++++++++++++++++ tests.cpp | 22 +++++++++++++++ 8 files changed, 142 insertions(+), 4 deletions(-) create mode 100644 sol/stack_probe.hpp diff --git a/docs/source/index.rst b/docs/source/index.rst index e3c0845c..331b04cd 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -3,7 +3,7 @@ You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. -Sol 2.0 +Sol 2.1 ======= a fast, simple C++ and Lua Binding ---------------------------------- diff --git a/sol/proxy.hpp b/sol/proxy.hpp index fdab1971..10f97528 100644 --- a/sol/proxy.hpp +++ b/sol/proxy.hpp @@ -93,6 +93,12 @@ public: decltype(auto) operator()(Args&&... args) { return call<>(std::forward(args)...); } + + bool valid () const { + auto p = stack::probe_get_field, global_table>::value>(tbl.lua_state(), key); + lua_pop(tbl.lua_state(), p.levels); + return p; + } }; template @@ -115,6 +121,26 @@ inline bool operator!=(const proxy& right, T&& left) { return right.template get>() != left; } +template +inline bool operator==(nil_t left, const proxy& right) { + return !right.valid(); +} + +template +inline bool operator==(const proxy& right, nil_t) { + return !right.valid(); +} + +template +inline bool operator!=(nil_t, const proxy& right) { + return right.valid(); +} + +template +inline bool operator!=(const proxy& right, nil_t) { + return right.valid(); +} + namespace stack { template struct pusher> { diff --git a/sol/proxy_base.hpp b/sol/proxy_base.hpp index 10585e76..037a27fc 100644 --- a/sol/proxy_base.hpp +++ b/sol/proxy_base.hpp @@ -27,8 +27,6 @@ #include "stack.hpp" namespace sol { -template -struct idn { typedef T type; }; template struct proxy_base { operator std::string() const { diff --git a/sol/stack.hpp b/sol/stack.hpp index fdabed06..439e5103 100644 --- a/sol/stack.hpp +++ b/sol/stack.hpp @@ -29,6 +29,7 @@ #include "stack_push.hpp" #include "stack_pop.hpp" #include "stack_field.hpp" +#include "stack_probe.hpp" #include #include diff --git a/sol/stack_core.hpp b/sol/stack_core.hpp index 458fd559..d4c580a2 100644 --- a/sol/stack_core.hpp +++ b/sol/stack_core.hpp @@ -55,6 +55,8 @@ template struct field_getter; template struct field_setter; +template +struct probe_field_getter; template struct getter; template @@ -66,6 +68,15 @@ struct checker; template struct check_getter; +struct probe { + bool success; + int levels; + + probe(bool s, int l) : success(s), levels(l) {} + + operator bool() const { return success; }; +}; + namespace stack_detail { template struct strip { @@ -93,6 +104,11 @@ inline decltype(auto) unchecked_get(lua_State* L, int index = -1) { } } // stack_detail +inline bool maybe_indexable(lua_State* L, int index = -1) { + type t = type_of(L, index); + return t == type::userdata || t == type::table; +} + template inline int push(lua_State* L, T&& t, Args&&... args) { return pusher>{}.push(L, std::forward(t), std::forward(args)...); @@ -168,6 +184,16 @@ void get_field(lua_State* L, Key&& key, int tableindex) { field_getter, global>{}.get(L, std::forward(key), tableindex); } +template +probe probe_get_field(lua_State* L, Key&& key) { + return probe_field_getter, global>{}.get(L, std::forward(key)); +} + +template +probe probe_get_field(lua_State* L, Key&& key, int tableindex) { + return probe_field_getter, global>{}.get(L, std::forward(key), tableindex); +} + template void set_field(lua_State* L, Key&& key, Value&& value) { field_setter, global>{}.set(L, std::forward(key), std::forward(value)); diff --git a/sol/stack_field.hpp b/sol/stack_field.hpp index df5117a4..aee92210 100644 --- a/sol/stack_field.hpp +++ b/sol/stack_field.hpp @@ -42,7 +42,6 @@ template struct field_getter, b, C> { template void apply(std::index_sequence, lua_State* L, Keys&& keys, int tableindex) { - tableindex = lua_absindex(L, tableindex); void(detail::swallow{ (get_field(L, detail::forward_get(keys), tableindex), 0)... }); reference saved(L, -1); lua_pop(L, static_cast(sizeof...(I))); @@ -51,6 +50,7 @@ struct field_getter, b, C> { template void get(lua_State* L, Keys&& keys, int tableindex = -2) { + tableindex = lua_absindex(L, tableindex); apply(std::index_sequence_for(), L, std::forward(keys), tableindex); } }; diff --git a/sol/stack_probe.hpp b/sol/stack_probe.hpp new file mode 100644 index 00000000..ca190753 --- /dev/null +++ b/sol/stack_probe.hpp @@ -0,0 +1,65 @@ +// The MIT License (MIT) + +// Copyright (c) 2013-2016 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. + +#ifndef SOL_STACK_PROBE_HPP +#define SOL_STACK_PROBE_HPP + +#include "stack_core.hpp" +#include "stack_field.hpp" +#include "stack_check.hpp" + +namespace sol { +namespace stack { +template +struct probe_field_getter { + template + probe get(lua_State* L, Key&& key, int tableindex = -2) { + get_field(L, std::forward(key), tableindex); + return probe(!check(L), 1); + } +}; + +template +struct probe_field_getter, b, C> { + template + probe apply(std::index_sequence, int sofar, lua_State* L, Keys&& keys, int tableindex) { + get_field(L, std::get(keys), tableindex); + return probe(!check(L), sofar); + } + + template + probe apply(std::index_sequence, int sofar, lua_State* L, Keys&& keys, int tableindex) { + get_field(L, std::get(keys), tableindex); + if (!maybe_indexable(L)) { + return probe(false, sofar); + } + return apply(std::index_sequence(), sofar + 1, L, std::forward(keys), tableindex); + } + + template + probe get(lua_State* L, Keys&& keys, int tableindex = -2) { + return apply(std::index_sequence_for(), 1, L, std::forward(keys), tableindex); + } +}; +} // stack +} // sol + +#endif // SOL_STACK_PROBE_HPP \ No newline at end of file diff --git a/tests.cpp b/tests.cpp index 4635f6fd..7155ed2e 100644 --- a/tests.cpp +++ b/tests.cpp @@ -651,6 +651,28 @@ TEST_CASE("tables/operator[]", "Check if operator[] retrieval and setting works REQUIRE_NOTHROW(assert1(lua.globals())); } +TEST_CASE("tables/operator[]-valid", "Test if proxies on tables can lazily evaluate validity") { + sol::state lua; + bool isFullScreen = false; + auto fullscreennopers = lua["fullscreen"]["nopers"]; + auto fullscreen = lua["fullscreen"]; + REQUIRE_FALSE(fullscreennopers.valid()); + REQUIRE_FALSE(fullscreen.valid()); + + lua["fullscreen"] = true; + + REQUIRE_FALSE(fullscreennopers.valid()); + REQUIRE(fullscreen.valid()); + isFullScreen = lua["fullscreen"]; + REQUIRE(isFullScreen); + + lua["fullscreen"] = false; + REQUIRE_FALSE(fullscreennopers.valid()); + REQUIRE(fullscreen.valid()); + isFullScreen = lua["fullscreen"]; + REQUIRE_FALSE(isFullScreen); +} + TEST_CASE("tables/usertype", "Show that we can create classes from usertype and use them") { sol::state lua;