File: metatable_customization.cpp

package info (click to toggle)
sol2 3.5.0-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 23,096 kB
  • sloc: cpp: 43,816; ansic: 1,018; python: 356; sh: 288; makefile: 202
file content (195 lines) | stat: -rw-r--r-- 5,466 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
#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;
}