diff --git a/examples/c_array.cpp b/examples/c_array.cpp new file mode 100644 index 00000000..b4ce328b --- /dev/null +++ b/examples/c_array.cpp @@ -0,0 +1,55 @@ +#define SOL_CHECK_ARGUMENTS 1 +#include + +#include "assert.hpp" + +#include + +struct something { + int arr[4]; + + something() : arr{ 5, 6, 7, 8 } {} +}; + +int main() { + + std::cout << "=== c arrays (works with Visual C++ too) ===" << std::endl; + + sol::state lua; + lua.open_libraries(sol::lib::base); + + lua.new_usertype("something", + "arr", sol::property([](something& s) { + return std::ref(s.arr); + }) + ); + lua.script(R"(s = something.new() + print(s.arr[3]) + s.arr[3] = 40 + print(s.arr[3]) + )"); + + something& s = lua["s"]; + c_assert(s.arr[0] == 5); + c_assert(s.arr[1] == 6); + c_assert(s.arr[2] == 40); + c_assert(s.arr[3] == 8); + + std::string string_array[] = { + "first string", + "second string", + "third string", + }; + lua["str_arr"] = std::ref(string_array); + // or: + // lua["str_array"] = &string_array; + lua.script(R"( + print(str_arr[3]) + str_arr[3] = str_arr[3] .. ': modified' + print(str_arr[3]) + )"); + + c_assert(string_array[2] == "third string: modified"); + + return 0; +} diff --git a/examples/metatable_customization.cpp b/examples/metatable_customization.cpp new file mode 100644 index 00000000..0cbef2f0 --- /dev/null +++ b/examples/metatable_customization.cpp @@ -0,0 +1,151 @@ +#define SOL_CHECK_ARGUMENTS 1 +#include + +#include + +struct thing { + int member_variable = 5; + + double member_function() const { + return member_variable / 2.0; + } +}; + +#define TEMPLATE_AUTO(x) decltype(x), x + +// cheap storage: in reality, you'd need to find a +// better way of handling this. +// or not! It's up to you. +static std::unordered_map thing_function_associations; +static std::unordered_map thing_variable_associations; + +void register_thing_type(sol::state& lua) { + thing_variable_associations.emplace_hint(thing_variable_associations.cend(), + "member_variable", sol::object(lua.lua_state(), sol::in_place, &sol::c_call) + ); + thing_function_associations.emplace_hint(thing_function_associations.cend(), + "member_function", sol::object(lua.lua_state(), sol::in_place, &sol::c_call) + ); + + struct call_handler { + static int lookup_function(lua_State* L) { + sol::stack_object source(L, 1); + sol::stack_object key(L, 2); + if (!source.is()) { + return luaL_error(L, "given an incorrect object for this call"); + } + sol::optional maybe_svkey = key.as(); + if (maybe_svkey) { + { + // functions are different from variables + // functions, when obtain with the syntax + // obj.f, obj.f(), and obj:f() + // must return the function itself + // so we just push it realy into our target + auto it = thing_function_associations.find(*maybe_svkey); + if (it != thing_function_associations.cend()) { + return it->second.push(L); + } + } + { + // variables are different than funtions + // when someone does `obj.a`, they expect + // this __index call (this lookup function) to + // return to them the value itself they're seeing + // so we call out lua_CFunction that we serialized earlier + auto it = thing_variable_associations.find(*maybe_svkey); + if (it != thing_variable_associations.cend()) { + // note that calls generated by sol2 + // for member variables expect the stack ordering to be + // 2(, 3, ..., n) -- value(s) + // 1 -- source + // so we destroy the key on the stack + sol::stack::remove(L, 2, 1); + lua_CFunction cf = it->second.as(); + return cf(L); + } + } + } + return sol::stack::push(L, sol::lua_nil); + } + + static int insertion_function(lua_State* L) { + sol::stack_object source(L, 1); + sol::stack_object key(L, 2); + sol::stack_object value(L, 3); + if (!source.is()) { + return luaL_error(L, "given an incorrect object for this call"); + } + // write to member variables, etc. etc... + sol::optional maybe_svkey = key.as(); + if (maybe_svkey) { + { + // variables are different than funtions + // when someone does `obj.a`, they expect + // this __index call (this lookup function) to + // return to them the value itself they're seeing + // so we call out lua_CFunction that we serialized earlier + auto it = thing_variable_associations.find(*maybe_svkey); + if (it != thing_variable_associations.cend()) { + // note that calls generated by sol2 + // for member variables expect the stack ordering to be + // 2(, 3, ..., n) -- value(s) + // 1 -- source + // so we remove the key value + sol::stack::remove(L, 2, 1); + lua_CFunction cf = it->second.as(); + return cf(L); + } + else { + // write to member variable, maybe override function + // if your class allows for it? + (void)value; + } + } + // exercise for reader: + // how do you override functions on the metatable with + // proper syntax, but error when the type is + // an "instance" object? + } + return 0; + } + }; + + lua.new_usertype("thing"); + sol::table metatable = lua["thing"]; + + metatable[sol::meta_method::index] = &call_handler::lookup_function; + metatable[sol::meta_method::new_index] = &call_handler::insertion_function; +} + +void unregister_thing_type(sol::state&) { + thing_function_associations.clear(); + thing_variable_associations.clear(); +} + +int main() { + + std::cout << "=== metatable with custom-built (static) handling ===" << std::endl; + + + sol::state lua; + lua.open_libraries(sol::lib::base); + + // register custom type + storage + register_thing_type(lua); + + lua.script(R"(t = thing.new() + print(t.member_variable) + print(t:member_function()) + t.member_variable = 24 + print(t.member_variable) + print(t:member_function()) + )"); + + // clear storage + unregister_thing_type(lua); + + std::cout << std::endl; + + return 0; +}