make sure tests don't throw where possible

edit stack checks
update container_usertype_as_container examples
This commit is contained in:
ThePhD 2017-08-11 18:12:13 -04:00
parent 2e5d31983a
commit 7af8b4e80a
12 changed files with 151 additions and 41 deletions

View File

@ -17,7 +17,7 @@ Whatever the case is, you need it to be returned to Lua and have many of the tra
See `this container example`_ to see how it works. See `this container example`_ to see how it works.
.. _this container example: https://github.com/ThePhD/sol2/blob/develop/examples/container_as_container.cpp .. _this container example: https://github.com/ThePhD/sol2/blob/develop/examples/container_usertype_as_container.cpp
.. _Container requirements: http://en.cppreference.com/w/cpp/concept/Container .. _Container requirements: http://en.cppreference.com/w/cpp/concept/Container
.. _Sequence Container requirements: http://en.cppreference.com/w/cpp/concept/SequenceContainer .. _Sequence Container requirements: http://en.cppreference.com/w/cpp/concept/SequenceContainer
.. _forward_list: http://en.cppreference.com/w/cpp/container/forward_list .. _forward_list: http://en.cppreference.com/w/cpp/container/forward_list

View File

@ -30,6 +30,12 @@ There are lots of reasons for compiler linker errors. A common one is not knowin
However, when the target Lua library is compiled with C++, one must change the calling convention and name mangling scheme by getting rid of the ``extern 'C'`` block. This can be achieved by adding ``#define SOL_USING_CXX_LUA`` before including sol2, or by adding it to your compilation's command line. However, when the target Lua library is compiled with C++, one must change the calling convention and name mangling scheme by getting rid of the ``extern 'C'`` block. This can be achieved by adding ``#define SOL_USING_CXX_LUA`` before including sol2, or by adding it to your compilation's command line.
"caught (...) exception" errors
-------------------------------
Sometimes, you expect properly written errors and instead receive an error about catching a ``...`` exception instead. This might mean that you either built Lua as C++ or are using a framework like LuaJIT that has full interopability support for exceptions on certain system types (x64 for LuaJIT 2.0.5, x86 and x64 on LuaJIT 2.1.x-beta and later).
Please make sure to use the ``SOL_EXCEPTIONS_SAFE_PROPAGATION`` define before including sol2 to make this work out. You can read more :ref:`at the exception page here<exception-interop>`.
Catch and CRASH! Catch and CRASH!
---------------- ----------------

View File

@ -49,6 +49,9 @@ It is important to note that a popular 5.1 distribution of Lua, LuaJIT, has some
Testing in `this closed issue`_ that it doesn't play nice on 64-bit Linux in many cases either, especially when it hits an error internal to the interpreter (and does not go through Sol). We do have tests, however, that compile for our continuous integration check-ins that check this functionality across several compilers and platforms to keep you protected and given hard, strong guarantees for what happens if you throw in a function bound by Sol. If you stray outside the realm of Sol's protection, however... Good luck. Testing in `this closed issue`_ that it doesn't play nice on 64-bit Linux in many cases either, especially when it hits an error internal to the interpreter (and does not go through Sol). We do have tests, however, that compile for our continuous integration check-ins that check this functionality across several compilers and platforms to keep you protected and given hard, strong guarantees for what happens if you throw in a function bound by Sol. If you stray outside the realm of Sol's protection, however... Good luck.
.. _exception-interop:
Lua and LuaJIT C++ Exception Full Interoperability Lua and LuaJIT C++ Exception Full Interoperability
-------------------------------------------------- --------------------------------------------------

View File

@ -16,13 +16,17 @@ Note that you can obtain safety with regards to functions you bind by using the
``SOL_SAFE_FUNCTION`` triggers the following change: ``SOL_SAFE_FUNCTION`` triggers the following change:
* All uses of ``sol::function`` and ``sol::stack_function`` will default to ``sol::protected_function`` and ``sol::stack_protected_function``, respectively, rather than ``sol::unsafe_function`` and ``sol::stack_unsafe_function``. * All uses of ``sol::function`` and ``sol::stack_function`` will default to ``sol::protected_function`` and ``sol::stack_protected_function``, respectively, rather than ``sol::unsafe_function`` and ``sol::stack_unsafe_function``.
* Not turned on by default under any detectible compiler settings: you must turn this one on manually * **Not** turned on by default under any detectible compiler settings: *you must turn this one on manually*
``SOL_CHECK_ARGUMENTS`` triggers the following changes: ``SOL_CHECK_ARGUMENTS`` triggers the following changes:
* ``sol::stack::get`` (used everywhere) defaults to using ``sol::stack::check_get`` and dereferencing the argument. It uses ``sol::type_panic`` as the handler if something goes wrong * ``sol::stack::get`` (used everywhere) defaults to using ``sol::stack::check_get`` and dereferencing the argument. It uses ``sol::type_panic`` as the handler if something goes wrong
* ``lua_tolstring`` conversions are not permitted on numbers: through the API: only actual strings are allowed. This is necessary to allow :doc:`sol::overload<api/overload>` to work properly * ``lua_tolstring`` conversions are not permitted on numbers: through the API: only actual strings are allowed. This is necessary to allow :doc:`sol::overload<api/overload>` to work properly
* ``sol::stack::call`` and its variants will, if no templated boolean is specified, check all of the arguments for a function call * ``sol::stack::call`` and its variants will, if no templated boolean is specified, check all of the arguments for a function call
* If ``SOL_SAFE_USERTYPE`` is not defined, it gets defined to turn being on and the effects described above kick in * If ``SOL_SAFE_USERTYPE`` is not defined, it gets defined to turn being on and the effects described above kick in
* Numbers will also be checked to see if they fit within a ``lua_Number`` if there is no ``lua_Integer`` type available that can fit your signed or unsigned number. You can opt-out of this behavior with ``SOL_NO_CHECK_NUMBER_PRECISION``
``SOL_NO_CHECK_NUMBER_PRECISION`` triggers the following changes:
* If ``SOL_CHECK_ARGUMENTS`` is defined, turns off number precision and integer precision fitting when pushing numbers into sol2
Tests are compiled with this on to ensure everything is going as expected. Remember that if you want these features, you must explicitly turn them on all of them to be sure you are getting them. Tests are compiled with this on to ensure everything is going as expected. Remember that if you want these features, you must explicitly turn them on all of them to be sure you are getting them.
@ -44,4 +48,4 @@ As a side note, binding functions with default parameters does not magically bin
.. warning:: .. warning::
Do NOT save the return type of a :ref:`function_result<function-result>` with ``auto``, as in ``auto numwoof = woof(20);``, and do NOT store it anywhere. See :ref:`here<function-result-warning>`. Do **NOT** save the return type of a :ref:`function_result<function-result>` with ``auto``, as in ``auto numwoof = woof(20);``, and do NOT store it anywhere unless you are exactly aware of the consequences of messing with the stack. See :ref:`here<function-result-warning>` for more information.

View File

@ -20,8 +20,8 @@
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// This file was generated with a script. // This file was generated with a script.
// Generated 2017-08-11 12:59:23.551177 UTC // Generated 2017-08-11 22:03:08.549881 UTC
// This header was generated with sol v2.18.0 (revision 5e109c2) // This header was generated with sol v2.18.0 (revision 2e5d319)
// https://github.com/ThePhD/sol2 // https://github.com/ThePhD/sol2
#ifndef SOL_SINGLE_INCLUDE_HPP #ifndef SOL_SINGLE_INCLUDE_HPP
@ -7121,11 +7121,16 @@ namespace sol {
int isnum = 0; int isnum = 0;
const lua_Number value = lua_tonumberx(L, index, &isnum); const lua_Number value = lua_tonumberx(L, index, &isnum);
if (isnum != 0) { if (isnum != 0) {
#if 1 // defined(SOL_CHECK_ARGUMENTS) && !defined(SOL_NO_CHECK_NUMBER_PRECISION)
const auto integer_value = std::llround(value); const auto integer_value = std::llround(value);
if (static_cast<lua_Number>(integer_value) == value) { if (static_cast<lua_Number>(integer_value) == value) {
tracking.use(1); tracking.use(1);
return static_cast<T>(integer_value); return static_cast<T>(integer_value);
} }
#else
tracking.use(1);
return static_cast<T>(value);
#endif
} }
const type t = type_of(L, index); const type t = type_of(L, index);
tracking.use(static_cast<int>(t != type::none)); tracking.use(static_cast<int>(t != type::none));
@ -7497,7 +7502,7 @@ namespace sol {
static int push(lua_State* L, const T& value) { static int push(lua_State* L, const T& value) {
#if SOL_LUA_VERSION >= 503 #if SOL_LUA_VERSION >= 503
static auto integer_value_fits = [](T const& value) { static auto integer_value_fits = [](T const& value) {
if (sizeof(T) < sizeof(lua_Integer) || std::is_signed<T>::value && sizeof(T) == sizeof(lua_Integer)) { if (sizeof(T) < sizeof(lua_Integer) || (std::is_signed<T>::value && sizeof(T) == sizeof(lua_Integer))) {
return true; return true;
} }
auto u_min = static_cast<std::intmax_t>(std::numeric_limits<lua_Integer>::min()); auto u_min = static_cast<std::intmax_t>(std::numeric_limits<lua_Integer>::min());
@ -7513,11 +7518,7 @@ namespace sol {
#endif #endif
#if defined(SOL_CHECK_ARGUMENTS) && !defined(SOL_NO_CHECK_NUMBER_PRECISION) #if defined(SOL_CHECK_ARGUMENTS) && !defined(SOL_NO_CHECK_NUMBER_PRECISION)
if (static_cast<T>(std::llround(static_cast<lua_Number>(value))) != value) { if (static_cast<T>(std::llround(static_cast<lua_Number>(value))) != value) {
#ifndef SOL_NO_EXCEPTIONS luaL_error(L, "integer value will be misrepresented in lua");
throw sol::error("The integer will be misrepresented in lua.");
#else
assert(false && "The integer will be misrepresented in lua.");
#endif
} }
#endif #endif
lua_pushnumber(L, static_cast<lua_Number>(value)); lua_pushnumber(L, static_cast<lua_Number>(value));
@ -12215,11 +12216,11 @@ namespace sol {
as_container_t(T value) : source(std::move(value)) {} as_container_t(T value) : source(std::move(value)) {}
operator T() { operator std::add_rvalue_reference_t<T>() {
return std::move(source); return std::move(source);
} }
operator std::add_const_t<std::add_lvalue_reference_t<T>>() const { operator std::add_lvalue_reference_t<std::add_const_t<T>>() const {
return source; return source;
} }
}; };
@ -13631,7 +13632,7 @@ namespace sol {
as_container_t<std::remove_pointer_t<T>>, as_container_t<std::remove_pointer_t<T>>,
std::remove_pointer_t<T> std::remove_pointer_t<T>
>> meta_cumt; >> meta_cumt;
static const char* metakey = is_shim ? &usertype_traits<as_container_t<T>>::metatable()[0] : &usertype_traits<T>::metatable()[0]; static const char* metakey = is_shim ? &usertype_traits<as_container_t<std::remove_pointer_t<T>>>::metatable()[0] : &usertype_traits<T>::metatable()[0];
static const std::array<luaL_Reg, 16> reg = { { static const std::array<luaL_Reg, 16> reg = { {
{ "__pairs", &meta_cumt::pairs_call }, { "__pairs", &meta_cumt::pairs_call },
{ "__ipairs", &meta_cumt::pairs_call }, { "__ipairs", &meta_cumt::pairs_call },
@ -13663,24 +13664,32 @@ namespace sol {
struct pusher<as_container_t<T>> { struct pusher<as_container_t<T>> {
typedef meta::unqualified_t<T> C; typedef meta::unqualified_t<T> C;
static int push(std::true_type, lua_State* L, const C& cont) { static int push_lvalue(std::true_type, lua_State* L, const C& cont) {
stack_detail::metatable_setup<C, true> fx(L); stack_detail::metatable_setup<C*, true> fx(L);
return pusher<detail::as_pointer_tag<const C>>{}.push_fx(L, fx, detail::ptr(cont)); return pusher<detail::as_pointer_tag<const C>>{}.push_fx(L, fx, detail::ptr(cont));
} }
static int push(std::false_type, lua_State* L, const C& cont) { static int push_lvalue(std::false_type, lua_State* L, const C& cont) {
stack_detail::metatable_setup<C, true> fx(L); stack_detail::metatable_setup<C, true> fx(L);
return pusher<detail::as_value_tag<C>>{}.push_fx(L, fx, cont); return pusher<detail::as_value_tag<C>>{}.push_fx(L, fx, cont);
} }
static int push(lua_State* L, const C& cont) { static int push_rvalue(std::true_type, lua_State* L, C&& cont) {
return push(std::is_lvalue_reference<T>(), L, cont);
}
static int push(lua_State* L, C&& cont) {
stack_detail::metatable_setup<C, true> fx(L); stack_detail::metatable_setup<C, true> fx(L);
return pusher<detail::as_value_tag<C>>{}.push_fx(L, fx, std::move(cont)); return pusher<detail::as_value_tag<C>>{}.push_fx(L, fx, std::move(cont));
} }
static int push_rvalue(std::false_type, lua_State* L, const C& cont) {
return push_lvalue(std::is_lvalue_reference<T>(), L, cont);
}
static int push(lua_State* L, const as_container_t<T>& as_cont) {
return push_lvalue(std::is_lvalue_reference<T>(), L, as_cont.source);
}
static int push(lua_State* L, as_container_t<T>&& as_cont) {
return push_rvalue(meta::all<std::is_rvalue_reference<T>, meta::neg<std::is_lvalue_reference<T>>>(), L, std::forward<T>(as_cont.source));
}
}; };
template<typename T> template<typename T>

View File

@ -37,11 +37,11 @@ namespace sol {
as_container_t(T value) : source(std::move(value)) {} as_container_t(T value) : source(std::move(value)) {}
operator T() { operator std::add_rvalue_reference_t<T>() {
return std::move(source); return std::move(source);
} }
operator std::add_const_t<std::add_lvalue_reference_t<T>>() const { operator std::add_lvalue_reference_t<std::add_const_t<T>>() const {
return source; return source;
} }
}; };

View File

@ -260,7 +260,7 @@ namespace sol {
as_container_t<std::remove_pointer_t<T>>, as_container_t<std::remove_pointer_t<T>>,
std::remove_pointer_t<T> std::remove_pointer_t<T>
>> meta_cumt; >> meta_cumt;
static const char* metakey = is_shim ? &usertype_traits<as_container_t<T>>::metatable()[0] : &usertype_traits<T>::metatable()[0]; static const char* metakey = is_shim ? &usertype_traits<as_container_t<std::remove_pointer_t<T>>>::metatable()[0] : &usertype_traits<T>::metatable()[0];
static const std::array<luaL_Reg, 16> reg = { { static const std::array<luaL_Reg, 16> reg = { {
{ "__pairs", &meta_cumt::pairs_call }, { "__pairs", &meta_cumt::pairs_call },
{ "__ipairs", &meta_cumt::pairs_call }, { "__ipairs", &meta_cumt::pairs_call },
@ -292,24 +292,32 @@ namespace sol {
struct pusher<as_container_t<T>> { struct pusher<as_container_t<T>> {
typedef meta::unqualified_t<T> C; typedef meta::unqualified_t<T> C;
static int push(std::true_type, lua_State* L, const C& cont) { static int push_lvalue(std::true_type, lua_State* L, const C& cont) {
stack_detail::metatable_setup<C, true> fx(L); stack_detail::metatable_setup<C*, true> fx(L);
return pusher<detail::as_pointer_tag<const C>>{}.push_fx(L, fx, detail::ptr(cont)); return pusher<detail::as_pointer_tag<const C>>{}.push_fx(L, fx, detail::ptr(cont));
} }
static int push(std::false_type, lua_State* L, const C& cont) { static int push_lvalue(std::false_type, lua_State* L, const C& cont) {
stack_detail::metatable_setup<C, true> fx(L); stack_detail::metatable_setup<C, true> fx(L);
return pusher<detail::as_value_tag<C>>{}.push_fx(L, fx, cont); return pusher<detail::as_value_tag<C>>{}.push_fx(L, fx, cont);
} }
static int push(lua_State* L, const C& cont) { static int push_rvalue(std::true_type, lua_State* L, C&& cont) {
return push(std::is_lvalue_reference<T>(), L, cont);
}
static int push(lua_State* L, C&& cont) {
stack_detail::metatable_setup<C, true> fx(L); stack_detail::metatable_setup<C, true> fx(L);
return pusher<detail::as_value_tag<C>>{}.push_fx(L, fx, std::move(cont)); return pusher<detail::as_value_tag<C>>{}.push_fx(L, fx, std::move(cont));
} }
static int push_rvalue(std::false_type, lua_State* L, const C& cont) {
return push_lvalue(std::is_lvalue_reference<T>(), L, cont);
}
static int push(lua_State* L, const as_container_t<T>& as_cont) {
return push_lvalue(std::is_lvalue_reference<T>(), L, as_cont.source);
}
static int push(lua_State* L, as_container_t<T>&& as_cont) {
return push_rvalue(meta::all<std::is_rvalue_reference<T>, meta::neg<std::is_lvalue_reference<T>>>(), L, std::forward<T>(as_cont.source));
}
}; };
template<typename T> template<typename T>

View File

@ -66,11 +66,16 @@ namespace sol {
int isnum = 0; int isnum = 0;
const lua_Number value = lua_tonumberx(L, index, &isnum); const lua_Number value = lua_tonumberx(L, index, &isnum);
if (isnum != 0) { if (isnum != 0) {
#if 1 // defined(SOL_CHECK_ARGUMENTS) && !defined(SOL_NO_CHECK_NUMBER_PRECISION)
const auto integer_value = std::llround(value); const auto integer_value = std::llround(value);
if (static_cast<lua_Number>(integer_value) == value) { if (static_cast<lua_Number>(integer_value) == value) {
tracking.use(1); tracking.use(1);
return static_cast<T>(integer_value); return static_cast<T>(integer_value);
} }
#else
tracking.use(1);
return static_cast<T>(value);
#endif
} }
const type t = type_of(L, index); const type t = type_of(L, index);
tracking.use(static_cast<int>(t != type::none)); tracking.use(static_cast<int>(t != type::none));

View File

@ -198,7 +198,7 @@ namespace sol {
static int push(lua_State* L, const T& value) { static int push(lua_State* L, const T& value) {
#if SOL_LUA_VERSION >= 503 #if SOL_LUA_VERSION >= 503
static auto integer_value_fits = [](T const& value) { static auto integer_value_fits = [](T const& value) {
if (sizeof(T) < sizeof(lua_Integer) || std::is_signed<T>::value && sizeof(T) == sizeof(lua_Integer)) { if (sizeof(T) < sizeof(lua_Integer) || (std::is_signed<T>::value && sizeof(T) == sizeof(lua_Integer))) {
return true; return true;
} }
auto u_min = static_cast<std::intmax_t>(std::numeric_limits<lua_Integer>::min()); auto u_min = static_cast<std::intmax_t>(std::numeric_limits<lua_Integer>::min());

View File

@ -992,7 +992,7 @@ c_arr[-1] = 7
#endif // Something is wrong with g++'s lower versions: it always fails this test... #endif // Something is wrong with g++'s lower versions: it always fails this test...
} }
TEST_CASE("containers/as_container", "test that we can force a container to be treated like one despite the trait being false using the proper marker") { TEST_CASE("containers/as_container reference", "test that we can force a container to be treated like one despite the trait being false using the proper marker") {
sol::state lua; sol::state lua;
lua.open_libraries(sol::lib::base); lua.open_libraries(sol::lib::base);
@ -1094,3 +1094,77 @@ print(mo)
REQUIRE(&mo == my_object::last_printed); REQUIRE(&mo == my_object::last_printed);
}()); }());
} }
TEST_CASE("containers/as_container", "test that we can force a container to be treated like one despite the trait being false using the proper marker") {
sol::state lua;
lua.open_libraries(sol::lib::base);
lua.set_function("f", [](int v) {
return sol::as_container(my_object(v));
});
#if SOL_LUA_VERSION > 501
REQUIRE_NOTHROW([&]() {
lua.safe_script(R"(
mop = f(20)
for i, v in pairs(mop) do
assert(i == v)
end
)");
}());
#endif
REQUIRE_NOTHROW([&]() {
lua.safe_script(R"(
mo = f(10)
c_iterable = mo
)");
}());
{
my_object& mo = lua["mo"];
my_object& mo_iterable = lua["c_iterable"];
REQUIRE(&mo == &mo_iterable);
REQUIRE(mo == mo_iterable);
}
REQUIRE_NOTHROW([&]() {
lua.safe_script(R"(
s1_iterable = c_iterable:size()
s1_iterable_len = #c_iterable
)");
}());
{
std::size_t s1_iterable = lua["s1_iterable"];
std::size_t s1_iterable_len = lua["s1_iterable_len"];
REQUIRE(s1_iterable == 10);
REQUIRE(s1_iterable == s1_iterable_len);
}
REQUIRE_NOTHROW([&]() {
lua.safe_script(R"(
for i=1,#c_iterable do
v_iterable = c_iterable[i]
assert(v_iterable == i)
end
)");
}());
REQUIRE_NOTHROW([&]() {
lua.safe_script(R"(
c_iterable:insert(1, 100)
v1 = c_iterable:get(1)
s2_iterable = c_iterable:size()
s2_iterable_len = #c_iterable
)");
}());
{
int v1 = lua["v1"];
std::size_t s2_iterable = lua["s2_iterable"];
std::size_t s2_iterable_len = lua["s2_iterable_len"];
REQUIRE(v1 == 100);
REQUIRE(s2_iterable_len == 11);
REQUIRE(s2_iterable == s2_iterable_len);
}
}

View File

@ -18,9 +18,10 @@ TEST_CASE("large_integer/bool", "pass bool integral value to and from lua") {
REQUIRE(x.is<bool>()); REQUIRE(x.is<bool>());
REQUIRE(x.as<bool>() == true); REQUIRE(x.as<bool>() == true);
REQUIRE_FALSE(x.is<std::int32_t>()); REQUIRE_FALSE(x.is<std::int32_t>());
REQUIRE_THROWS([&lua]() { {
lua.script("f(1)"); auto result = lua.safe_script("f(1)", sol::script_pass_on_error);
}()); REQUIRE_FALSE(result.valid());
}
} }
TEST_CASE("large_integers/unsigned32", "pass large unsigned 32bit values to and from lua") { TEST_CASE("large_integers/unsigned32", "pass large unsigned 32bit values to and from lua") {
@ -56,12 +57,12 @@ TEST_CASE("large_integer/unsigned53", "pass large unsigned 53bit value to and fr
TEST_CASE("large_integer/unsigned64", "pass too large unsigned 64bit value to lua") { TEST_CASE("large_integer/unsigned64", "pass too large unsigned 64bit value to lua") {
using T = std::int64_t; using T = std::int64_t;
sol::state lua; sol::state lua;
lua.open_libraries();
lua.set_function("f", [&](T num) -> T { lua.set_function("f", [&](T num) -> T {
return num; return num;
}); });
REQUIRE_THROWS([&lua]() { REQUIRE_THROWS([&lua](){
lua["f"](0xFFFFFFFFFFFFFFFFull); sol::protected_function pf = lua["f"];
auto result = pf(0xFFFFFFFFFFFFFFFFull);
}()); }());
} }