New Unsafe Feature: Function Pointers!

- Not at all type safe: there should be some investigation into making it less unsafe to work with these things (albeit it looks like it would cost +1 pointer to serialize a string name for each callable in Lua, at LEAST)
- Must be opted into - see the documentation
- Fixes #1015
- A few drive-by fixes here and there
- New configuration test harness with CMake
This commit is contained in:
ThePhD 2020-09-26 09:46:11 -04:00
parent 1fb483db91
commit 48eea7b573
No known key found for this signature in database
GPG Key ID: 1509DB1C0F702BFA
16 changed files with 505 additions and 45 deletions

View File

@ -24,6 +24,7 @@ Okay, so the features don't convince you, the documentation doesn't convince you
+----------+---------+ +----------+---------+
* In `Perforce`_ (later versions of the code using sol2 more directly can be requested by e-mailing support@perforce.com !) * In `Perforce`_ (later versions of the code using sol2 more directly can be requested by e-mailing support@perforce.com !)
* For Sandboxing `Fun`_!
* In `High Performance Computing research`_ * In `High Performance Computing research`_
- `Published research, too!`_ - `Published research, too!`_
* The `Multiple Arcade Machine Emulator (MAME)`_ project switched from using LuaBridge to sol3! * The `Multiple Arcade Machine Emulator (MAME)`_ project switched from using LuaBridge to sol3!
@ -39,7 +40,7 @@ Okay, so the features don't convince you, the documentation doesn't convince you
* (Twitter) Twitter has some people that link it: * (Twitter) Twitter has some people that link it:
- The image above, `tweeted out by eevee`_ - The image above, `tweeted out by eevee`_
- Eevee: `"I heartily recommend sol3"`_ - Eevee: `"I heartily recommend sol3"`_
- Elias Daler: `"sol3 saved my life."`_ - Elias Daler: `"sol2 is making usertypes in Lua so awesome <3"`_
- Racod's Lair: `"from outdated LuaBridge to superior #sol3"`_ - Racod's Lair: `"from outdated LuaBridge to superior #sol3"`_
* (Reddit) Posts on reddit about it! * (Reddit) Posts on reddit about it!
- `sol2's initial reddit release`_ - `sol2's initial reddit release`_
@ -52,12 +53,14 @@ Are you using sol3 for something neat? Want it to be featured here or think it's
.. _tell me about your uses!: https://github.com/ThePhD/sol2/issues/189 .. _tell me about your uses!: https://github.com/ThePhD/sol2/issues/189
.. _eevee: https://twitter.com/eevee .. _eevee: https://twitter.com/eevee
.. _eevee's blog: https://eev.ee/dev/2016/08/07/weekly-roundup-three-big-things/ .. _eevee's blog: https://eev.ee/dev/2016/08/07/weekly-roundup-three-big-things/
.. _Fun: https://blog.rubenwardy.com/2020/07/26/sol3-script-sandbox/
.. _Jason Turner's presentation: https://github.com/lefticus/presentations/blob/master/WhyAndHowToAddScripting.md .. _Jason Turner's presentation: https://github.com/lefticus/presentations/blob/master/WhyAndHowToAddScripting.md
.. _Elias Daler's blog: https://eliasdaler.github.io/cppcast#read-more .. _Elias Daler's blog: https://eliasdaler.github.io/cppcast#read-more
.. _CppCast: http://cppcast.com/2016/07/elias-daler/ .. _CppCast: http://cppcast.com/2016/07/elias-daler/
.. _tweeted out by eevee: https://twitter.com/eevee/status/762039984085798913 .. _tweeted out by eevee: https://twitter.com/eevee/status/762039984085798913
.. _"I heartily recommend sol3": https://twitter.com/eevee/status/762040086540144644 .. _"I heartily recommend sol3": https://twitter.com/eevee/status/762040086540144644
.. _"from outdated LuaBridge to superior #sol3": https://twitter.com/racodslair/status/754031870640267264 .. _"from outdated LuaBridge to superior #sol3": https://twitter.com/racodslair/status/754031870640267264
.. _"sol2 is making usertypes in Lua so awesome <3": https://twitter.com/EliasDaler/status/778708299029946368
.. _sol2's initial reddit release: https://www.reddit.com/r/cpp/comments/4a8gy7/sol2_lua_c_binding_framework/ .. _sol2's initial reddit release: https://www.reddit.com/r/cpp/comments/4a8gy7/sol2_lua_c_binding_framework/
.. _Benchmarking Discussing: https://www.reddit.com/r/cpp/comments/4x82hd/plain_c_versus_lua_libraries_benchmarking_speed/ .. _Benchmarking Discussing: https://www.reddit.com/r/cpp/comments/4x82hd/plain_c_versus_lua_libraries_benchmarking_speed/
.. _"After spending hours with sol2, it wins. Amazing lib.": https://twitter.com/EliasDaler/status/739215685264494593 .. _"After spending hours with sol2, it wins. Amazing lib.": https://twitter.com/EliasDaler/status/739215685264494593

View File

@ -89,6 +89,14 @@ Feature Config
* Includes ``<iostream>`` and prints all exceptions and errors to ``std::cerr``, for you to see * Includes ``<iostream>`` and prints all exceptions and errors to ``std::cerr``, for you to see
* **Not** turned on by default under any settings: *this MUST be turned on manually* * **Not** turned on by default under any settings: *this MUST be turned on manually*
``SOL_GET_FUNCTION_POINTERS_UNSAFE`` triggers the following change:
* Allows function pointers serialized into Lua as a callable to be retrieved back from Lua in a semi-proper manner
* **This is under NO circumstances type safe**
- It **WILL** break ``sol::overload`` type checking and will not discriminate properly between function types
- It **WILL** happily let you retrieve an ``int(*)(int, int int)`` from a ``void(*)()`` function pointer, and shatter your runtime if you call it
* This is an **advanced, experimental feature** for experts only and requires the user has **perfect type safety** in both C++ and Lua
* **Not** turned on by default under any settings: *this MUST be turned on manually*
``SOL_CONTAINERS_START`` triggers the following change: ``SOL_CONTAINERS_START`` triggers the following change:
* If defined and **is an integral value**, is used to adjust the container start value * If defined and **is an integral value**, is used to adjust the container start value
* Applies to C++ containers **only** (not Lua tables or algorithms) * Applies to C++ containers **only** (not Lua tables or algorithms)

View File

@ -39,7 +39,7 @@ namespace sol {
using type = T; using type = T;
}; };
struct call_indicator {}; struct call_indicator { };
template <bool yielding> template <bool yielding>
int lua_c_wrapper(lua_State* L) { int lua_c_wrapper(lua_State* L) {
@ -65,16 +65,15 @@ namespace sol {
} }
} }
struct c_function_invocation {}; struct c_function_invocation { };
template <bool is_yielding, typename Fx, typename... Args> template <bool is_yielding, typename Fx, typename... Args>
void select(lua_State* L, Fx&& fx, Args&&... args); void select(lua_State* L, Fx&& fx, Args&&... args);
template <bool is_yielding, bool no_trampoline, typename Fx, typename... Args> template <bool is_yielding, bool no_trampoline, typename Fx, typename... Args>
void select_set_fx(lua_State* L, Args&&... args) { void select_set_fx(lua_State* L, Args&&... args) {
lua_CFunction freefunc = no_trampoline ? lua_CFunction freefunc = no_trampoline ? detail::static_trampoline<function_detail::call<meta::unqualified_t<Fx>, 2, is_yielding>>
detail::static_trampoline<function_detail::call<meta::unqualified_t<Fx>, 2, is_yielding>> : function_detail::call<meta::unqualified_t<Fx>, 2, is_yielding>;
: function_detail::call<meta::unqualified_t<Fx>, 2, is_yielding>;
int upvalues = 0; int upvalues = 0;
upvalues += stack::push(L, nullptr); upvalues += stack::push(L, nullptr);
@ -680,7 +679,56 @@ namespace sol {
} }
} }
}; };
} // namespace stack
namespace stack_detail {
template <typename Function, typename Handler>
bool check_function_pointer(lua_State* L, int index, Handler&& handler, record& tracking) noexcept {
#if SOL_IS_ON(SOL_GET_FUNCTION_POINTER_UNSAFE_I_)
tracking.use(1);
bool success = lua_iscfunction(L, index) == 1;
if (success) {
// there must be at LEAST 2 upvalues; otherwise, we didn't serialize it.
const char* upvalue_name = lua_getupvalue(L, index, 2);
lua_pop(L, 1);
success = upvalue_name != nullptr;
}
if (!success) {
// expected type, actual type
handler(
L, index, type::function, type_of(L, index), "type must be a Lua C Function gotten from a function pointer serialized by sol2");
}
return success;
#else
return false;
#endif
}
template <typename Function>
Function* get_function_pointer(lua_State* L, int index, record& tracking) noexcept {
#if SOL_IS_ON(SOL_GET_FUNCTION_POINTER_UNSAFE_I_)
tracking.use(1);
auto udata = stack::stack_detail::get_as_upvalues_using_function<Function*>(L, index);
Function* fx = udata.first;
return fx;
#else
static_assert(meta::meta_detail::always_true<Function>::value,
#if SOL_IS_DEFAULT_OFF(SOL_GET_FUNCTION_POINTER_UNSAFE_I_)
"You are attempting to retrieve a function pointer type. "
"This is inherently unsafe in sol2. In order to do this, you must turn on the "
"SOL_GET_FUNCTION_POINTER_UNSAFE configuration macro, as detailed in the documentation. "
"Please be careful!"
#else
"You are attempting to retrieve a function pointer type. "
"You explicitly turned off the ability to do this by defining "
"SOL_GET_FUNCTION_POINTER_UNSAFE or similar to be off. "
"Please reconsider this!"
#endif
);
return nullptr;
#endif
}
} // namespace stack_detail
} // namespace stack
} // namespace sol } // namespace sol
#endif // SOL_FUNCTION_TYPES_HPP #endif // SOL_FUNCTION_TYPES_HPP

View File

@ -101,8 +101,9 @@ namespace sol {
data_t data { {} }; data_t data { {} };
std::memcpy(&data[0], std::addressof(item), itemsize); std::memcpy(&data[0], std::addressof(item), itemsize);
int pushcount = 0; int pushcount = 0;
for (auto&& v : data) { for (const auto& v : data) {
pushcount += push(L, lightuserdata_value(v)); lua_pushlightuserdata(L, v);
pushcount += 1;
} }
return pushcount; return pushcount;
} }
@ -113,11 +114,33 @@ namespace sol {
typedef std::array<void*, data_t_count> data_t; typedef std::array<void*, data_t_count> data_t;
data_t voiddata { {} }; data_t voiddata { {} };
for (std::size_t i = 0, d = 0; d < sizeof(T); ++i, d += sizeof(void*)) { for (std::size_t i = 0, d = 0; d < sizeof(T); ++i, d += sizeof(void*)) {
voiddata[i] = get<lightuserdata_value>(L, upvalue_index(index++)); voiddata[i] = lua_touserdata(L, upvalue_index(index++));
} }
return std::pair<T, int>(*reinterpret_cast<T*>(static_cast<void*>(voiddata.data())), index); return std::pair<T, int>(*reinterpret_cast<T*>(static_cast<void*>(voiddata.data())), index);
} }
template <typename T>
inline std::pair<T, int> get_as_upvalues_using_function(lua_State* L, int function_index = -1) {
static const std::size_t data_t_count = (sizeof(T) + (sizeof(void*) - 1)) / sizeof(void*);
typedef std::array<void*, data_t_count> data_t;
function_index = lua_absindex(L, function_index);
int index = 0;
data_t voiddata { {} };
for (std::size_t d = 0; d < sizeof(T); d += sizeof(void*)) {
// first upvalue is nullptr to respect environment shenanigans
// So +2 instead of +1
const char* upvalue_name = lua_getupvalue(L, function_index, index + 2);
if (upvalue_name == nullptr) {
// We should freak out here...
break;
}
voiddata[index] = lua_touserdata(L, -1);
++index;
}
lua_pop(L, index);
return std::pair<T, int>(*reinterpret_cast<T*>(static_cast<void*>(voiddata.data())), index);
}
template <typename Fx, typename... Args> template <typename Fx, typename... Args>
static decltype(auto) eval(types<>, std::index_sequence<>, lua_State*, int, record&, Fx&& fx, Args&&... args) { static decltype(auto) eval(types<>, std::index_sequence<>, lua_State*, int, record&, Fx&& fx, Args&&... args) {
return std::forward<Fx>(fx)(std::forward<Args>(args)...); return std::forward<Fx>(fx)(std::forward<Args>(args)...);

View File

@ -348,6 +348,11 @@ namespace sol { namespace stack {
} }
return stack::unqualified_check<ValueType>(L, index, no_panic, tracking); return stack::unqualified_check<ValueType>(L, index, no_panic, tracking);
} }
#if SOL_IS_ON(SOL_GET_FUNCTION_POINTER_UNSAFE_I_)
else if constexpr (std::is_function_v<T> || (std::is_pointer_v<T> && std::is_function_v<std::remove_pointer_t<T>>)) {
return stack_detail::check_function_pointer<std::remove_pointer_t<T>>(L, index, std::forward<Handler>(handler), tracking);
}
#endif
else if constexpr (expected == type::userdata) { else if constexpr (expected == type::userdata) {
if constexpr (meta::any_same_v<T, userdata_value> || meta::is_specialization_of_v<T, basic_userdata>) { if constexpr (meta::any_same_v<T, userdata_value> || meta::is_specialization_of_v<T, basic_userdata>) {
tracking.use(1); tracking.use(1);

View File

@ -164,7 +164,7 @@ namespace sol {
#if SOL_IS_OFF(SOL_ALIGN_MEMORY_I_) #if SOL_IS_OFF(SOL_ALIGN_MEMORY_I_)
false false
#else #else
(std::alignment_of<T>::value > 1) (std::alignment_of_v<T> > 1)
#endif #endif
> >
use_align; use_align;
@ -178,7 +178,7 @@ namespace sol {
return ptr; return ptr;
} }
std::size_t space = (std::numeric_limits<std::size_t>::max)(); std::size_t space = (std::numeric_limits<std::size_t>::max)();
return align(std::alignment_of<T>::value, sizeof(T), ptr, space); return align(std::alignment_of_v<T>, sizeof(T), ptr, space);
} }
template <typename T> template <typename T>
@ -187,7 +187,7 @@ namespace sol {
#if SOL_IS_OFF(SOL_ALIGN_MEMORY_I_) #if SOL_IS_OFF(SOL_ALIGN_MEMORY_I_)
false false
#else #else
(std::alignment_of<T>::value > 1) (std::alignment_of_v<T> > 1)
#endif #endif
> >
use_align; use_align;
@ -195,7 +195,7 @@ namespace sol {
return ptr; return ptr;
} }
std::size_t space = (std::numeric_limits<std::size_t>::max)(); std::size_t space = (std::numeric_limits<std::size_t>::max)();
return align(std::alignment_of<T>::value, sizeof(T), ptr, space); return align(std::alignment_of_v<T>, sizeof(T), ptr, space);
} }
template <typename T> template <typename T>
@ -297,7 +297,7 @@ namespace sol {
#if SOL_IS_OFF(SOL_ALIGN_MEMORY_I_) #if SOL_IS_OFF(SOL_ALIGN_MEMORY_I_)
false false
#else #else
(std::alignment_of<T*>::value > 1 || std::alignment_of<T>::value > 1) (std::alignment_of<T*>::value > 1 || std::alignment_of_v<T> > 1)
#endif #endif
> >
use_align; use_align;
@ -436,7 +436,7 @@ namespace sol {
#if SOL_IS_OFF(SOL_ALIGN_MEMORY_I_) #if SOL_IS_OFF(SOL_ALIGN_MEMORY_I_)
false false
#else #else
(std::alignment_of<T>::value > 1) (std::alignment_of_v<T> > 1)
#endif #endif
> >
use_align; use_align;
@ -450,13 +450,13 @@ namespace sol {
std::size_t allocated_size = initial_size; std::size_t allocated_size = initial_size;
void* unadjusted = lua_newuserdata(L, allocated_size); void* unadjusted = lua_newuserdata(L, allocated_size);
void* adjusted = align(std::alignment_of<T>::value, sizeof(T), unadjusted, allocated_size); void* adjusted = align(std::alignment_of_v<T>, sizeof(T), unadjusted, allocated_size);
if (adjusted == nullptr) { if (adjusted == nullptr) {
lua_pop(L, 1); lua_pop(L, 1);
// try again, add extra space for alignment padding // try again, add extra space for alignment padding
allocated_size = misaligned_size; allocated_size = misaligned_size;
unadjusted = lua_newuserdata(L, allocated_size); unadjusted = lua_newuserdata(L, allocated_size);
adjusted = align(std::alignment_of<T>::value, sizeof(T), unadjusted, allocated_size); adjusted = align(std::alignment_of_v<T>, sizeof(T), unadjusted, allocated_size);
if (adjusted == nullptr) { if (adjusted == nullptr) {
lua_pop(L, 1); lua_pop(L, 1);
luaL_error(L, "cannot properly align memory for '%s'", detail::demangle<T>().data()); luaL_error(L, "cannot properly align memory for '%s'", detail::demangle<T>().data());
@ -630,14 +630,21 @@ namespace sol {
int last; int last;
int used; int used;
record() : last(), used() { record() noexcept : last(), used() {
} }
void use(int count) { void use(int count) noexcept {
last = count; last = count;
used += count; used += count;
} }
}; };
namespace stack_detail {
template <typename Function>
Function* get_function_pointer(lua_State*, int, record&) noexcept;
template <typename Function, typename Handler>
bool check_function_pointer(lua_State* L, int index, Handler&& handler, record& tracking) noexcept;
} // namespace stack_detail
} // namespace stack } // namespace stack
namespace meta { namespace meta_detail { namespace meta { namespace meta_detail {

View File

@ -163,6 +163,11 @@ namespace sol { namespace stack {
luaL_Stream* pstream = static_cast<luaL_Stream*>(lua_touserdata(L, index)); luaL_Stream* pstream = static_cast<luaL_Stream*>(lua_touserdata(L, index));
return *pstream; return *pstream;
} }
#if SOL_IS_ON(SOL_GET_FUNCTION_POINTER_UNSAFE_I_)
else if constexpr (std::is_function_v<T> || (std::is_pointer_v<T> && std::is_function_v<std::remove_pointer_t<T>>)) {
return stack_detail::get_function_pointer<std::remove_pointer_t<T>>(L, index, tracking);
}
#endif
else { else {
return stack_detail::unchecked_unqualified_get<detail::as_value_tag<T>>(L, index, tracking); return stack_detail::unchecked_unqualified_get<detail::as_value_tag<T>>(L, index, tracking);
} }
@ -949,10 +954,22 @@ namespace sol { namespace stack {
template <typename T> template <typename T>
struct unqualified_getter<T*> { struct unqualified_getter<T*> {
static T* get(lua_State* L, int index, record& tracking) { static T* get(lua_State* L, int index, record& tracking) {
#if SOL_IS_ON(SOL_GET_FUNCTION_POINTER_UNSAFE_I_)
if constexpr (std::is_function_v<T>) {
return stack_detail::get_function_pointer<T>(L, index, tracking);
}
else {
unqualified_getter<detail::as_pointer_tag<T>> g;
// Avoid VC++ warning
(void)g;
return g.get(L, index, tracking);
}
#else
unqualified_getter<detail::as_pointer_tag<T>> g; unqualified_getter<detail::as_pointer_tag<T>> g;
// Avoid VC++ warning // Avoid VC++ warning
(void)g; (void)g;
return g.get(L, index, tracking); return g.get(L, index, tracking);
#endif
} }
}; };

View File

@ -1077,13 +1077,21 @@ namespace sol {
template <> template <>
struct lua_type_of<type> : std::integral_constant<type, type::poly> { }; struct lua_type_of<type> : std::integral_constant<type, type::poly> { };
#if SOL_IS_ON(SOL_GET_FUNCTION_POINTER_UNSAFE_I_)
template <typename T>
struct lua_type_of<T*> : std::integral_constant<type, std::is_function_v<T> ? type::function : type::userdata> { };
#else
template <typename T> template <typename T>
struct lua_type_of<T*> : std::integral_constant<type, type::userdata> { }; struct lua_type_of<T*> : std::integral_constant<type, type::userdata> { };
#endif
template <typename T> template <typename T>
struct lua_type_of<T, std::enable_if_t<std::is_arithmetic_v<T> || std::is_same_v<T, lua_Number> || std::is_same_v<T, lua_Integer>>> struct lua_type_of<T, std::enable_if_t<std::is_arithmetic_v<T> || std::is_same_v<T, lua_Number> || std::is_same_v<T, lua_Integer>>>
: std::integral_constant<type, type::number> { }; : std::integral_constant<type, type::number> { };
template <typename T>
struct lua_type_of<T, std::enable_if_t<std::is_function_v<T>>> : std::integral_constant<type, type::function> { };
template <typename T> template <typename T>
struct lua_type_of<T, std::enable_if_t<std::is_enum_v<T>>> : std::integral_constant<type, type::number> { }; struct lua_type_of<T, std::enable_if_t<std::is_enum_v<T>>> : std::integral_constant<type, type::number> { };

View File

@ -563,6 +563,16 @@
#define SOL_USE_COMPATIBILITY_LAYER_I_ SOL_DEFAULT_ON #define SOL_USE_COMPATIBILITY_LAYER_I_ SOL_DEFAULT_ON
#endif #endif
#if defined(SOL_GET_FUNCTION_POINTER_UNSAFE)
#if (SOL_GET_FUNCTION_POINTER_UNSAFE != 0)
#define SOL_GET_FUNCTION_POINTER_UNSAFE_I_ SOL_ON
#else
#define SOL_GET_FUNCTION_POINTER_UNSAFE_I_ SOL_OFF
#endif
#else
#define SOL_GET_FUNCTION_POINTER_UNSAFE_I_ SOL_DEFAULT_OFF
#endif
#if SOL_IS_ON(SOL_COMPILER_FRONTEND_MINGW_I_) && defined(__GNUC__) && (__GNUC__ < 6) #if SOL_IS_ON(SOL_COMPILER_FRONTEND_MINGW_I_) && defined(__GNUC__) && (__GNUC__ < 6)
// MinGW is off its rocker in some places... // MinGW is off its rocker in some places...
#define SOL_MINGW_CCTYPE_IS_POISONED_I_ SOL_ON #define SOL_MINGW_CCTYPE_IS_POISONED_I_ SOL_ON

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 2020-09-26 10:47:52.233754 UTC // Generated 2020-09-26 13:38:14.149661 UTC
// This header was generated with sol v3.2.1 (revision 000fa31) // This header was generated with sol v3.2.1 (revision 1fb483d)
// https://github.com/ThePhD/sol2 // https://github.com/ThePhD/sol2
#ifndef SOL_SINGLE_CONFIG_HPP #ifndef SOL_SINGLE_CONFIG_HPP

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 2020-09-26 10:47:52.207756 UTC // Generated 2020-09-26 13:38:14.123663 UTC
// This header was generated with sol v3.2.1 (revision 000fa31) // This header was generated with sol v3.2.1 (revision 1fb483d)
// https://github.com/ThePhD/sol2 // https://github.com/ThePhD/sol2
#ifndef SOL_SINGLE_INCLUDE_FORWARD_HPP #ifndef SOL_SINGLE_INCLUDE_FORWARD_HPP
@ -569,6 +569,16 @@
#define SOL_USE_COMPATIBILITY_LAYER_I_ SOL_DEFAULT_ON #define SOL_USE_COMPATIBILITY_LAYER_I_ SOL_DEFAULT_ON
#endif #endif
#if defined(SOL_GET_FUNCTION_POINTER_UNSAFE)
#if (SOL_GET_FUNCTION_POINTER_UNSAFE != 0)
#define SOL_GET_FUNCTION_POINTER_UNSAFE_I_ SOL_ON
#else
#define SOL_GET_FUNCTION_POINTER_UNSAFE_I_ SOL_OFF
#endif
#else
#define SOL_GET_FUNCTION_POINTER_UNSAFE_I_ SOL_DEFAULT_OFF
#endif
#if SOL_IS_ON(SOL_COMPILER_FRONTEND_MINGW_I_) && defined(__GNUC__) && (__GNUC__ < 6) #if SOL_IS_ON(SOL_COMPILER_FRONTEND_MINGW_I_) && defined(__GNUC__) && (__GNUC__ < 6)
// MinGW is off its rocker in some places... // MinGW is off its rocker in some places...
#define SOL_MINGW_CCTYPE_IS_POISONED_I_ SOL_ON #define SOL_MINGW_CCTYPE_IS_POISONED_I_ SOL_ON

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 2020-09-26 10:47:51.277755 UTC // Generated 2020-09-26 13:38:13.096661 UTC
// This header was generated with sol v3.2.1 (revision 000fa31) // This header was generated with sol v3.2.1 (revision 1fb483d)
// https://github.com/ThePhD/sol2 // https://github.com/ThePhD/sol2
#ifndef SOL_SINGLE_INCLUDE_HPP #ifndef SOL_SINGLE_INCLUDE_HPP
@ -569,6 +569,16 @@
#define SOL_USE_COMPATIBILITY_LAYER_I_ SOL_DEFAULT_ON #define SOL_USE_COMPATIBILITY_LAYER_I_ SOL_DEFAULT_ON
#endif #endif
#if defined(SOL_GET_FUNCTION_POINTER_UNSAFE)
#if (SOL_GET_FUNCTION_POINTER_UNSAFE != 0)
#define SOL_GET_FUNCTION_POINTER_UNSAFE_I_ SOL_ON
#else
#define SOL_GET_FUNCTION_POINTER_UNSAFE_I_ SOL_OFF
#endif
#else
#define SOL_GET_FUNCTION_POINTER_UNSAFE_I_ SOL_DEFAULT_OFF
#endif
#if SOL_IS_ON(SOL_COMPILER_FRONTEND_MINGW_I_) && defined(__GNUC__) && (__GNUC__ < 6) #if SOL_IS_ON(SOL_COMPILER_FRONTEND_MINGW_I_) && defined(__GNUC__) && (__GNUC__ < 6)
// MinGW is off its rocker in some places... // MinGW is off its rocker in some places...
#define SOL_MINGW_CCTYPE_IS_POISONED_I_ SOL_ON #define SOL_MINGW_CCTYPE_IS_POISONED_I_ SOL_ON
@ -7588,13 +7598,21 @@ namespace sol {
template <> template <>
struct lua_type_of<type> : std::integral_constant<type, type::poly> { }; struct lua_type_of<type> : std::integral_constant<type, type::poly> { };
#if SOL_IS_ON(SOL_GET_FUNCTION_POINTER_UNSAFE_I_)
template <typename T>
struct lua_type_of<T*> : std::integral_constant<type, std::is_function_v<T> ? type::function : type::userdata> { };
#else
template <typename T> template <typename T>
struct lua_type_of<T*> : std::integral_constant<type, type::userdata> { }; struct lua_type_of<T*> : std::integral_constant<type, type::userdata> { };
#endif
template <typename T> template <typename T>
struct lua_type_of<T, std::enable_if_t<std::is_arithmetic_v<T> || std::is_same_v<T, lua_Number> || std::is_same_v<T, lua_Integer>>> struct lua_type_of<T, std::enable_if_t<std::is_arithmetic_v<T> || std::is_same_v<T, lua_Number> || std::is_same_v<T, lua_Integer>>>
: std::integral_constant<type, type::number> { }; : std::integral_constant<type, type::number> { };
template <typename T>
struct lua_type_of<T, std::enable_if_t<std::is_function_v<T>>> : std::integral_constant<type, type::function> { };
template <typename T> template <typename T>
struct lua_type_of<T, std::enable_if_t<std::is_enum_v<T>>> : std::integral_constant<type, type::number> { }; struct lua_type_of<T, std::enable_if_t<std::is_enum_v<T>>> : std::integral_constant<type, type::number> { };
@ -9849,7 +9867,7 @@ namespace sol {
#if SOL_IS_OFF(SOL_ALIGN_MEMORY_I_) #if SOL_IS_OFF(SOL_ALIGN_MEMORY_I_)
false false
#else #else
(std::alignment_of<T>::value > 1) (std::alignment_of_v<T> > 1)
#endif #endif
> >
use_align; use_align;
@ -9863,7 +9881,7 @@ namespace sol {
return ptr; return ptr;
} }
std::size_t space = (std::numeric_limits<std::size_t>::max)(); std::size_t space = (std::numeric_limits<std::size_t>::max)();
return align(std::alignment_of<T>::value, sizeof(T), ptr, space); return align(std::alignment_of_v<T>, sizeof(T), ptr, space);
} }
template <typename T> template <typename T>
@ -9872,7 +9890,7 @@ namespace sol {
#if SOL_IS_OFF(SOL_ALIGN_MEMORY_I_) #if SOL_IS_OFF(SOL_ALIGN_MEMORY_I_)
false false
#else #else
(std::alignment_of<T>::value > 1) (std::alignment_of_v<T> > 1)
#endif #endif
> >
use_align; use_align;
@ -9880,7 +9898,7 @@ namespace sol {
return ptr; return ptr;
} }
std::size_t space = (std::numeric_limits<std::size_t>::max)(); std::size_t space = (std::numeric_limits<std::size_t>::max)();
return align(std::alignment_of<T>::value, sizeof(T), ptr, space); return align(std::alignment_of_v<T>, sizeof(T), ptr, space);
} }
template <typename T> template <typename T>
@ -9982,7 +10000,7 @@ namespace sol {
#if SOL_IS_OFF(SOL_ALIGN_MEMORY_I_) #if SOL_IS_OFF(SOL_ALIGN_MEMORY_I_)
false false
#else #else
(std::alignment_of<T*>::value > 1 || std::alignment_of<T>::value > 1) (std::alignment_of<T*>::value > 1 || std::alignment_of_v<T> > 1)
#endif #endif
> >
use_align; use_align;
@ -10121,7 +10139,7 @@ namespace sol {
#if SOL_IS_OFF(SOL_ALIGN_MEMORY_I_) #if SOL_IS_OFF(SOL_ALIGN_MEMORY_I_)
false false
#else #else
(std::alignment_of<T>::value > 1) (std::alignment_of_v<T> > 1)
#endif #endif
> >
use_align; use_align;
@ -10135,13 +10153,13 @@ namespace sol {
std::size_t allocated_size = initial_size; std::size_t allocated_size = initial_size;
void* unadjusted = lua_newuserdata(L, allocated_size); void* unadjusted = lua_newuserdata(L, allocated_size);
void* adjusted = align(std::alignment_of<T>::value, sizeof(T), unadjusted, allocated_size); void* adjusted = align(std::alignment_of_v<T>, sizeof(T), unadjusted, allocated_size);
if (adjusted == nullptr) { if (adjusted == nullptr) {
lua_pop(L, 1); lua_pop(L, 1);
// try again, add extra space for alignment padding // try again, add extra space for alignment padding
allocated_size = misaligned_size; allocated_size = misaligned_size;
unadjusted = lua_newuserdata(L, allocated_size); unadjusted = lua_newuserdata(L, allocated_size);
adjusted = align(std::alignment_of<T>::value, sizeof(T), unadjusted, allocated_size); adjusted = align(std::alignment_of_v<T>, sizeof(T), unadjusted, allocated_size);
if (adjusted == nullptr) { if (adjusted == nullptr) {
lua_pop(L, 1); lua_pop(L, 1);
luaL_error(L, "cannot properly align memory for '%s'", detail::demangle<T>().data()); luaL_error(L, "cannot properly align memory for '%s'", detail::demangle<T>().data());
@ -10315,14 +10333,21 @@ namespace sol {
int last; int last;
int used; int used;
record() : last(), used() { record() noexcept : last(), used() {
} }
void use(int count) { void use(int count) noexcept {
last = count; last = count;
used += count; used += count;
} }
}; };
namespace stack_detail {
template <typename Function>
Function* get_function_pointer(lua_State*, int, record&) noexcept;
template <typename Function, typename Handler>
bool check_function_pointer(lua_State* L, int index, Handler&& handler, record& tracking) noexcept;
} // namespace stack_detail
} // namespace stack } // namespace stack
namespace meta { namespace meta_detail { namespace meta { namespace meta_detail {
@ -11457,6 +11482,11 @@ namespace sol { namespace stack {
} }
return stack::unqualified_check<ValueType>(L, index, no_panic, tracking); return stack::unqualified_check<ValueType>(L, index, no_panic, tracking);
} }
#if SOL_IS_ON(SOL_GET_FUNCTION_POINTER_UNSAFE_I_)
else if constexpr (std::is_function_v<T> || (std::is_pointer_v<T> && std::is_function_v<std::remove_pointer_t<T>>)) {
return stack_detail::check_function_pointer<std::remove_pointer_t<T>>(L, index, std::forward<Handler>(handler), tracking);
}
#endif
else if constexpr (expected == type::userdata) { else if constexpr (expected == type::userdata) {
if constexpr (meta::any_same_v<T, userdata_value> || meta::is_specialization_of_v<T, basic_userdata>) { if constexpr (meta::any_same_v<T, userdata_value> || meta::is_specialization_of_v<T, basic_userdata>) {
tracking.use(1); tracking.use(1);
@ -12265,6 +12295,11 @@ namespace sol { namespace stack {
luaL_Stream* pstream = static_cast<luaL_Stream*>(lua_touserdata(L, index)); luaL_Stream* pstream = static_cast<luaL_Stream*>(lua_touserdata(L, index));
return *pstream; return *pstream;
} }
#if SOL_IS_ON(SOL_GET_FUNCTION_POINTER_UNSAFE_I_)
else if constexpr (std::is_function_v<T> || (std::is_pointer_v<T> && std::is_function_v<std::remove_pointer_t<T>>)) {
return stack_detail::get_function_pointer<std::remove_pointer_t<T>>(L, index, tracking);
}
#endif
else { else {
return stack_detail::unchecked_unqualified_get<detail::as_value_tag<T>>(L, index, tracking); return stack_detail::unchecked_unqualified_get<detail::as_value_tag<T>>(L, index, tracking);
} }
@ -13051,10 +13086,22 @@ namespace sol { namespace stack {
template <typename T> template <typename T>
struct unqualified_getter<T*> { struct unqualified_getter<T*> {
static T* get(lua_State* L, int index, record& tracking) { static T* get(lua_State* L, int index, record& tracking) {
#if SOL_IS_ON(SOL_GET_FUNCTION_POINTER_UNSAFE_I_)
if constexpr (std::is_function_v<T>) {
return stack_detail::get_function_pointer<T>(L, index, tracking);
}
else {
unqualified_getter<detail::as_pointer_tag<T>> g;
// Avoid VC++ warning
(void)g;
return g.get(L, index, tracking);
}
#else
unqualified_getter<detail::as_pointer_tag<T>> g; unqualified_getter<detail::as_pointer_tag<T>> g;
// Avoid VC++ warning // Avoid VC++ warning
(void)g; (void)g;
return g.get(L, index, tracking); return g.get(L, index, tracking);
#endif
} }
}; };
@ -14936,8 +14983,9 @@ namespace sol {
data_t data { {} }; data_t data { {} };
std::memcpy(&data[0], std::addressof(item), itemsize); std::memcpy(&data[0], std::addressof(item), itemsize);
int pushcount = 0; int pushcount = 0;
for (auto&& v : data) { for (const auto& v : data) {
pushcount += push(L, lightuserdata_value(v)); lua_pushlightuserdata(L, v);
pushcount += 1;
} }
return pushcount; return pushcount;
} }
@ -14948,11 +14996,33 @@ namespace sol {
typedef std::array<void*, data_t_count> data_t; typedef std::array<void*, data_t_count> data_t;
data_t voiddata { {} }; data_t voiddata { {} };
for (std::size_t i = 0, d = 0; d < sizeof(T); ++i, d += sizeof(void*)) { for (std::size_t i = 0, d = 0; d < sizeof(T); ++i, d += sizeof(void*)) {
voiddata[i] = get<lightuserdata_value>(L, upvalue_index(index++)); voiddata[i] = lua_touserdata(L, upvalue_index(index++));
} }
return std::pair<T, int>(*reinterpret_cast<T*>(static_cast<void*>(voiddata.data())), index); return std::pair<T, int>(*reinterpret_cast<T*>(static_cast<void*>(voiddata.data())), index);
} }
template <typename T>
inline std::pair<T, int> get_as_upvalues_using_function(lua_State* L, int function_index = -1) {
static const std::size_t data_t_count = (sizeof(T) + (sizeof(void*) - 1)) / sizeof(void*);
typedef std::array<void*, data_t_count> data_t;
function_index = lua_absindex(L, function_index);
int index = 0;
data_t voiddata { {} };
for (std::size_t d = 0; d < sizeof(T); d += sizeof(void*)) {
// first upvalue is nullptr to respect environment shenanigans
// So +2 instead of +1
const char* upvalue_name = lua_getupvalue(L, function_index, index + 2);
if (upvalue_name == nullptr) {
// We should freak out here...
break;
}
voiddata[index] = lua_touserdata(L, -1);
++index;
}
lua_pop(L, index);
return std::pair<T, int>(*reinterpret_cast<T*>(static_cast<void*>(voiddata.data())), index);
}
template <typename Fx, typename... Args> template <typename Fx, typename... Args>
static decltype(auto) eval(types<>, std::index_sequence<>, lua_State*, int, record&, Fx&& fx, Args&&... args) { static decltype(auto) eval(types<>, std::index_sequence<>, lua_State*, int, record&, Fx&& fx, Args&&... args) {
return std::forward<Fx>(fx)(std::forward<Args>(args)...); return std::forward<Fx>(fx)(std::forward<Args>(args)...);
@ -18120,7 +18190,7 @@ namespace sol {
using type = T; using type = T;
}; };
struct call_indicator {}; struct call_indicator { };
template <bool yielding> template <bool yielding>
int lua_c_wrapper(lua_State* L) { int lua_c_wrapper(lua_State* L) {
@ -18146,16 +18216,15 @@ namespace sol {
} }
} }
struct c_function_invocation {}; struct c_function_invocation { };
template <bool is_yielding, typename Fx, typename... Args> template <bool is_yielding, typename Fx, typename... Args>
void select(lua_State* L, Fx&& fx, Args&&... args); void select(lua_State* L, Fx&& fx, Args&&... args);
template <bool is_yielding, bool no_trampoline, typename Fx, typename... Args> template <bool is_yielding, bool no_trampoline, typename Fx, typename... Args>
void select_set_fx(lua_State* L, Args&&... args) { void select_set_fx(lua_State* L, Args&&... args) {
lua_CFunction freefunc = no_trampoline ? lua_CFunction freefunc = no_trampoline ? detail::static_trampoline<function_detail::call<meta::unqualified_t<Fx>, 2, is_yielding>>
detail::static_trampoline<function_detail::call<meta::unqualified_t<Fx>, 2, is_yielding>> : function_detail::call<meta::unqualified_t<Fx>, 2, is_yielding>;
: function_detail::call<meta::unqualified_t<Fx>, 2, is_yielding>;
int upvalues = 0; int upvalues = 0;
upvalues += stack::push(L, nullptr); upvalues += stack::push(L, nullptr);
@ -18761,7 +18830,56 @@ namespace sol {
} }
} }
}; };
} // namespace stack
namespace stack_detail {
template <typename Function, typename Handler>
bool check_function_pointer(lua_State* L, int index, Handler&& handler, record& tracking) noexcept {
#if SOL_IS_ON(SOL_GET_FUNCTION_POINTER_UNSAFE_I_)
tracking.use(1);
bool success = lua_iscfunction(L, index) == 1;
if (success) {
// there must be at LEAST 2 upvalues; otherwise, we didn't serialize it.
const char* upvalue_name = lua_getupvalue(L, index, 2);
lua_pop(L, 1);
success = upvalue_name != nullptr;
}
if (!success) {
// expected type, actual type
handler(
L, index, type::function, type_of(L, index), "type must be a Lua C Function gotten from a function pointer serialized by sol2");
}
return success;
#else
return false;
#endif
}
template <typename Function>
Function* get_function_pointer(lua_State* L, int index, record& tracking) noexcept {
#if SOL_IS_ON(SOL_GET_FUNCTION_POINTER_UNSAFE_I_)
tracking.use(1);
auto udata = stack::stack_detail::get_as_upvalues_using_function<Function*>(L, index);
Function* fx = udata.first;
return fx;
#else
static_assert(meta::meta_detail::always_true<Function>::value,
#if SOL_IS_DEFAULT_OFF(SOL_GET_FUNCTION_POINTER_UNSAFE_I_)
"You are attempting to retrieve a function pointer type. "
"This is inherently unsafe in sol2. In order to do this, you must turn on the "
"SOL_GET_FUNCTION_POINTER_UNSAFE configuration macro, as detailed in the documentation. "
"Please be careful!"
#else
"You are attempting to retrieve a function pointer type. "
"You explicitly turned off the ability to do this by defining "
"SOL_GET_FUNCTION_POINTER_UNSAFE or similar to be off. "
"Please reconsider this!"
#endif
);
return nullptr;
#endif
}
} // namespace stack_detail
} // namespace stack
} // namespace sol } // namespace sol
// end of sol/function_types.hpp // end of sol/function_types.hpp

View File

@ -24,4 +24,5 @@
add_subdirectory(runtime_tests) add_subdirectory(runtime_tests)
add_subdirectory(compile_tests) add_subdirectory(compile_tests)
add_subdirectory(config_tests)
add_subdirectory(regression_tests) add_subdirectory(regression_tests)

View File

@ -0,0 +1,25 @@
# # # # sol3
# The MIT License (MIT)
#
# Copyright (c) 2013-2020 Rapptz, ThePhD, and contributors
#
# Permission is hereby granted, free of charge, to any person obtaining a copy of
# this software and associated documentation files (the "Software"), to deal in
# the Software without restriction, including without limitation the rights to
# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
# the Software, and to permit persons to whom the Software is furnished to do so,
# subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
# FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
# # # # sol3 tests
add_subdirectory(function_pointers)

View File

@ -0,0 +1,103 @@
# # # # sol3
# The MIT License (MIT)
#
# Copyright (c) 2013-2020 Rapptz, ThePhD, and contributors
#
# Permission is hereby granted, free of charge, to any person obtaining a copy of
# this software and associated documentation files (the "Software"), to deal in
# the Software without restriction, including without limitation the rights to
# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
# the Software, and to permit persons to whom the Software is furnished to do so,
# subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
# FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
# # # # sol3 tests - simple regression tests
file(GLOB test_sources source/*.cpp)
source_group(sources FILES ${test_sources})
function(CREATE_TEST test_target_name test_name target_sol)
add_executable(${test_target_name} ${test_sources})
set_target_properties(${test_target_name}
PROPERTIES
OUTPUT_NAME ${test_name}
EXPORT_NAME sol2::${test_name})
target_link_libraries(${test_target_name}
PUBLIC Threads::Threads ${LUA_LIBRARIES} ${target_sol})
target_compile_definitions(${test_target_name}
PRIVATE SOL_GET_FUNCTION_POINTER_UNSAFE=1 SOL_ALL_SAFETIES_ON=1)
target_include_directories(${test_target_name}
PRIVATE ../../../examples/include)
if (MSVC)
if (NOT CMAKE_COMPILER_ID MATCHES "Clang")
target_compile_options(${test_target_name}
PRIVATE /bigobj /W4)
endif()
else()
target_compile_options(${test_target_name}
PRIVATE -std=c++1z -pthread
-Wno-unknown-warning -Wno-unknown-warning-option
-Wall -Wpedantic -Werror -pedantic -pedantic-errors
-Wno-noexcept-type)
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
# For another day, when C++ is not so crap
# and we have time to audit the entire lib
# for all uses of `detail::swallow`...
#target_compile_options(${test_target_name}
# PRIVATE -Wcomma)
endif()
if (IS_X86)
if(MINGW)
set_target_properties(${test_target_name}
PROPERTIES
LINK_FLAGS -static-libstdc++)
endif()
endif()
endif()
if (MSVC)
target_compile_options(${test_target_name}
PRIVATE /EHsc /std:c++latest)
target_compile_definitions(${test_target_name}
PRIVATE UNICODE _UNICODE
_CRT_SECURE_NO_WARNINGS _CRT_SECURE_NO_DEPRECATE)
else()
target_compile_options(${test_target_name}
PRIVATE -std=c++1z -Wno-unknown-warning -Wno-unknown-warning-option
-Wall -Wextra -Wpedantic -pedantic -pedantic-errors)
endif()
if (SOL2_CI)
target_compile_definitions(${test_target_name}
PRIVATE SOL2_CI)
endif()
if (CMAKE_DL_LIBS)
target_link_libraries(${test_target_name}
PRIVATE ${CMAKE_DL_LIBS})
endif()
add_test(NAME ${test_name} COMMAND ${test_target_name})
install(TARGETS ${test_target_name} RUNTIME DESTINATION bin)
endfunction(CREATE_TEST)
if (SOL2_TESTS)
CREATE_TEST(config_function_pointers_tests "config_function_pointers_tests" sol2::sol2)
endif()
if (SOL2_TESTS_SINGLE)
CREATE_TEST(config_function_pointers_tests_single "config_function_pointers_tests.single" sol2::sol2_single)
endif()
if (SOL2_TESTS_SINGLE_GENERATED)
CREATE_TEST(config_function_pointers_tests_generated_single "config_function_pointers_tests.single.generated" sol2::sol2_single_generated)
endif()

View File

@ -0,0 +1,74 @@
#include <sol/sol.hpp>
#include <assert.hpp>
#include <iostream>
inline constexpr int magic_value = 24;
using zero_arg_type = int (*)();
using one_arg_type = int (*)(int);
using callback_type = int (*)(one_arg_type);
int free_function(int value) {
return value + 1;
}
int callback(one_arg_type f) {
return f(magic_value) + 1;
}
int main() {
constexpr int expected_value = magic_value;
constexpr int expected_free_function_value = magic_value + 1;
constexpr int expected_callback_value = magic_value + 1 + 1;
sol::state lua;
lua.open_libraries(sol::lib::base);
auto lambda = []() { return magic_value; };
auto lambda_ptr = static_cast<zero_arg_type>(lambda);
lua["magic_value"] = magic_value;
lua["expected_value"] = expected_value;
lua["expected_free_function_value"] = expected_free_function_value;
lua["expected_callback_value"] = expected_callback_value;
lua["lambda"] = sol::as_function_reference(lambda);
lua["lambda_ptr"] = lambda_ptr;
lua["free_function"] = &free_function;
lua["callback"] = &callback;
zero_arg_type lambda_f = lua["lambda"];
zero_arg_type lambda_ptr_f = lua["lambda_ptr"];
one_arg_type free_function_f = lua["free_function"];
callback_type callback_f = lua["callback"];
sol::function lua_callback_f = lua["callback"];
int lambda_f_result = lambda_f();
int lambda_ptr_f_result = lambda_ptr_f();
int free_function_f_result = free_function_f(magic_value);
int callback_f_result = callback_f(&free_function);
int lua_callback_f_result = lua_callback_f(&free_function);
c_assert(lambda_f_result == expected_value);
c_assert(lambda_ptr_f_result == expected_value);
c_assert(free_function_f_result == expected_free_function_value);
c_assert(callback_f_result == expected_callback_value);
c_assert(lua_callback_f_result == expected_callback_value);
const char code[] = R"(
assert(lambda() == expected_value)
assert(lambda_ptr() == expected_value)
assert(free_function(magic_value) == expected_free_function_value)
assert(callback(free_function) == expected_callback_value)
)";
sol::optional<sol::error> err = lua.safe_script(code, sol::script_pass_on_error);
if (err.has_value()) {
std::cerr << err.value().what() << std::endl;
return 1;
}
c_assert(!err.has_value());
return 0;
}