mirror of
https://github.com/ThePhD/sol2.git
synced 2024-03-22 13:10:44 +08:00
153 lines
4.8 KiB
C++
153 lines
4.8 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 sol3
|
|
// 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 sol3
|
|
// 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;
|
|
}
|