sol2/examples/source/metatable_customization.cpp

152 lines
4.8 KiB
C++

#define SOL_CHECK_ARGUMENTS 1
#include <sol/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;
}