Add custom metatable and c_array example

This commit is contained in:
ThePhD 2018-03-21 17:19:46 -04:00
parent 2bd1cdccf0
commit b447c3d69c
2 changed files with 206 additions and 0 deletions

55
examples/c_array.cpp Normal file
View File

@ -0,0 +1,55 @@
#define SOL_CHECK_ARGUMENTS 1
#include <sol.hpp>
#include "assert.hpp"
#include <iostream>
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>("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;
}

View File

@ -0,0 +1,151 @@
#define SOL_CHECK_ARGUMENTS 1
#include <sol.hpp>
#include <iostream>
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<sol::string_view, sol::object> thing_function_associations;
static std::unordered_map<sol::string_view, sol::object> 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<TEMPLATE_AUTO(&thing::member_variable)>)
);
thing_function_associations.emplace_hint(thing_function_associations.cend(),
"member_function", sol::object(lua.lua_state(), sol::in_place, &sol::c_call<TEMPLATE_AUTO(&thing::member_function)>)
);
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<thing>()) {
return luaL_error(L, "given an incorrect object for this call");
}
sol::optional<sol::string_view> maybe_svkey = key.as<sol::string_view>();
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<lua_CFunction>();
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<thing>()) {
return luaL_error(L, "given an incorrect object for this call");
}
// write to member variables, etc. etc...
sol::optional<sol::string_view> maybe_svkey = key.as<sol::string_view>();
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<lua_CFunction>();
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>("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;
}