1
+ // bindclass.h
2
+
3
+ #pragma once
4
+
5
+ #include < boost/fusion/include/vector.hpp>
6
+ #include < boost/fusion/algorithm/iteration/for_each.hpp>
7
+ #include " common.h"
8
+
9
+ namespace MyGameProject
10
+ {
11
+ namespace LuaUtilities
12
+ {
13
+ // Bind C++ class to Lua.
14
+ // C++ object is weak-bound to lua object.
15
+ template <typename CXXClass>
16
+ class RegisterCXXClass
17
+ {
18
+ private:
19
+ using Ref = std::shared_ptr<CXXClass>;
20
+ using WeakRef = std::weak_ptr <CXXClass>;
21
+
22
+ lua_State* L;
23
+ const char * name;
24
+
25
+ template <typename ... Args, typename F>
26
+ static auto call (lua_State* _state, F _functor, int _first_arg_index)
27
+ {
28
+ auto args{ detail::to_cvalues<Args...>(_state, _first_arg_index) };
29
+ return detail::call_with_expanded_tuple
30
+ (
31
+ [f = _functor](Args... _args)
32
+ {
33
+ return f (_args...);
34
+ }, args
35
+ );
36
+ }
37
+
38
+ template <typename T, typename R, typename ... Args>
39
+ static auto bind (R(T::*_memfunc_ptr)(Args...), T* _obj)
40
+ {
41
+ return [memf = _memfunc_ptr, obj = _obj](Args... _args)
42
+ {
43
+ return (obj->*memf)(_args...);
44
+ };
45
+ }
46
+ public:
47
+ RegisterCXXClass (lua_State* _state, const char * _class_name) :L(_state), name(_class_name)
48
+ {
49
+ lua_newtable (L); // Class body
50
+ lua_newtable (L); // Metatable
51
+ lua_newtable (L); // Method list
52
+
53
+ // Method 'expired' checks if the bound object is invalidated or not.
54
+ lua_pushcfunction
55
+ (
56
+ L,
57
+ [](lua_State* _state)
58
+ {
59
+ lua_getfield (_state, 1 , " weak_ref" );
60
+ auto weak_ref{ *static_cast <WeakRef*>(lua_touserdata (_state, -1 )) };
61
+ lua_settop (_state, 0 );
62
+ lua_pushboolean (_state, weak_ref.expired ());
63
+ return 1 ;
64
+ }
65
+ );
66
+ lua_setfield (L, -2 , " expired" );
67
+ }
68
+
69
+ // Helper to get self pointer.
70
+ // Doesn't work if self is poped before call of this method.
71
+ // You should use this method to cash the self pointer before all in the glue functions.
72
+ static CXXClass* self (lua_State* _state)
73
+ {
74
+ lua_getfield (_state, 1 , " this" );
75
+ auto this_{ lua_touserdata (_state, -1 ) };
76
+ return static_cast <CXXClass*>(this_);
77
+ }
78
+
79
+ // Return 1 value.
80
+ template <typename R, typename ... Args>
81
+ auto add_memfunc (R (CXXClass::*_memfunc)(Args...), const char* _name)->std::enable_if_t<detail::is_plain_type<R>::value>
82
+ {
83
+ using MemFunc = decltype (_memfunc);
84
+ detail::construct_userdata (L, _memfunc);
85
+ lua_pushcclosure
86
+ (
87
+ L,
88
+ [](lua_State* _state)->int
89
+ {
90
+ auto memfunc_ptr{detail::upvalue_cast<MemFunc*>(_state, 1 )};
91
+ auto result{call<Args...>(_state, bind (*memfunc_ptr, self (_state)), 2 )};
92
+ detail::push_cvalue (_state, result);
93
+ return 1 ;
94
+ },
95
+ 1
96
+ );
97
+ lua_setfield (L, -2 , _name);
98
+ }
99
+ // Return 0 value.
100
+ template <typename ... Args>
101
+ void add_memfunc (void (CXXClass::*_memfunc)(Args...), const char* _name)
102
+ {
103
+ using MemFunc = decltype (_memfunc);
104
+ detail::construct_userdata (L, _memfunc);
105
+ lua_pushcclosure
106
+ (
107
+ L,
108
+ [](lua_State* _state)->int
109
+ {
110
+ auto memfunc_ptr{detail::upvalue_cast<MemFunc*>(_state, 1 )};
111
+ call<Args...>(_state, bind (*memfunc_ptr, self (_state)), 2 );
112
+ return 0 ;
113
+ },
114
+ 1
115
+ );
116
+ lua_setfield (L, -2 , _name);
117
+ }
118
+ // Return multiple values.
119
+ template <typename ... Rs, typename ... Args>
120
+ void add_memfunc (Struct<Rs...>(CXXClass::*_memfunc)(Args...), const char* _name)
121
+ {
122
+ using MemFunc = decltype (_memfunc);
123
+ detail::construct_userdata (L, _memfunc);
124
+ lua_pushcclosure
125
+ (
126
+ L,
127
+ [](lua_State* _state)->int
128
+ {
129
+ auto memfunc_ptr{detail::upvalue_cast<MemFunc*>(_state, 1 )};
130
+ auto result{call<Args...>(_state, bind (*memfunc_ptr, self (_state)), 2 )};
131
+ // Call push_cvalue for each values of the result.
132
+ boost::fusion::for_each (result, [state = _state](auto && _v) {detail::push_cvalue (state, std::forward<decltype (_v)>(_v));});
133
+ // Return the number of elements of the result.
134
+ return boost::fusion::size (result);
135
+ },
136
+ 1
137
+ );
138
+ lua_setfield (L, -2 , _name);
139
+ }
140
+
141
+ // This will be needed if a returned value or arguments are of not lua compatible type.
142
+ //
143
+ // << EXAMPLE >>
144
+ //
145
+ // RegisterCXXClass<Widget> w(lua_state, "Widget");
146
+ // w.adapt_memfunc
147
+ // (
148
+ // [](lua_State* L)
149
+ // {
150
+ // auto self = self(L);
151
+ //
152
+ // //Process using self pointer.
153
+ // int result = self->method();
154
+ // lua_pushinteger(L, result);
155
+ //
156
+ // //Number of retuned values.
157
+ // return 1;
158
+ // }, "get" //Register this member of name "get"
159
+ // );
160
+ void adapt_memfunc (lua_CFunction _glue_func, const char * _name)
161
+ {
162
+ lua_pushcfunction (L, _glue_func);
163
+ lua_setfield (L, -2 , _name);
164
+ }
165
+
166
+ template <typename ... Args>
167
+ void finalize (std::shared_ptr<CXXClass>(*_make_shared)(Args...))
168
+ {
169
+ lua_setfield (L, -2 , " __index" );
170
+ lua_pushlightuserdata (L, _make_shared);
171
+
172
+ lua_pushcclosure
173
+ (
174
+ L,
175
+ [](lua_State* _state)->int
176
+ {
177
+ // Upvalue 1: metatable
178
+ // Upvalue 2: new adaptor
179
+
180
+ // Allocate and construct new object.
181
+ auto make_shared{static_cast <decltype (_make_shared)>(lua_touserdata (_state, lua_upvalueindex (2 )))};
182
+ auto initializer{detail::to_cvalues<Args...>(_state, 1 )};
183
+ auto ref{detail::call_with_expanded_tuple (make_shared, initializer)};
184
+
185
+ // Clear all arguments.
186
+ lua_settop (_state, 0 );
187
+
188
+ // Object structure:
189
+ // {
190
+ // this = pointer to C++ object,
191
+ // weak_ref = weak reference to the object
192
+ // }
193
+ lua_newtable (_state);
194
+ auto userdata{ static_cast <WeakRef*>(lua_newuserdata (_state, sizeof (WeakRef)) )};
195
+ new (userdata) WeakRef (ref);
196
+ lua_setfield (_state, -2 , " weak_ref" );
197
+ lua_pushlightuserdata (_state, ref.get ());
198
+ lua_setfield (_state, -2 , " this" );
199
+
200
+ // Set the metatable.
201
+ lua_pushvalue (_state, lua_upvalueindex (1 ));
202
+ lua_setmetatable (_state, -2 );
203
+
204
+ // Return new object.
205
+ return 1 ;
206
+ },
207
+ 2
208
+ );
209
+ lua_setfield (L, -2 , " new" );
210
+
211
+ // Finally, register the class to the global field.
212
+ lua_setglobal (L, name);
213
+ }
214
+ };
215
+
216
+ }
217
+ }
0 commit comments