New feature: environment plus documentation about the environment table and its usage with scripts.

This commit is contained in:
ThePhD 2017-04-02 16:10:00 -04:00
parent fb06c8a754
commit 309325d0f5
43 changed files with 923 additions and 384 deletions

3
.gitignore vendored
View File

@ -76,3 +76,6 @@ main.ilk
main.pdb main.pdb
lua-5.3.4-cxx/ lua-5.3.4-cxx/
lua-5.3.4/ lua-5.3.4/
temp.bad_runtime.lua
temp.bad_syntax.lua
temp.good.lua

View File

@ -10,7 +10,12 @@ Browse the various function and classes :doc:`Sol<../index>` utilizes to make yo
:maxdepth: 2 :maxdepth: 2
state state
reference
stack_reference
make_reference
table table
userdata
environment
proxy proxy
containers containers
nested nested
@ -25,11 +30,7 @@ Browse the various function and classes :doc:`Sol<../index>` utilizes to make yo
coroutine coroutine
error error
object object
userdata
reference
thread thread
stack_reference
make_reference
optional optional
this_state this_state
variadic_args variadic_args

View File

@ -0,0 +1,28 @@
environment
===========
encapsulation table for script sandboxing
-----------------------------------------
.. code-block:: cpp
:caption: environment
class environment : public table;
void set_environment( const reference& target, const environment& env);
This type is passed to :ref:`sol::state(_view)::script/do_x<state-script-function>` to provide an environment where local variables that are set and get retrieve. It is just a plain table, and all the same operations :doc:`from table still apply<table>`. This is important because it allows you to do things like set the table's metatable (using :doc:`sol::metatable_key<metatable_key>` for instance) and having its ``__index`` entry point to the global table, meaning you can get -- but not set -- variables from a Global environment.
Please see `an example demonstrating the above and more`_ for further information.
members
-------
.. code-block:: cpp
:caption: constructor: environment
environment(lua_State* L, sol::new_table nt, const sol::reference& fallback);
The ones from table are used here (of particular note is the ability to use ``sol::environment(my_lua_state, sol::create);`` to make a fresh, unnamed environment), plus the one unique constructor shown above. It is generally used as ``sol::environmeny my_env(my_lua_state, sol::create, my_fallback_table);``. The fallback table serves as the backup to lookup attempts on the environment table being created. It is achieved by simply creating a metatable for the ``sol::environment`` being created, and then doing ``env_metatable["__index"] = fallback;``. You can achieve fancier effects by changing the metatable of the environment to your liking, by creating it in some fashion and then setting the metatable explicitly and populating it with data, particularly with :doc:`sol::metatable_key<metatable_key>`.
.. an example demonstrating the above and more:

View File

@ -25,8 +25,9 @@ members
:caption: constructor: table :caption: constructor: table
table(lua_State* L, int index = -1); table(lua_State* L, int index = -1);
table(lua_State* L, sol::new_table nt)
Takes a table from the Lua stack at the specified index and allows a person to use all of the abstractions therein. The first takes a table from the Lua stack at the specified index and allows a person to use all of the abstractions therein. The second creates a new table using the capacity hints specified in ``sol::new_table``'s structure (``sequence_hint`` and ``map_hint``). If you don't care exactly about the capacity, create a table using ``sol::table my_table(my_lua_state, sol::create);``.
.. code-block:: cpp .. code-block:: cpp
:caption: function: get / traversing get :caption: function: get / traversing get

View File

@ -6,8 +6,8 @@ reference to a userdata
.. code-block:: cpp .. code-block:: cpp
:caption: (light\_)userdata reference :caption: (light\_)userdata reference
class userdata : public reference; class userdata : public table;
class light_userdata : public reference; class light_userdata : public table;
These type is meant to hold a reference to a (light) userdata from Lua and make it easy to push an existing userdata onto the stack. It is essentially identical to :doc:`reference<reference>` in every way, just with a definitive C++ type. These types are meant to hold a reference to a (light) userdata from Lua and make it easy to push an existing userdata onto the stack. It is essentially identical to :doc:`table<table>` in every way, just with a definitive C++ type that ensures the type is some form of userdata (helpful for trapping type errors with :doc:`safety features turned on<../safety>`). You can also use its ``.is<T>()`` and ``.as<T>()`` methods to check if its of a specific type and retrieve that type, respectively.

View File

@ -87,6 +87,7 @@ Explanations for a few categories are below (rest are self-explanatory).
* overloading: the ability to call overloaded functions, matched based on arity or type (``foo( 1 )`` from lua calls a different function then ``foo( "bark" )``). * overloading: the ability to call overloaded functions, matched based on arity or type (``foo( 1 )`` from lua calls a different function then ``foo( "bark" )``).
* Lua thread: basic wrapping of the lua thread API; ties in with coroutine. * Lua thread: basic wrapping of the lua thread API; ties in with coroutine.
* coroutines: allowing a function to be called multiple times, resuming the execution of a Lua coroutine each time * coroutines: allowing a function to be called multiple times, resuming the execution of a Lua coroutine each time
* environments: an abstraction for getting, setting and manipulating an environment, using table techniques, functions or otherwise. Typically for the purposes of sandboxing
+---------------------------+-------------+------------+----------+---------+----------+-----------+-----------+----------------+----------+----------+-----------+-----------------+--------+ +---------------------------+-------------+------------+----------+---------+----------+-----------+-----------+----------------+----------+----------+-----------+-----------------+--------+
| | plain C | luawrapper | lua-intf | luabind | Selene | Sol2 | oolua | lua-api-pp | kaguya | SLB3 | SWIG | luacppinterface | luwra | | | plain C | luawrapper | lua-intf | luabind | Selene | Sol2 | oolua | lua-api-pp | kaguya | SLB3 | SWIG | luacppinterface | luwra |
@ -124,6 +125,8 @@ Explanations for a few categories are below (rest are self-explanatory).
+---------------------------+-------------+------------+----------+---------+----------+-----------+-----------+----------------+----------+----------+-----------+-----------------+--------+ +---------------------------+-------------+------------+----------+---------+----------+-----------+-----------+----------------+----------+----------+-----------+-----------------+--------+
| Lua thread | ~ | ✗ | ~ | ✗ | ✗ | ✔ | ✔ | ✗ | ✔ | ✗ | ✗ | ✔ | ✗ | | Lua thread | ~ | ✗ | ~ | ✗ | ✗ | ✔ | ✔ | ✗ | ✔ | ✗ | ✗ | ✔ | ✗ |
+---------------------------+-------------+------------+----------+---------+----------+-----------+-----------+----------------+----------+----------+-----------+-----------------+--------+ +---------------------------+-------------+------------+----------+---------+----------+-----------+-----------+----------------+----------+----------+-----------+-----------------+--------+
| environments | ✗ | ✗ | ✗ | ✗ | ✗ | ✔ | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ |
+---------------------------+-------------+------------+----------+---------+----------+-----------+-----------+----------------+----------+----------+-----------+-----------------+--------+
| coroutines | ~ | ✗ | ~ | ✔ | ✔ | ✔ | ✗ | ✗ | ✔ | ✗ | ✗ | ✔ | ✗ | | coroutines | ~ | ✗ | ~ | ✔ | ✔ | ✔ | ✗ | ✗ | ✔ | ✗ | ✗ | ✔ | ✗ |
+---------------------------+-------------+------------+----------+---------+----------+-----------+-----------+----------------+----------+----------+-----------+-----------------+--------+ +---------------------------+-------------+------------+----------+---------+----------+-----------+-----------+----------------+----------+----------+-----------+-----------------+--------+
| no-rtti support | ✔ | ✗ | ✔ | ✗ | ✗ | ✔ | ✔ | ✗ | ✔ | ✔ | ~ | ✔ | ✔ | | no-rtti support | ✔ | ✗ | ✔ | ✗ | ✗ | ✔ | ✔ | ✗ | ✔ | ✔ | ~ | ✔ | ✔ |

View File

@ -1,3 +1,4 @@
#define SOL_CHECK_ARGUMENTS
#include <sol.hpp> #include <sol.hpp>
#include <iostream> #include <iostream>

View File

@ -1,3 +1,4 @@
#define SOL_CHECK_ARGUMENTS
#include <sol.hpp> #include <sol.hpp>
#include <iostream> #include <iostream>
@ -57,4 +58,6 @@ int main() {
} }
std::cout << std::endl; std::cout << std::endl;
return 0;
} }

View File

@ -1,4 +1,6 @@
#define SOL_CHECK_ARGUMENTS
#include <sol.hpp> #include <sol.hpp>
#include <string> #include <string>
#include <iostream> #include <iostream>

View File

@ -1,6 +1,7 @@
#include <string> #define SOL_CHECK_ARGUMENTS
#include "sol.hpp" #include "sol.hpp"
#include <string>
#include <iostream> #include <iostream>
int main() { int main() {

View File

@ -1,3 +1,4 @@
#define SOL_CHECK_ARGUMENTS
#include <sol.hpp> #include <sol.hpp>
#include <iostream> #include <iostream>
@ -34,8 +35,8 @@ namespace sol {
// its absolute position using the lua_absindex function // its absolute position using the lua_absindex function
int absolute_index = lua_absindex(L, index); int absolute_index = lua_absindex(L, index);
// Check first and second second index for being the proper types // Check first and second second index for being the proper types
bool success = stack::check<int>(L, absolute_index + 1, handler) bool success = stack::check<int>(L, absolute_index, handler)
&& stack::check<bool>(L, absolute_index, handler); && stack::check<bool>(L, absolute_index + 1, handler);
tracking.use(2); tracking.use(2);
return success; return success;
} }

53
examples/environments.cpp Normal file
View File

@ -0,0 +1,53 @@
#define SOL_CHECK_ARGUMENTS
#include <sol.hpp>
#include <vector>
#include <map>
#include <cassert>
#include <iostream>
void test_environment(std::string key, const sol::environment& env, const sol::state_view& lua) {
sol::optional<int> maybe_env_a = env[key];
sol::optional<int> maybe_global_a = lua[key];
if (maybe_global_a) {
std::cout << "\t'" << key << "' is " << maybe_global_a.value() << " in the global environment" << std::endl;
}
else {
std::cout << "\t'" << key << "' does not exist in the global environment" << std::endl;
}
if (maybe_env_a) {
std::cout << "\t'" << key << "' is " << maybe_env_a.value() << " in target environment" << std::endl;
}
else {
std::cout << "\t'" << key << "' does not exist in target environment" << std::endl;
}
}
int main(int, char**) {
std::cout << "=== environments example ===" << std::endl;
sol::state lua;
// A global variable to see if we can "fallback" into it
lua["b"] = 2142;
// Create a new environment
sol::environment plain_env(lua, sol::create);
// Use it
lua.script("a = 24", plain_env);
std::cout << "-- target: plain_env" << std::endl;
test_environment("a", plain_env, lua);
test_environment("b", plain_env, lua);
std::cout << std::endl;
// Create an environment with a fallback
sol::environment env_with_fallback(lua, sol::create, lua.globals());
// Use it
lua.script("a = 56", env_with_fallback);
std::cout << "-- target: env_with_fallback (fallback is global table)" << std::endl;
test_environment("a", env_with_fallback, lua);
test_environment("b", env_with_fallback, lua);
std::cout << std::endl;
return 0;
}

View File

@ -1,4 +1,6 @@
#define SOL_CHECK_ARGUMENTS
#include <sol.hpp> #include <sol.hpp>
#include <cassert> #include <cassert>
#include <iostream> #include <iostream>

View File

@ -1,5 +1,6 @@
#define SOL_CHECK_ARGUMENTS #define SOL_CHECK_ARGUMENTS
#include <sol.hpp> #include <sol.hpp>
#include <cassert> #include <cassert>
int main() { int main() {

View File

@ -1,6 +1,6 @@
#define SOL_CHECK_ARGUMENTS 1 #define SOL_CHECK_ARGUMENTS
#include <sol.hpp> #include <sol.hpp>
#include <string> #include <string>
#include <memory> #include <memory>
#include <iostream> #include <iostream>

View File

@ -1,3 +1,4 @@
#define SOL_CHECK_ARGUMENTS
#include <sol.hpp> #include <sol.hpp>
#include <iostream> #include <iostream>

View File

@ -1,7 +1,7 @@
#define SOL_CHECK_ARGUMENTS #define SOL_CHECK_ARGUMENTS
#include <sol.hpp> #include <sol.hpp>
#include <cassert>
#include <cassert>
#include <iostream> #include <iostream>
struct some_class { struct some_class {

View File

@ -1,5 +1,6 @@
#define SOL_CHECK_ARGUMENTS #define SOL_CHECK_ARGUMENTS
#include <sol.hpp> #include <sol.hpp>
#include <cassert> #include <cassert>
#include <iostream> #include <iostream>

View File

@ -1,3 +1,4 @@
#define SOL_CHECK_ARGUMENTS
#include <sol.hpp> #include <sol.hpp>
#include <iostream> #include <iostream>

View File

@ -1,4 +1,6 @@
#define SOL_CHECK_ARGUMENTS
#include <sol.hpp> #include <sol.hpp>
#include <string> #include <string>
#include <iostream> #include <iostream>

View File

@ -1,4 +1,6 @@
#define SOL_CHECK_ARGUMENTS
#include <sol.hpp> #include <sol.hpp>
#include <iostream> #include <iostream>
#include <cassert> #include <cassert>
#include <cmath> #include <cmath>

View File

@ -1,3 +1,4 @@
#define SOL_CHECK_ARGUMENTS
#include <sol.hpp> #include <sol.hpp>
#include <iostream> #include <iostream>

View File

@ -1,5 +1,6 @@
#define SOL_CHECK_ARGUMENTS #define SOL_CHECK_ARGUMENTS
#include <sol.hpp> #include <sol.hpp>
#include <memory> #include <memory>
#include <iostream> #include <iostream>
#include <cassert> #include <cassert>

View File

@ -1,5 +1,6 @@
#define SOL_CHECK_ARGUMENTS #define SOL_CHECK_ARGUMENTS
#include <sol.hpp> #include <sol.hpp>
#include <memory> #include <memory>
#include <iostream> #include <iostream>
#include <cassert> #include <cassert>

View File

@ -1,5 +1,6 @@
#define SOL_CHECK_ARGUMENTS #define SOL_CHECK_ARGUMENTS
#include <sol.hpp> #include <sol.hpp>
#include <iostream> #include <iostream>
#include <cassert> #include <cassert>
#include <cmath> #include <cmath>

View File

@ -1,5 +1,6 @@
#define SOL_CHECK_ARGUMENTS #define SOL_CHECK_ARGUMENTS
#include <sol.hpp> #include <sol.hpp>
#include <iostream> #include <iostream>
struct test { struct test {

View File

@ -1,4 +1,6 @@
#define SOL_CHECK_ARGUMENTS
#include <sol.hpp> #include <sol.hpp>
#include <iostream> #include <iostream>
int main() { int main() {

View File

@ -1,3 +1,4 @@
#define SOL_CHECK_ARGUMENTS
#include <sol.hpp> #include <sol.hpp>
#include <iostream> #include <iostream>

View File

@ -31,6 +31,15 @@
#ifndef SOL_NO_COMPAT #ifndef SOL_NO_COMPAT
#ifdef SOL_USING_CXX_LUA
#include "compatibility/5.1.0.h"
#include "compatibility/5.0.0.h"
#include "compatibility/5.x.x.h"
#include "compatibility/5.x.x.inl"
#else
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif
@ -42,6 +51,8 @@ extern "C" {
} }
#endif #endif
#endif // C++ Mangling for Lua
#endif // SOL_NO_COMPAT #endif // SOL_NO_COMPAT
#endif // SOL_COMPATIBILITY_HPP #endif // SOL_COMPATIBILITY_HPP

View File

@ -28,7 +28,7 @@
#include <lauxlib.h> #include <lauxlib.h>
#else #else
#include <lua.hpp> #include <lua.hpp>
#endif // C++-compiler Lua #endif // C++ Mangming for Lua
#if defined(_WIN32) || defined(_MSC_VER) #if defined(_WIN32) || defined(_MSC_VER)
#ifndef SOL_CODECVT_SUPPORT #ifndef SOL_CODECVT_SUPPORT

73
sol/environment.hpp Normal file
View File

@ -0,0 +1,73 @@
// 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_ENVIRONMENT_HPP
#define SOL_ENVIRONMENT_HPP
#include "table.hpp"
namespace sol {
template <typename base_type>
struct basic_environment : basic_table<base_type> {
private:
typedef basic_table<base_type> table_t;
public:
basic_environment() noexcept = default;
basic_environment(const basic_environment&) = default;
basic_environment(basic_environment&&) = default;
basic_environment& operator=(const basic_environment&) = default;
basic_environment& operator=(basic_environment&&) = default;
basic_environment(lua_State* L, sol::new_table t, const sol::reference& fallback) : table_t(L, std::move(t)) {
sol::stack_table mt(L, sol::new_table(0, 1));
mt.set(sol::meta_function::index, fallback);
this->set(metatable_key, mt);
mt.pop();
}
template <typename T, typename... Args, meta::enable<meta::neg<std::is_same<meta::unqualified_t<T>, basic_environment>>, meta::boolean<!(sizeof...(Args) == 2 && meta::any_same<new_table, meta::unqualified_t<Args>...>::value)>> = meta::enabler>
basic_environment(T&& arg, Args&&... args) : table_t(std::forward<T>(arg), std::forward<Args>(args)...) { }
};
template <typename E>
void set_environment(const reference& target, const basic_environment<E>& env) {
lua_State* L = target.lua_state();
#if SOL_LUA_VERSION < 502
// Use lua_setfenv
target.push();
env.push();
lua_setfenv(L, -2);
env.pop();
target.pop();
#else
// Use upvalues as explained in Lua 5.2 and beyond's manual
target.push();
env.push();
if (lua_setupvalue(L, -2, 1) == nullptr) {
env.pop();
}
target.pop();
#endif
}
} // sol
#endif // SOL_ENVIRONMENT_HPP

View File

@ -24,6 +24,7 @@
#include "reference.hpp" #include "reference.hpp"
#include "stack.hpp" #include "stack.hpp"
#include "object_base.hpp"
#include "userdata.hpp" #include "userdata.hpp"
#include "as_args.hpp" #include "as_args.hpp"
#include "variadic_args.hpp" #include "variadic_args.hpp"
@ -51,35 +52,10 @@ namespace sol {
return r; return r;
} }
template <typename base_t> template <typename base_type>
class basic_object : public base_t { class basic_object : public basic_object_base<base_type> {
private: private:
template<typename T> typedef basic_object_base<base_type> base_t;
decltype(auto) as_stack(std::true_type) const {
return stack::get<T>(base_t::lua_state(), base_t::stack_index());
}
template<typename T>
decltype(auto) as_stack(std::false_type) const {
base_t::push();
return stack::pop<T>(base_t::lua_state());
}
template<typename T>
bool is_stack(std::true_type) const {
return stack::check<T>(base_t::lua_state(), base_t::stack_index(), no_panic);
}
template<typename T>
bool is_stack(std::false_type) const {
int r = base_t::registry_index();
if (r == LUA_REFNIL)
return meta::any_same<meta::unqualified_t<T>, lua_nil_t, nullopt_t, std::nullptr_t>::value ? true : false;
if (r == LUA_NOREF)
return false;
auto pp = stack::push_pop(*this);
return stack::check<T>(base_t::lua_state(), -1, no_panic);
}
template <bool invert_and_pop = false> template <bool invert_and_pop = false>
basic_object(std::integral_constant<bool, invert_and_pop>, lua_State* L, int index = -1) noexcept : base_t(L, index) { basic_object(std::integral_constant<bool, invert_and_pop>, lua_State* L, int index = -1) noexcept : base_t(L, index) {
@ -90,7 +66,7 @@ namespace sol {
public: public:
basic_object() noexcept = default; basic_object() noexcept = default;
template <typename T, meta::enable<meta::neg<std::is_same<meta::unqualified_t<T>, basic_object>>, meta::neg<std::is_same<base_t, stack_reference>>, std::is_base_of<base_t, meta::unqualified_t<T>>> = meta::enabler> template <typename T, meta::enable<meta::neg<std::is_same<meta::unqualified_t<T>, basic_object>>, meta::neg<std::is_same<base_type, stack_reference>>, std::is_base_of<base_type, meta::unqualified_t<T>>> = meta::enabler>
basic_object(T&& r) : base_t(std::forward<T>(r)) {} basic_object(T&& r) : base_t(std::forward<T>(r)) {}
basic_object(lua_nil_t r) : base_t(r) {} basic_object(lua_nil_t r) : base_t(r) {}
basic_object(const basic_object&) = default; basic_object(const basic_object&) = default;
@ -109,22 +85,12 @@ namespace sol {
basic_object(lua_State* L, in_place_t, T&& arg, Args&&... args) noexcept : basic_object(L, in_place<T>, std::forward<T>(arg), std::forward<Args>(args)...) {} basic_object(lua_State* L, in_place_t, T&& arg, Args&&... args) noexcept : basic_object(L, in_place<T>, std::forward<T>(arg), std::forward<Args>(args)...) {}
basic_object& operator=(const basic_object&) = default; basic_object& operator=(const basic_object&) = default;
basic_object& operator=(basic_object&&) = default; basic_object& operator=(basic_object&&) = default;
basic_object& operator=(const base_t& b) { base_t::operator=(b); return *this; } basic_object& operator=(const base_type& b) { base_t::operator=(b); return *this; }
basic_object& operator=(base_t&& b) { base_t::operator=(std::move(b)); return *this; } basic_object& operator=(base_type&& b) { base_t::operator=(std::move(b)); return *this; }
template <typename Super> template <typename Super>
basic_object& operator=(const proxy_base<Super>& r) { this->operator=(r.operator basic_object()); return *this; } basic_object& operator=(const proxy_base<Super>& r) { this->operator=(r.operator basic_object()); return *this; }
template <typename Super> template <typename Super>
basic_object& operator=(proxy_base<Super>&& r) { this->operator=(r.operator basic_object()); return *this; } basic_object& operator=(proxy_base<Super>&& r) { this->operator=(r.operator basic_object()); return *this; }
template<typename T>
decltype(auto) as() const {
return as_stack<T>(std::is_same<base_t, stack_reference>());
}
template<typename T>
bool is() const {
return is_stack<T>(std::is_same<base_t, stack_reference>());
}
}; };
template <typename T> template <typename T>
@ -136,22 +102,6 @@ namespace sol {
object make_object(lua_State* L, Args&&... args) { object make_object(lua_State* L, Args&&... args) {
return make_reference<T, object, true>(L, std::forward<Args>(args)...); return make_reference<T, object, true>(L, std::forward<Args>(args)...);
} }
inline bool operator==(const object& lhs, const lua_nil_t&) {
return !lhs.valid();
}
inline bool operator==(const lua_nil_t&, const object& rhs) {
return !rhs.valid();
}
inline bool operator!=(const object& lhs, const lua_nil_t&) {
return lhs.valid();
}
inline bool operator!=(const lua_nil_t&, const object& rhs) {
return rhs.valid();
}
} // sol } // sol
#endif // SOL_OBJECT_HPP #endif // SOL_OBJECT_HPP

81
sol/object_base.hpp Normal file
View File

@ -0,0 +1,81 @@
// 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_OBJECT_BASE_HPP
#define SOL_OBJECT_BASE_HPP
#include "reference.hpp"
#include "stack.hpp"
namespace sol {
template <typename base_t>
class basic_object_base : public base_t {
private:
template<typename T>
decltype(auto) as_stack(std::true_type) const {
return stack::get<T>(base_t::lua_state(), base_t::stack_index());
}
template<typename T>
decltype(auto) as_stack(std::false_type) const {
base_t::push();
return stack::pop<T>(base_t::lua_state());
}
template<typename T>
bool is_stack(std::true_type) const {
return stack::check<T>(base_t::lua_state(), base_t::stack_index(), no_panic);
}
template<typename T>
bool is_stack(std::false_type) const {
int r = base_t::registry_index();
if (r == LUA_REFNIL)
return meta::any_same<meta::unqualified_t<T>, lua_nil_t, nullopt_t, std::nullptr_t>::value ? true : false;
if (r == LUA_NOREF)
return false;
auto pp = stack::push_pop(*this);
return stack::check<T>(base_t::lua_state(), -1, no_panic);
}
public:
basic_object_base() noexcept = default;
basic_object_base(const basic_object_base&) = default;
basic_object_base(basic_object_base&&) = default;
basic_object_base& operator=(const basic_object_base&) = default;
basic_object_base& operator=(basic_object_base&&) = default;
template <typename T, typename... Args, meta::enable<meta::neg<std::is_same<meta::unqualified_t<T>, basic_object_base>>> = meta::enabler>
basic_object_base(T&& arg, Args&&... args) : base_t(std::forward<T>(arg), std::forward<Args>(args)...) { }
template<typename T>
decltype(auto) as() const {
return as_stack<T>(std::is_same<base_t, stack_reference>());
}
template<typename T>
bool is() const {
return is_stack<T>(std::is_same<base_t, stack_reference>());
}
};
} // sol
#endif // SOL_OBJECT_BASE_HPP

View File

@ -189,6 +189,22 @@ namespace sol {
inline bool operator!= (const reference& l, const reference& r) { inline bool operator!= (const reference& l, const reference& r) {
return !operator==(l, r); return !operator==(l, r);
} }
inline bool operator==(const reference& lhs, const lua_nil_t&) {
return !lhs.valid();
}
inline bool operator==(const lua_nil_t&, const reference& rhs) {
return !rhs.valid();
}
inline bool operator!=(const reference& lhs, const lua_nil_t&) {
return lhs.valid();
}
inline bool operator!=(const lua_nil_t&, const reference& rhs) {
return rhs.valid();
}
} // sol } // sol
#endif // SOL_REFERENCE_HPP #endif // SOL_REFERENCE_HPP

View File

@ -25,7 +25,6 @@
#include "types.hpp" #include "types.hpp"
#include "reference.hpp" #include "reference.hpp"
#include "stack_reference.hpp" #include "stack_reference.hpp"
#include "userdata.hpp"
#include "tuple.hpp" #include "tuple.hpp"
#include "traits.hpp" #include "traits.hpp"
#include "tie.hpp" #include "tie.hpp"

View File

@ -22,6 +22,8 @@
#ifndef SOL_STACK_REFERENCE_HPP #ifndef SOL_STACK_REFERENCE_HPP
#define SOL_STACK_REFERENCE_HPP #define SOL_STACK_REFERENCE_HPP
#include "types.hpp"
namespace sol { namespace sol {
class stack_reference { class stack_reference {
private: private:
@ -91,6 +93,22 @@ namespace sol {
inline bool operator!= (const stack_reference& l, const stack_reference& r) { inline bool operator!= (const stack_reference& l, const stack_reference& r) {
return !operator==(l, r); return !operator==(l, r);
} }
inline bool operator==(const stack_reference& lhs, const lua_nil_t&) {
return !lhs.valid();
}
inline bool operator==(const lua_nil_t&, const stack_reference& rhs) {
return !rhs.valid();
}
inline bool operator!=(const stack_reference& lhs, const lua_nil_t&) {
return lhs.valid();
}
inline bool operator!=(const lua_nil_t&, const stack_reference& rhs) {
return rhs.valid();
}
} // sol } // sol
#endif // SOL_STACK_REFERENCE_HPP #endif // SOL_STACK_REFERENCE_HPP

View File

@ -24,6 +24,7 @@
#include "error.hpp" #include "error.hpp"
#include "table.hpp" #include "table.hpp"
#include "environment.hpp"
#include "load_result.hpp" #include "load_result.hpp"
#include <memory> #include <memory>
@ -256,6 +257,28 @@ namespace sol {
return require_core(key, [this, &filename]() {stack::script_file(L, filename); }, create_global); return require_core(key, [this, &filename]() {stack::script_file(L, filename); }, create_global);
} }
template <typename E>
protected_function_result do_string(const std::string& code, const basic_environment<E>& env) {
load_status x = static_cast<load_status>(luaL_loadstring(L, code.c_str()));
if (x != load_status::ok) {
return protected_function_result(L, -1, 0, 1, static_cast<call_status>(x));
}
protected_function pf(L, -1);
set_environment(pf, env);
return pf();
}
template <typename E>
protected_function_result do_file(const std::string& filename, const basic_environment<E>& env) {
load_status x = static_cast<load_status>(luaL_loadfile(L, filename.c_str()));
if (x != load_status::ok) {
return protected_function_result(L, -1, 0, 1, static_cast<call_status>(x));
}
protected_function pf(L, -1);
set_environment(pf, env);
return pf();
}
protected_function_result do_string(const std::string& code) { protected_function_result do_string(const std::string& code) {
load_status x = static_cast<load_status>(luaL_loadstring(L, code.c_str())); load_status x = static_cast<load_status>(luaL_loadstring(L, code.c_str()));
if (x != load_status::ok) { if (x != load_status::ok) {
@ -274,7 +297,15 @@ namespace sol {
return pf(); return pf();
} }
template <typename Fx> protected_function_result script(const std::string& code, const environment& env) {
return script(code, env, sol::default_on_error);
}
protected_function_result script_file(const std::string& filename, const environment& env) {
return script_file(filename, env, sol::default_on_error);
}
template <typename Fx, meta::disable<meta::is_specialization_of<basic_environment, meta::unqualified_t<Fx>>> = meta::enabler>
protected_function_result script(const std::string& code, Fx&& on_error) { protected_function_result script(const std::string& code, Fx&& on_error) {
protected_function_result pfr = do_string(code); protected_function_result pfr = do_string(code);
if (!pfr.valid()) { if (!pfr.valid()) {
@ -283,7 +314,7 @@ namespace sol {
return pfr; return pfr;
} }
template <typename Fx> template <typename Fx, meta::disable<meta::is_specialization_of<basic_environment, meta::unqualified_t<Fx>>> = meta::enabler>
protected_function_result script_file(const std::string& filename, Fx&& on_error) { protected_function_result script_file(const std::string& filename, Fx&& on_error) {
protected_function_result pfr = do_file(filename); protected_function_result pfr = do_file(filename);
if (!pfr.valid()) { if (!pfr.valid()) {
@ -292,6 +323,24 @@ namespace sol {
return pfr; return pfr;
} }
template <typename Fx, typename E>
protected_function_result script(const std::string& code, const basic_environment<E>& env, Fx&& on_error) {
protected_function_result pfr = do_string(code, env);
if (!pfr.valid()) {
return on_error(L, std::move(pfr));
}
return pfr;
}
template <typename Fx, typename E>
protected_function_result script_file(const std::string& filename, const basic_environment<E>& env, Fx&& on_error) {
protected_function_result pfr = do_file(filename, env);
if (!pfr.valid()) {
return on_error(L, std::move(pfr));
}
return pfr;
}
function_result script(const std::string& code) { function_result script(const std::string& code) {
int index = lua_gettop(L); int index = lua_gettop(L);
stack::script(L, code); stack::script(L, code);

View File

@ -38,8 +38,24 @@ namespace sol {
} }
} }
template <bool top_level, typename base_t> struct new_table {
class basic_table_core : public base_t { int sequence_hint = 0;
int map_hint = 0;
new_table() = default;
new_table(const new_table&) = default;
new_table(new_table&&) = default;
new_table& operator=(const new_table&) = default;
new_table& operator=(new_table&&) = default;
new_table(int sequence_hint, int map_hint = 0) : sequence_hint(sequence_hint), map_hint(map_hint) {}
};
const new_table create = new_table{};
template <bool top_level, typename base_type>
class basic_table_core : public basic_object_base<base_type> {
typedef basic_object_base<base_type> base_t;
friend class state; friend class state;
friend class state_view; friend class state_view;
@ -152,30 +168,26 @@ namespace sol {
traverse_set_deep<false>(std::forward<Keys>(keys)...); traverse_set_deep<false>(std::forward<Keys>(keys)...);
} }
basic_table_core(lua_State* L, detail::global_tag t) noexcept : reference(L, t) { } basic_table_core(lua_State* L, detail::global_tag t) noexcept : base_t(L, t) { }
public: public:
typedef basic_table_iterator<base_t> iterator; typedef basic_table_iterator<base_type> iterator;
typedef iterator const_iterator; typedef iterator const_iterator;
basic_table_core() noexcept : base_t() { } basic_table_core() noexcept = default;
template <typename T, meta::enable<meta::neg<std::is_same<meta::unqualified_t<T>, basic_table_core>>, meta::neg<std::is_same<base_t, stack_reference>>, std::is_base_of<base_t, meta::unqualified_t<T>>> = meta::enabler>
basic_table_core(T&& r) noexcept : base_t(std::forward<T>(r)) {
#ifdef SOL_CHECK_ARGUMENTS
if (!is_table<meta::unqualified_t<T>>::value) {
auto pp = stack::push_pop(*this);
stack::check<basic_table_core>(base_t::lua_state(), -1, type_panic);
}
#endif // Safety
}
basic_table_core(const basic_table_core&) = default; basic_table_core(const basic_table_core&) = default;
basic_table_core(basic_table_core&&) = default; basic_table_core(basic_table_core&&) = default;
basic_table_core& operator=(const basic_table_core&) = default; basic_table_core& operator=(const basic_table_core&) = default;
basic_table_core& operator=(basic_table_core&&) = default; basic_table_core& operator=(basic_table_core&&) = default;
basic_table_core(const stack_reference& r) : basic_table_core(r.lua_state(), r.stack_index()) {} basic_table_core(const stack_reference& r) : basic_table_core(r.lua_state(), r.stack_index()) {}
basic_table_core(stack_reference&& r) : basic_table_core(r.lua_state(), r.stack_index()) {} basic_table_core(stack_reference&& r) : basic_table_core(r.lua_state(), r.stack_index()) {}
template <typename T, meta::enable<meta::neg<std::is_integral<meta::unqualified_t<T>>>, meta::neg<std::is_same<T, ref_index>>> = meta::enabler> template <typename T, meta::enable<meta::neg<std::is_integral<meta::unqualified_t<T>>>, meta::neg<std::is_same<meta::unqualified_t<T>, ref_index>>> = meta::enabler>
basic_table_core(lua_State* L, T&& r) : basic_table_core(L, sol::ref_index(r.registry_index())) {} basic_table_core(lua_State* L, T&& r) : basic_table_core(L, sol::ref_index(r.registry_index())) {}
basic_table_core(lua_State* L, new_table nt) : base_t(L, (lua_createtable(L, nt.sequence_hint, nt.map_hint), -1)) {
if (!std::is_base_of<stack_reference, base_type>::value) {
lua_pop(L, 1);
}
}
basic_table_core(lua_State* L, int index = -1) : base_t(L, index) { basic_table_core(lua_State* L, int index = -1) : base_t(L, index) {
#ifdef SOL_CHECK_ARGUMENTS #ifdef SOL_CHECK_ARGUMENTS
stack::check<basic_table_core>(L, index, type_panic); stack::check<basic_table_core>(L, index, type_panic);
@ -187,6 +199,15 @@ namespace sol {
stack::check<basic_table_core>(L, -1, type_panic); stack::check<basic_table_core>(L, -1, type_panic);
#endif // Safety #endif // Safety
} }
template <typename T, meta::enable<meta::neg<meta::any_same<meta::unqualified_t<T>, basic_table_core>>, meta::neg<std::is_same<base_type, stack_reference>>, std::is_base_of<base_type, meta::unqualified_t<T>>> = meta::enabler>
basic_table_core(T&& r) noexcept : base_t(std::forward<T>(r)) {
#ifdef SOL_CHECK_ARGUMENTS
if (!is_table<meta::unqualified_t<T>>::value) {
auto pp = stack::push_pop(*this);
stack::check<basic_table_core>(base_t::lua_state(), -1, type_panic);
}
#endif // Safety
}
iterator begin() const { iterator begin() const {
return iterator(*this); return iterator(*this);

View File

@ -104,6 +104,9 @@ namespace sol {
return std::addressof(item); return std::addressof(item);
} }
}; };
struct unchecked_t {};
const unchecked_t unchecked = unchecked_t{};
} // detail } // detail
struct lua_nil_t {}; struct lua_nil_t {};
@ -571,10 +574,16 @@ namespace sol {
using table_core = basic_table_core<b, reference>; using table_core = basic_table_core<b, reference>;
template <bool b> template <bool b>
using stack_table_core = basic_table_core<b, stack_reference>; using stack_table_core = basic_table_core<b, stack_reference>;
template <typename T>
using basic_table = basic_table_core<false, T>;
typedef table_core<false> table; typedef table_core<false> table;
typedef table_core<true> global_table; typedef table_core<true> global_table;
typedef stack_table_core<false> stack_table; typedef stack_table_core<false> stack_table;
typedef stack_table_core<true> stack_global_table; typedef stack_table_core<true> stack_global_table;
template <typename base_t>
struct basic_environment;
using environment = basic_environment<reference>;
using stack_environment = basic_environment<stack_reference>;
template <typename T> template <typename T>
class basic_function; class basic_function;
template <typename T> template <typename T>

View File

@ -22,14 +22,16 @@
#ifndef SOL_USERDATA_HPP #ifndef SOL_USERDATA_HPP
#define SOL_USERDATA_HPP #define SOL_USERDATA_HPP
#include "reference.hpp" #include "object_base.hpp"
#include "table.hpp"
namespace sol { namespace sol {
template <typename base_t> template <typename base_type>
class basic_userdata : public base_t { class basic_userdata : public basic_table<base_type> {
typedef basic_table<base_type> base_t;
public: public:
basic_userdata() noexcept = default; basic_userdata() noexcept = default;
template <typename T, meta::enable<meta::neg<std::is_same<meta::unqualified_t<T>, basic_userdata>>, meta::neg<std::is_same<base_t, stack_reference>>, std::is_base_of<base_t, meta::unqualified_t<T>>> = meta::enabler> template <typename T, meta::enable<meta::neg<std::is_same<meta::unqualified_t<T>, basic_userdata>>, meta::neg<std::is_same<base_t, stack_reference>>, std::is_base_of<base_type, meta::unqualified_t<T>>> = meta::enabler>
basic_userdata(T&& r) noexcept : base_t(std::forward<T>(r)) { basic_userdata(T&& r) noexcept : base_t(std::forward<T>(r)) {
#ifdef SOL_CHECK_ARGUMENTS #ifdef SOL_CHECK_ARGUMENTS
if (!is_userdata<meta::unqualified_t<T>>::value) { if (!is_userdata<meta::unqualified_t<T>>::value) {
@ -44,7 +46,7 @@ namespace sol {
basic_userdata& operator=(basic_userdata&&) = default; basic_userdata& operator=(basic_userdata&&) = default;
basic_userdata(const stack_reference& r) : basic_userdata(r.lua_state(), r.stack_index()) {} basic_userdata(const stack_reference& r) : basic_userdata(r.lua_state(), r.stack_index()) {}
basic_userdata(stack_reference&& r) : basic_userdata(r.lua_state(), r.stack_index()) {} basic_userdata(stack_reference&& r) : basic_userdata(r.lua_state(), r.stack_index()) {}
template <typename T, meta::enable<meta::neg<std::is_integral<meta::unqualified_t<T>>>, meta::neg<std::is_same<T, ref_index>>> = meta::enabler> template <typename T, meta::enable<meta::neg<std::is_integral<meta::unqualified_t<T>>>, meta::neg<std::is_same<meta::unqualified_t<T>, ref_index>>> = meta::enabler>
basic_userdata(lua_State* L, T&& r) : basic_userdata(L, sol::ref_index(r.registry_index())) {} basic_userdata(lua_State* L, T&& r) : basic_userdata(L, sol::ref_index(r.registry_index())) {}
basic_userdata(lua_State* L, int index = -1) : base_t(L, index) { basic_userdata(lua_State* L, int index = -1) : base_t(L, index) {
#ifdef SOL_CHECK_ARGUMENTS #ifdef SOL_CHECK_ARGUMENTS
@ -59,14 +61,15 @@ namespace sol {
} }
}; };
template <typename base_t> template <typename base_type>
class basic_lightuserdata : public base_t { class basic_lightuserdata : public basic_object_base< base_type > {
typedef basic_object_base<base_type> base_t;
public: public:
basic_lightuserdata() noexcept = default; basic_lightuserdata() noexcept = default;
template <typename T, meta::enable<meta::neg<std::is_same<meta::unqualified_t<T>, basic_lightuserdata>>, meta::neg<std::is_same<base_t, stack_reference>>, std::is_base_of<base_t, meta::unqualified_t<T>>> = meta::enabler> template <typename T, meta::enable<meta::neg<std::is_same<meta::unqualified_t<T>, basic_lightuserdata>>, meta::neg<std::is_same<base_t, stack_reference>>, std::is_base_of<base_type, meta::unqualified_t<T>>> = meta::enabler>
basic_lightuserdata(T&& r) noexcept : base_t(std::forward<T>(r)) { basic_lightuserdata(T&& r) noexcept : base_t(std::forward<T>(r)) {
#ifdef SOL_CHECK_ARGUMENTS #ifdef SOL_CHECK_ARGUMENTS
if (!is_userdata<meta::unqualified_t<T>>::value) { if (!is_lightuserdata<meta::unqualified_t<T>>::value) {
auto pp = stack::push_pop(*this); auto pp = stack::push_pop(*this);
type_assert(base_t::lua_state(), -1, type::lightuserdata); type_assert(base_t::lua_state(), -1, type::lightuserdata);
} }
@ -78,7 +81,7 @@ namespace sol {
basic_lightuserdata& operator=(basic_lightuserdata&&) = default; basic_lightuserdata& operator=(basic_lightuserdata&&) = default;
basic_lightuserdata(const stack_reference& r) : basic_lightuserdata(r.lua_state(), r.stack_index()) {} basic_lightuserdata(const stack_reference& r) : basic_lightuserdata(r.lua_state(), r.stack_index()) {}
basic_lightuserdata(stack_reference&& r) : basic_lightuserdata(r.lua_state(), r.stack_index()) {} basic_lightuserdata(stack_reference&& r) : basic_lightuserdata(r.lua_state(), r.stack_index()) {}
template <typename T, meta::enable<meta::neg<std::is_integral<meta::unqualified_t<T>>>, meta::neg<std::is_same<T, ref_index>>> = meta::enabler> template <typename T, meta::enable<meta::neg<std::is_integral<meta::unqualified_t<T>>>, meta::neg<std::is_same<meta::unqualified_t<T>, ref_index>>> = meta::enabler>
basic_lightuserdata(lua_State* L, T&& r) : basic_lightuserdata(L, sol::ref_index(r.registry_index())) {} basic_lightuserdata(lua_State* L, T&& r) : basic_lightuserdata(L, sol::ref_index(r.registry_index())) {}
basic_lightuserdata(lua_State* L, int index = -1) : base_t(L, index) { basic_lightuserdata(lua_State* L, int index = -1) : base_t(L, index) {
#ifdef SOL_CHECK_ARGUMENTS #ifdef SOL_CHECK_ARGUMENTS

46
test_environments.cpp Normal file
View File

@ -0,0 +1,46 @@
#define SOL_CHECK_ARGUMENTS
#include <catch.hpp>
#include <sol.hpp>
#include <iostream>
#include "test_stack_guard.hpp"
TEST_CASE("environments/shadowing", "Environments can properly shadow and fallback on variables") {
sol::state lua;
lua["b"] = 2142;
SECTION("no fallback") {
sol::environment plain_env(lua, sol::create);
lua.script("a = 24", plain_env);
sol::optional<int> maybe_env_a = plain_env["a"];
sol::optional<int> maybe_global_a = lua["a"];
sol::optional<int> maybe_env_b = plain_env["b"];
sol::optional<int> maybe_global_b = lua["b"];
REQUIRE(maybe_env_a != sol::nullopt);
REQUIRE(maybe_env_a.value() == 24);
REQUIRE(maybe_env_b == sol::nullopt);
REQUIRE(maybe_global_a == sol::nullopt);
REQUIRE(maybe_global_b != sol::nullopt);
REQUIRE(maybe_global_b.value() == 2142);
}
SECTION("fallback") {
sol::environment env_with_fallback(lua, sol::create, lua.globals());
lua.script("a = 56", env_with_fallback, sol::default_on_error);
sol::optional<int> maybe_env_a = env_with_fallback["a"];
sol::optional<int> maybe_global_a = lua["a"];
sol::optional<int> maybe_env_b = env_with_fallback["b"];
sol::optional<int> maybe_global_b = lua["b"];
REQUIRE(maybe_env_a != sol::nullopt);
REQUIRE(maybe_env_a.value() == 56);
REQUIRE(maybe_env_b != sol::nullopt);
REQUIRE(maybe_env_b.value() == 2142);
REQUIRE(maybe_global_a == sol::nullopt);
REQUIRE(maybe_global_b != sol::nullopt);
REQUIRE(maybe_global_b.value() == 2142);
}
}

424
test_state.cpp Normal file
View File

@ -0,0 +1,424 @@
#define SOL_CHECK_ARGUMENTS
#include <catch.hpp>
#include <sol.hpp>
#include <iostream>
#include <fstream>
#include "test_stack_guard.hpp"
TEST_CASE("state/require_file", "opening files as 'requires'") {
static const char FILE_NAME[] = "./tmp_thingy.lua";
sol::state lua;
lua.open_libraries(sol::lib::base);
SECTION("with usertypes")
{
struct foo {
foo(int bar) : bar(bar) {}
const int bar;
};
lua.new_usertype<foo>("foo",
sol::constructors<sol::types<int>>{},
"bar", &foo::bar
);
std::fstream file(FILE_NAME, std::ios::out);
file << "return { modfunc = function () return foo.new(221) end }" << std::endl;
file.close();
const sol::table thingy1 = lua.require_file("thingy", FILE_NAME);
CHECK(thingy1.valid());
const foo foo_v = thingy1["modfunc"]();
int val1 = foo_v.bar;
CHECK(val1 == 221);
}
SECTION("simple")
{
std::fstream file(FILE_NAME, std::ios::out);
file << "return { modfunc = function () return 221 end }" << std::endl;
file.close();
const sol::table thingy1 = lua.require_file("thingy", FILE_NAME);
const sol::table thingy2 = lua.require_file("thingy", FILE_NAME);
CHECK(thingy1.valid());
CHECK(thingy2.valid());
int val1 = thingy1["modfunc"]();
int val2 = thingy2["modfunc"]();
CHECK(val1 == 221);
CHECK(val2 == 221);
// must have loaded the same table
CHECK(thingy1 == thingy2);
}
std::remove(FILE_NAME);
}
TEST_CASE("state/require_script", "opening strings as 'requires' clauses") {
std::string code = "return { modfunc = function () return 221 end }";
sol::state lua;
sol::table thingy1 = lua.require_script("thingy", code);
sol::table thingy2 = lua.require_script("thingy", code);
int val1 = thingy1["modfunc"]();
int val2 = thingy2["modfunc"]();
REQUIRE(val1 == 221);
REQUIRE(val2 == 221);
// must have loaded the same table
REQUIRE(thingy1 == thingy2);
}
TEST_CASE("state/require", "opening using a file") {
struct open {
static int open_func(lua_State* L) {
sol::state_view lua = L;
return sol::stack::push(L, lua.create_table_with("modfunc", sol::as_function([]() { return 221; })));
}
};
sol::state lua;
sol::table thingy1 = lua.require("thingy", open::open_func);
sol::table thingy2 = lua.require("thingy", open::open_func);
int val1 = thingy1["modfunc"]();
int val2 = thingy2["modfunc"]();
REQUIRE(val1 == 221);
REQUIRE(val2 == 221);
// THIS IS ONLY REQUIRED IN LUA 5.3, FOR SOME REASON
// must have loaded the same table
// REQUIRE(thingy1 == thingy2);
}
TEST_CASE("state/multi-require", "make sure that requires transfers across hand-rolled script implementation and standard requiref") {
struct open {
static int open_func(lua_State* L) {
sol::state_view lua = L;
return sol::stack::push(L, lua.create_table_with("modfunc", sol::as_function([]() { return 221; })));
}
};
std::string code = "return { modfunc = function () return 221 end }";
sol::state lua;
sol::table thingy1 = lua.require("thingy", open::open_func);
sol::table thingy2 = lua.require("thingy", open::open_func);
sol::table thingy3 = lua.require_script("thingy", code);
int val1 = thingy1["modfunc"]();
int val2 = thingy2["modfunc"]();
int val3 = thingy3["modfunc"]();
REQUIRE(val1 == 221);
REQUIRE(val2 == 221);
REQUIRE(val3 == 221);
// must have loaded the same table
// Lua is not obliged to give a shit. Thanks, Lua
//REQUIRE(thingy1 == thingy2);
// But we care, thankfully
//REQUIRE(thingy1 == thingy3);
REQUIRE(thingy2 == thingy3);
}
TEST_CASE("state/require-safety", "make sure unrelated modules aren't harmed in using requires") {
sol::state lua;
lua.open_libraries();
std::string t1 = lua.script(R"(require 'io'
return 'test1')");
sol::object ot2 = lua.require_script("test2", R"(require 'io'
return 'test2')");
std::string t2 = ot2.as<std::string>();
std::string t3 = lua.script(R"(require 'io'
return 'test3')");
REQUIRE(t1 == "test1");
REQUIRE(t2 == "test2");
REQUIRE(t3 == "test3");
}
TEST_CASE("state/leak-check", "make sure there are no humongous memory leaks in iteration") {
#if 0
sol::state lua;
lua.script(R"(
record = {}
for i=1,256 do
record[i] = i
end
function run()
for i=1,25000 do
fun(record)
end
end
function run2()
for i=1,50000 do
fun(record)
end
end
)");
lua["fun"] = [](const sol::table &t) {
//removing the for loop fixes the memory leak
auto b = t.begin();
auto e = t.end();
for (; b != e; ++b) {
}
};
size_t beforewarmup = lua.memory_used();
lua["run"]();
size_t beforerun = lua.memory_used();
lua["run"]();
size_t afterrun = lua.memory_used();
lua["run2"]();
size_t afterrun2 = lua.memory_used();
// Less memory used before the warmup
REQUIRE(beforewarmup <= beforerun);
// Iteration size and such does not bloat or affect memory
// (these are weak checks but they'll warn us nonetheless if something goes wrong)
REQUIRE(beforerun == afterrun);
REQUIRE(afterrun == afterrun2);
#else
REQUIRE(true);
#endif
}
TEST_CASE("state/script-returns", "make sure script returns are done properly") {
std::string script =
R"(
local example =
{
str = "this is a string",
num = 1234,
func = function(self)
print(self.str)
return "fstr"
end
}
return example;
)";
auto bar = [&script](sol::this_state l) {
sol::state_view lua = l;
sol::table data = lua.script(script);
std::string str = data["str"];
int num = data["num"];
std::string fstr = data["func"](data);
REQUIRE(str == "this is a string");
REQUIRE(fstr == "fstr");
REQUIRE(num == 1234);
};
auto foo = [&script](int, sol::this_state l) {
sol::state_view lua = l;
sol::table data = lua.script(script);
std::string str = data["str"];
int num = data["num"];
std::string fstr = data["func"](data);
REQUIRE(str == "this is a string");
REQUIRE(fstr == "fstr");
REQUIRE(num == 1234);
};
auto bar2 = [&script](sol::this_state l) {
sol::state_view lua = l;
sol::table data = lua.do_string(script);
std::string str = data["str"];
int num = data["num"];
std::string fstr = data["func"](data);
REQUIRE(str == "this is a string");
REQUIRE(fstr == "fstr");
REQUIRE(num == 1234);
};
auto foo2 = [&script](int, sol::this_state l) {
sol::state_view lua = l;
sol::table data = lua.do_string(script);
std::string str = data["str"];
int num = data["num"];
std::string fstr = data["func"](data);
REQUIRE(str == "this is a string");
REQUIRE(fstr == "fstr");
REQUIRE(num == 1234);
};
sol::state lua;
lua.open_libraries();
lua.set_function("foo", foo);
lua.set_function("foo2", foo2);
lua.set_function("bar", bar);
lua.set_function("bar2", bar2);
lua.script("bar() bar2() foo(1) foo2(1)");
}
TEST_CASE("state/copy-move", "ensure state can be properly copied and moved") {
sol::state lua;
lua["a"] = 1;
sol::state lua2(std::move(lua));
int a2 = lua2["a"];
REQUIRE(a2 == 1);
lua = std::move(lua2);
int a = lua["a"];
REQUIRE(a == 1);
}
TEST_CASE("state/requires-reload", "ensure that reloading semantics do not cause a crash") {
sol::state lua;
lua.open_libraries();
lua.script("require 'io'\nreturn 'test1'");
lua.require_script("test2", "require 'io'\nreturn 'test2'");
lua.script("require 'io'\nreturn 'test3'");
}
TEST_CASE("state/script-do-load", "test success and failure cases for loading and running scripts") {
const static std::string bad_syntax = "weird\n%$@symb\nols";
static const char file_bad_syntax[] = "./temp.bad_syntax.lua";
const static std::string bad_runtime = "bad.code = 20";
static const char file_bad_runtime[] = "./temp.bad_runtime.lua";
const static std::string good = "a = 21\nreturn a";
static const char file_good[] = "./temp.good.lua";
SECTION("script") {
sol::state lua;
int ar = lua.script(good);
int a = lua["a"];
REQUIRE(a == 21);
REQUIRE(ar == 21);
}
SECTION("script-handler") {
sol::state lua;
auto errbs = lua.script(bad_syntax, sol::simple_on_error);
REQUIRE(!errbs.valid());
auto errbr = lua.script(bad_runtime, sol::simple_on_error);
REQUIRE(!errbr.valid());
auto result = lua.script(good, sol::simple_on_error);
int a = lua["a"];
int ar = result;
REQUIRE(result.valid());
REQUIRE(a == 21);
REQUIRE(ar == 21);
}
SECTION("do_string") {
sol::state lua;
auto errbs = lua.do_string(bad_syntax);
REQUIRE(!errbs.valid());
auto errbr = lua.do_string(bad_runtime);
REQUIRE(!errbr.valid());
auto result = lua.do_string(good);
int a = lua["a"];
int ar = result;
REQUIRE(result.valid());
REQUIRE(a == 21);
REQUIRE(ar == 21);
}
SECTION("load_string") {
sol::state lua;
auto errbsload = lua.load(bad_syntax);
REQUIRE(!errbsload.valid());
sol::load_result errbrload = lua.load(bad_runtime);
REQUIRE(errbrload.valid());
sol::protected_function errbrpf = errbrload;
auto errbr = errbrpf();
REQUIRE(!errbr.valid());
sol::load_result resultload = lua.load(good);
REQUIRE(resultload.valid());
sol::protected_function resultpf = resultload;
auto result = resultpf();
int a = lua["a"];
int ar = result;
REQUIRE(result.valid());
REQUIRE(a == 21);
REQUIRE(ar == 21);
}
{
std::ofstream fbs(file_bad_syntax, std::ios::out);
fbs << bad_syntax;
std::ofstream fbr(file_bad_runtime, std::ios::out);
fbr << bad_runtime;
std::ofstream fg(file_good, std::ios::out);
fg << good;
}
SECTION("script_file") {
sol::state lua;
int ar = lua.script_file(file_good);
int a = lua["a"];
REQUIRE(a == 21);
REQUIRE(ar == 21);
}
SECTION("script_file-handler") {
sol::state lua;
auto errbs = lua.script_file(file_bad_syntax, sol::simple_on_error);
REQUIRE(!errbs.valid());
auto errbr = lua.script_file(file_bad_runtime, sol::simple_on_error);
REQUIRE(!errbr.valid());
auto result = lua.script_file(file_good, sol::simple_on_error);
int a = lua["a"];
int ar = result;
REQUIRE(result.valid());
REQUIRE(a == 21);
REQUIRE(ar == 21);
}
SECTION("do_file") {
sol::state lua;
auto errbs = lua.do_file(file_bad_syntax);
REQUIRE(!errbs.valid());
auto errbr = lua.do_file(file_bad_runtime);
REQUIRE(!errbr.valid());
auto result = lua.do_file(file_good);
int a = lua["a"];
int ar = result;
REQUIRE(result.valid());
REQUIRE(a == 21);
REQUIRE(ar == 21);
}
SECTION("load_file") {
sol::state lua;
auto errbsload = lua.load_file(file_bad_syntax);
REQUIRE(!errbsload.valid());
sol::load_result errbrload = lua.load_file(file_bad_runtime);
REQUIRE(errbrload.valid());
sol::protected_function errbrpf = errbrload;
auto errbr = errbrpf();
REQUIRE(!errbr.valid());
sol::load_result resultload = lua.load_file(file_good);
REQUIRE(resultload.valid());
sol::protected_function resultpf = resultload;
auto result = resultpf();
int a = lua["a"];
int ar = result;
REQUIRE(result.valid());
REQUIRE(a == 21);
REQUIRE(ar == 21);
}
}

290
tests.cpp
View File

@ -286,19 +286,23 @@ TEST_CASE("object/conversions", "make sure all basic reference types can be made
lua["l"] = static_cast<void*>(nullptr); lua["l"] = static_cast<void*>(nullptr);
sol::table t = lua.create_table(); sol::table t = lua.create_table();
sol::table t2(lua, sol::create);
sol::thread th = sol::thread::create(lua); sol::thread th = sol::thread::create(lua);
sol::function f = lua["f"]; sol::function f = lua["f"];
sol::protected_function pf = lua["f"]; sol::protected_function pf = lua["f"];
sol::userdata ud = lua["d"]; sol::userdata ud = lua["d"];
sol::lightuserdata lud = lua["l"]; sol::lightuserdata lud = lua["l"];
sol::environment env(lua, sol::create);
sol::object ot(t); sol::object ot(t);
sol::object ot2 = ot; sol::object ot2(t2);
sol::object oteq = ot;
sol::object oth(th); sol::object oth(th);
sol::object of(f); sol::object of(f);
sol::object opf(pf); sol::object opf(pf);
sol::object od(ud); sol::object od(ud);
sol::object ol(lud); sol::object ol(lud);
sol::object oenv(env);
auto oni = sol::make_object(lua, 50); auto oni = sol::make_object(lua, 50);
auto ond = sol::make_object(lua, 50.0); auto ond = sol::make_object(lua, 50.0);
@ -311,6 +315,7 @@ TEST_CASE("object/conversions", "make sure all basic reference types can be made
REQUIRE(ot.get_type() == sol::type::table); REQUIRE(ot.get_type() == sol::type::table);
REQUIRE(ot2.get_type() == sol::type::table); REQUIRE(ot2.get_type() == sol::type::table);
REQUIRE(oteq.get_type() == sol::type::table);
REQUIRE(oth.get_type() == sol::type::thread); REQUIRE(oth.get_type() == sol::type::thread);
REQUIRE(of.get_type() == sol::type::function); REQUIRE(of.get_type() == sol::type::function);
REQUIRE(opf.get_type() == sol::type::function); REQUIRE(opf.get_type() == sol::type::function);
@ -321,143 +326,7 @@ TEST_CASE("object/conversions", "make sure all basic reference types can be made
REQUIRE(osl.get_type() == sol::type::string); REQUIRE(osl.get_type() == sol::type::string);
REQUIRE(os.get_type() == sol::type::string); REQUIRE(os.get_type() == sol::type::string);
REQUIRE(omn.get_type() == sol::type::nil); REQUIRE(omn.get_type() == sol::type::nil);
} REQUIRE(oenv.get_type() == sol::type::table);
TEST_CASE("state/require_file", "opening files as 'requires'") {
static const char FILE_NAME[] = "./tmp_thingy.lua";
sol::state lua;
lua.open_libraries(sol::lib::base);
SECTION("with usertypes")
{
struct foo {
foo(int bar) : bar(bar) {}
const int bar;
};
lua.new_usertype<foo>("foo",
sol::constructors<sol::types<int>>{},
"bar", &foo::bar
);
std::fstream file(FILE_NAME, std::ios::out);
file << "return { modfunc = function () return foo.new(221) end }" << std::endl;
file.close();
const sol::table thingy1 = lua.require_file("thingy", FILE_NAME);
CHECK(thingy1.valid());
const foo foo_v = thingy1["modfunc"]();
int val1 = foo_v.bar;
CHECK(val1 == 221);
}
SECTION("simple")
{
std::fstream file(FILE_NAME, std::ios::out);
file << "return { modfunc = function () return 221 end }" << std::endl;
file.close();
const sol::table thingy1 = lua.require_file("thingy", FILE_NAME);
const sol::table thingy2 = lua.require_file("thingy", FILE_NAME);
CHECK(thingy1.valid());
CHECK(thingy2.valid());
int val1 = thingy1["modfunc"]();
int val2 = thingy2["modfunc"]();
CHECK(val1 == 221);
CHECK(val2 == 221);
// must have loaded the same table
CHECK(thingy1 == thingy2);
}
std::remove(FILE_NAME);
}
TEST_CASE("state/require_script", "opening strings as 'requires' clauses") {
std::string code = "return { modfunc = function () return 221 end }";
sol::state lua;
sol::table thingy1 = lua.require_script("thingy", code);
sol::table thingy2 = lua.require_script("thingy", code);
int val1 = thingy1["modfunc"]();
int val2 = thingy2["modfunc"]();
REQUIRE(val1 == 221);
REQUIRE(val2 == 221);
// must have loaded the same table
REQUIRE(thingy1 == thingy2);
}
TEST_CASE("state/require", "opening using a file") {
struct open {
static int open_func(lua_State* L) {
sol::state_view lua = L;
return sol::stack::push(L, lua.create_table_with("modfunc", sol::as_function([]() { return 221; })));
}
};
sol::state lua;
sol::table thingy1 = lua.require("thingy", open::open_func);
sol::table thingy2 = lua.require("thingy", open::open_func);
int val1 = thingy1["modfunc"]();
int val2 = thingy2["modfunc"]();
REQUIRE(val1 == 221);
REQUIRE(val2 == 221);
// THIS IS ONLY REQUIRED IN LUA 5.3, FOR SOME REASON
// must have loaded the same table
// REQUIRE(thingy1 == thingy2);
}
TEST_CASE("state/multi-require", "make sure that requires transfers across hand-rolled script implementation and standard requiref") {
struct open {
static int open_func(lua_State* L) {
sol::state_view lua = L;
return sol::stack::push(L, lua.create_table_with("modfunc", sol::as_function([]() { return 221; })));
}
};
std::string code = "return { modfunc = function () return 221 end }";
sol::state lua;
sol::table thingy1 = lua.require("thingy", open::open_func);
sol::table thingy2 = lua.require("thingy", open::open_func);
sol::table thingy3 = lua.require_script("thingy", code);
int val1 = thingy1["modfunc"]();
int val2 = thingy2["modfunc"]();
int val3 = thingy3["modfunc"]();
REQUIRE(val1 == 221);
REQUIRE(val2 == 221);
REQUIRE(val3 == 221);
// must have loaded the same table
// Lua is not obliged to give a shit. Thanks, Lua
//REQUIRE(thingy1 == thingy2);
// But we care, thankfully
//REQUIRE(thingy1 == thingy3);
REQUIRE(thingy2 == thingy3);
}
TEST_CASE("state/require-safety", "make sure unrelated modules aren't harmed in using requires") {
sol::state lua;
lua.open_libraries();
std::string t1 = lua.script(R"(require 'io'
return 'test1')");
sol::object ot2 = lua.require_script("test2", R"(require 'io'
return 'test2')");
std::string t2 = ot2.as<std::string>();
std::string t3 = lua.script(R"(require 'io'
return 'test3')");
REQUIRE(t1 == "test1");
REQUIRE(t2 == "test2");
REQUIRE(t3 == "test3");
} }
TEST_CASE("feature/indexing-overrides", "make sure index functions can be overridden on types") { TEST_CASE("feature/indexing-overrides", "make sure index functions can be overridden on types") {
@ -732,151 +601,6 @@ TEST_CASE("numbers/integers", "make sure integers are detectable on most platfor
REQUIRE(b_is_double); REQUIRE(b_is_double);
} }
TEST_CASE("state/leak-check", "make sure there are no humongous memory leaks in iteration") {
#if 0
sol::state lua;
lua.script(R"(
record = {}
for i=1,256 do
record[i] = i
end
function run()
for i=1,25000 do
fun(record)
end
end
function run2()
for i=1,50000 do
fun(record)
end
end
)");
lua["fun"] = [](const sol::table &t) {
//removing the for loop fixes the memory leak
auto b = t.begin();
auto e = t.end();
for (; b != e; ++b) {
}
};
size_t beforewarmup = lua.memory_used();
lua["run"]();
size_t beforerun = lua.memory_used();
lua["run"]();
size_t afterrun = lua.memory_used();
lua["run2"]();
size_t afterrun2 = lua.memory_used();
// Less memory used before the warmup
REQUIRE(beforewarmup <= beforerun);
// Iteration size and such does not bloat or affect memory
// (these are weak checks but they'll warn us nonetheless if something goes wrong)
REQUIRE(beforerun == afterrun);
REQUIRE(afterrun == afterrun2);
#else
REQUIRE(true);
#endif
}
TEST_CASE("state/script-returns", "make sure script returns are done properly") {
std::string script =
R"(
local example =
{
str = "this is a string",
num = 1234,
func = function(self)
print(self.str)
return "fstr"
end
}
return example;
)";
auto bar = [&script](sol::this_state l) {
sol::state_view lua = l;
sol::table data = lua.script(script);
std::string str = data["str"];
int num = data["num"];
std::string fstr = data["func"](data);
REQUIRE(str == "this is a string");
REQUIRE(fstr == "fstr");
REQUIRE(num == 1234);
};
auto foo = [&script](int, sol::this_state l) {
sol::state_view lua = l;
sol::table data = lua.script(script);
std::string str = data["str"];
int num = data["num"];
std::string fstr = data["func"](data);
REQUIRE(str == "this is a string");
REQUIRE(fstr == "fstr");
REQUIRE(num == 1234);
};
auto bar2 = [&script](sol::this_state l) {
sol::state_view lua = l;
sol::table data = lua.do_string(script);
std::string str = data["str"];
int num = data["num"];
std::string fstr = data["func"](data);
REQUIRE(str == "this is a string");
REQUIRE(fstr == "fstr");
REQUIRE(num == 1234);
};
auto foo2 = [&script](int, sol::this_state l) {
sol::state_view lua = l;
sol::table data = lua.do_string(script);
std::string str = data["str"];
int num = data["num"];
std::string fstr = data["func"](data);
REQUIRE(str == "this is a string");
REQUIRE(fstr == "fstr");
REQUIRE(num == 1234);
};
sol::state lua;
lua.open_libraries();
lua.set_function("foo", foo);
lua.set_function("foo2", foo2);
lua.set_function("bar", bar);
lua.set_function("bar2", bar2);
lua.script("bar() bar2() foo(1) foo2(1)");
}
TEST_CASE("state/copy-move", "ensure state can be properly copied and moved") {
sol::state lua;
lua["a"] = 1;
sol::state lua2(std::move(lua));
int a2 = lua2["a"];
REQUIRE(a2 == 1);
lua = std::move(lua2);
int a = lua["a"];
REQUIRE(a == 1);
}
TEST_CASE("requires/reload", "ensure that reloading semantics do not cause a crash") {
sol::state lua;
lua.open_libraries();
lua.script("require 'io'\nreturn 'test1'");
lua.require_script("test2", "require 'io'\nreturn 'test2'");
lua.script("require 'io'\nreturn 'test3'");
}
TEST_CASE("object/is-method", "test whether or not the is abstraction works properly for a user-defined type") { TEST_CASE("object/is-method", "test whether or not the is abstraction works properly for a user-defined type") {
struct thing {}; struct thing {};