sol2/examples/source/metatable_customization.cpp
ThePhD 57d9a05f88
🎨 Refactor tutorial examples
- 🛠 Make sure the tutorials compile across platforms!
- ✍ Redo quite a bit of the documentation
2021-03-06 10:14:48 -05:00

196 lines
5.3 KiB
C++

#define SOL_ALL_SAFETIES_ON 1
#include <sol/sol.hpp>
#include <iostream>
#include <unordered_map>
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::optional<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::optional<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;
}