-
Notifications
You must be signed in to change notification settings - Fork 1
/
LuaMethod.inl
252 lines (198 loc) · 9.17 KB
/
LuaMethod.inl
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
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
// Copyright � 2013 Tom Tondeur
//
// This file is part of LuaLink.
//
// LuaLink is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// LuaLink is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with LuaLink. If not, see <http://www.gnu.org/licenses/>.
#include "LuaStack.hpp"
#include "TemplateUtil.h"
#include <vector>
#include <map>
namespace LuaLink
{
template<typename ClassT>
//Struct form of wrapper/callbacks, necessary to keep a lookup table of all wrappers/callbacks
struct LuaMethod<ClassT>::Unsafe_MethodWrapper{
Unsafe_MethodWrapper(WrapperDoubleArg w1, WrapperSingleArg w2, Unsafe_MethodType cb) : pWrapper(w1), pWrapperSingle(w2), pFunc(cb){}
WrapperDoubleArg pWrapper; //Used for calls to overloaded member functions
WrapperSingleArg pWrapperSingle; //Used for calls to non-overloaded member functions
Unsafe_MethodType pFunc; //Serves as callback, discards return & argument types, wrappers restore return & argument types
};
template<typename ClassT>
template<typename _RetType, typename... _ArgTypes>
// // Add a C++ member function to the appropriate lookup table
void LuaMethod<ClassT>::Register(_RetType(ClassT::*pFunc)(_ArgTypes...), const char* name)
{
//Get iterator that points to the lookup table associated with 'name'
auto it = s_LuaFunctionMap.find(name);
if( it == s_LuaFunctionMap.end() )
it = s_LuaFunctionMap.insert(make_pair(name, std::vector<Unsafe_MethodWrapper>() ) ).first;
//Add wrapper to lookup table
auto test = Unsafe_MethodWrapper(detail::MethodWrapper<ClassT, _RetType, _ArgTypes...>::execute,
detail::MethodWrapper<ClassT, _RetType, _ArgTypes...>::execute,
reinterpret_cast<Unsafe_MethodType>(pFunc));
it->second.push_back(test);
}
template<typename ClassT>
// // Pushes all registered member functions to the Lua environment
void LuaMethod<ClassT>::Commit(lua_State* pLuaState, int tablePosOnStack)
{
if(!s_LuaFunctionTable.empty())
s_LuaFunctionTable.clear();
for(auto& elem : s_LuaFunctionMap)
{
//No overloading
if(elem.second.size() == 1){
s_LuaFunctionTable.push_back(elem.second[0]); //Add function to the lookup table
lua_pushstring(pLuaState, elem.first ); //Push function name
//Push index of function in lookup table & wrapper as closure
lua_pushinteger(pLuaState, s_LuaFunctionTable.size() - 1);
lua_pushcclosure(pLuaState, elem.second[0].pWrapperSingle, 1);
lua_settable(pLuaState, tablePosOnStack); //Add entry to the Lua table
continue;
}
//This function needs to be overloaded =>
//Move functions to lookup table
size_t startIdx = s_LuaFunctionTable.size();
size_t endIdx = startIdx + elem.second.size();
std::move(elem.second.begin(), elem.second.end(), std::insert_iterator<std::vector<Unsafe_MethodWrapper>>(s_LuaFunctionTable, s_LuaFunctionTable.end()));
lua_pushstring(pLuaState, elem.first); //Push function name
//Push start and end indices of functions in lookup table (endIdx is one past last function, like .end() iterators)
lua_pushinteger(pLuaState, startIdx);
lua_pushinteger(pLuaState, endIdx);
lua_pushcclosure(pLuaState, OverloadDispatch, 2); //Push closure
lua_settable(pLuaState, tablePosOnStack); //Set table
}
s_LuaFunctionMap.clear();
}
template<typename ClassT>
void LuaMethod<ClassT>::PushThisPointer(lua_State* L)
{
//Make sure the object was pushed to the stack
if(lua_istable(L, 1) == 0)
luaL_error(L, "Calling a nonstatic member function requires a reference to an object");
//Push ppObj to stack
lua_pushstring(L, "core_");
lua_rawget(L, 1);
if(lua_isuserdata(L, -1) == 0)
luaL_error(L, "Calling a nonstatic member function requires a reference to an object");
lua_remove(L, 1); //Remove 'self table' from stack
}
template<typename ClassT>
// Tries out all overloads until it finds an overload that matches the arguments used in the Lua call
int LuaMethod<ClassT>::OverloadDispatch(lua_State* L)
{
//Retrieve range in lookup table where overloads are located
auto startIdx = static_cast<int>( lua_tointeger( L, lua_upvalueindex(1) ) );
auto endIdx = static_cast<int>( lua_tointeger( L, lua_upvalueindex(2) ) );
PushThisPointer(L);
//Try all functions
for(auto i = startIdx; i < endIdx; ++i){
int ret = s_LuaFunctionTable[i].pWrapper(L, s_LuaFunctionTable[i].pFunc, LuaFunction::OverloadedErrorHandling);
if(ret < 0)
continue;
return ret;
}
//No correct overload found
return luaL_error(L, "Invalid member function call - no overload found that takes these parameters.");
}
template<typename ClassT>
// // Common code in all MethodWrappers, returns pointer to pointer to object to call member function on
ClassT** LuaMethod<ClassT>::GetObjectAndVerifyStackSize(lua_State* L, int nrOfArgs)
{
int argc = lua_gettop(L);
if(argc != nrOfArgs + 1) //stack should contain 'this pointer' + args
return nullptr;
auto ppObj = static_cast<ClassT**>(lua_touserdata(L, -1) ); //Retrieve internal object
lua_pop(L, 1); //Remove internal object from stack
return ppObj; //Return internal object
}
// CALLBACK WRAPPERS
#define GET_THIS(ARGC) auto ppObj = LuaMethod<ClassT>::GetObjectAndVerifyStackSize(pLuaState, ARGC); \
if(!ppObj) \
return onArgError(pLuaState, 0); \
bool isOk = true; \
(void)isOk;
#define DO_LUACALLBACK(CBTYPE,...) ((*ppObj)->*(reinterpret_cast<CBTYPE>(fn)))( __VA_ARGS__ )
#define EXECUTE_V2 static int execute(lua_State* L){ \
LuaMethod<ClassT>::PushThisPointer(L);\
return execute(L, LuaMethod<ClassT>::s_LuaFunctionTable[static_cast<unsigned int>(lua_tointeger( L, lua_upvalueindex(1) ) )].pFunc, ::LuaLink::LuaFunction::DefaultErrorHandling);}
namespace detail {
// ret, n args
template<typename ClassT, typename _RetType, typename... _ArgTypes>
struct MethodWrapper
{
typedef _RetType(ClassT::*CbType)(_ArgTypes...);
static int execute(lua_State* pLuaState, typename LuaMethod<ClassT>::Unsafe_MethodType fn, ArgErrorCbType onArgError)
{
GET_THIS(sizeof...(_ArgTypes))
int errnum = 0;
auto tpl = build_tuple_from_lua_stack<_ArgTypes...>::execute(pLuaState, 1, isOk, onArgError, errnum);
if(!isOk)
return errnum;
LuaStack::pushVariable( pLuaState, call_mem(reinterpret_cast<CbType>(fn), *ppObj, tpl) );
return 1;
}
EXECUTE_V2
};
//no ret, n args
template<typename ClassT, typename... _ArgTypes>
struct MethodWrapper<ClassT, void, _ArgTypes...>
{
typedef void(ClassT::*CbType)(_ArgTypes...);
static int execute(lua_State* pLuaState, typename LuaMethod<ClassT>::Unsafe_MethodType fn, ArgErrorCbType onArgError)
{
GET_THIS(sizeof...(_ArgTypes))
int errnum = 0;
auto tpl = build_tuple_from_lua_stack<_ArgTypes...>::execute(pLuaState, 1, isOk, onArgError, errnum);
if(!isOk)
return errnum;
call_mem(reinterpret_cast<CbType>(fn), *ppObj, tpl);
return 0;
}
EXECUTE_V2
};
//no ret, 0 args
template<typename ClassT>
struct MethodWrapper<ClassT, void>
{
static int execute(lua_State* pLuaState, typename LuaMethod<ClassT>::Unsafe_MethodType fn, ArgErrorCbType onArgError)
{
GET_THIS(0);
DO_LUACALLBACK( void(ClassT::*)(void) );
return 0;
}
EXECUTE_V2
};
//ret, 0 args
template<typename ClassT, typename _RetType>
struct MethodWrapper<ClassT, _RetType>
{
static int execute(lua_State* pLuaState, typename LuaMethod<ClassT>::Unsafe_MethodType fn, ArgErrorCbType onArgError)
{
GET_THIS(0)
LuaStack::pushVariable( pLuaState, DO_LUACALLBACK( _RetType(ClassT::*)(void) ) );
return 1;
}
EXECUTE_V2
};
}
#undef GET_THIS
#undef GET_ARG
#undef DO_LUACALLBACK
#undef EXECUTE_V2
template<typename ClassT>
std::vector<typename LuaMethod<ClassT>::Unsafe_MethodWrapper> LuaMethod<ClassT>::s_LuaFunctionTable;
template<typename ClassT>
std::map<const char*, std::vector<typename LuaMethod<ClassT>::Unsafe_MethodWrapper>, detail::CStrCmp > LuaMethod<ClassT>::s_LuaFunctionMap;
}