-
Notifications
You must be signed in to change notification settings - Fork 1
/
LuaScript.inl
285 lines (240 loc) · 10.1 KB
/
LuaScript.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
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
// 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 <sstream>
#include <map>
#include "LuaStack.hpp"
#include "LuaClass.hpp"
#include "LuaAuto.hpp"
namespace LuaLink
{
struct LuaLoadException : public std::runtime_error
{
explicit LuaLoadException(const char* msg):std::runtime_error(msg){}
};
struct LuaCallException : public std::runtime_error
{
explicit LuaCallException(const char* msg):std::runtime_error(msg){}
};
#define LUA_STATE s_pLuaState.get()
//Call implementations
template<typename _RetType> //1 return value
struct LuaScript::Call
{
template<typename... _ArgTypes>
static _RetType LuaFunction(const char* functionName, _ArgTypes... arguments)
{
//Look for global function with the provided name
lua_getglobal( LUA_STATE, functionName );
if( lua_type(LUA_STATE, lua_gettop(LUA_STATE)) == LUA_TNIL ){
lua_settop (LUA_STATE, 0);
throw LuaCallException( ("Global not found: " + std::string(functionName) ).c_str() );
}
//Push arguments onto the Lua stack
LuaStack::pushStack<_ArgTypes...>(LUA_STATE, arguments...);
//Perform function call
if (lua_pcall(LUA_STATE, sizeof...(_ArgTypes), 1, 0) != 0)
throw LuaCallException(lua_tostring(LUA_STATE, -1));
//Check return value
bool isOk = true;
auto ret = LuaStack::getVariable<_RetType>(LUA_STATE, -1, isOk);
if(!isOk){
std::stringstream strstr;
strstr << "Error: Expected return type " << typeid(_RetType).name() << " does not match the value returned by " << functionName;
throw LuaCallException(strstr.str().c_str());
}
return ret;
}
template<typename... _ArgTypes>
static _RetType LuaStaticMethod(const char* tableName, const char* functionName, _ArgTypes... arguments)
{
//Look for global table with the provided name
lua_getglobal( LUA_STATE, tableName );
if( lua_type(LUA_STATE, lua_gettop(LUA_STATE)) == LUA_TNIL ){
lua_settop (LUA_STATE, 0);
throw LuaCallException( ("Global not found: " + std::string(tableName) ).c_str() );
}
//Look for function in that table
lua_getfield(LUA_STATE, -1, functionName );
if(!lua_isfunction(LUA_STATE, -1)){
lua_settop (LUA_STATE, 0);
throw LuaCallException( (std::string(functionName) + " is not a function in " + tableName).c_str() );
}
//Push arguments onto the Lua stack
LuaStack::pushStack<_ArgTypes...>(LUA_STATE, arguments...);
//Perform function call
if (lua_pcall(LUA_STATE, sizeof...(_ArgTypes), 1, 0) != 0)
throw LuaCallException(lua_tostring(LUA_STATE, -1));
//Check return value
bool isOk = true;
auto ret = LuaStack::getVariable<_RetType>(LUA_STATE, -1, isOk);
if(!isOk){
std::stringstream strstr;
strstr << "Error: Expected return type " << typeid(_RetType).name() << " does not match the value returned by " << tableName << "::" << functionName;
throw LuaCallException(strstr.str().c_str());
}
return ret;
}
};
template<> //No return value
struct LuaScript::Call<void>
{
template<typename... _ArgTypes>
static void LuaFunction(const char* functionName, _ArgTypes... arguments)
{
//Look for global function with the provided name
lua_getglobal( LUA_STATE, functionName );
if( lua_type(LUA_STATE, lua_gettop(LUA_STATE)) == LUA_TNIL ){
lua_settop (LUA_STATE, 0);
throw LuaCallException( ("Global not found: " + std::string(functionName) ).c_str() );
}
//Push arguments onto the Lua stack
LuaStack::pushStack<_ArgTypes...>(LUA_STATE, arguments...);
//Perform function call
if (lua_pcall(LUA_STATE, sizeof...(_ArgTypes), 0, 0) != 0)
throw LuaCallException(lua_tostring(LUA_STATE, -1));
}
template<typename... _ArgTypes>
static void LuaStaticMethod(const char* tableName, const char* functionName, _ArgTypes... arguments)
{
//Look for global table with the provided name
lua_getglobal( LUA_STATE, tableName );
if( lua_type(LUA_STATE, lua_gettop(LUA_STATE)) == LUA_TNIL ){
lua_settop (LUA_STATE, 0);
throw LuaCallException( ("Global not found: " + std::string(tableName) ).c_str() );
}
//Look for function in that table
lua_getfield(LUA_STATE, -1, functionName );
if(!lua_isfunction(LUA_STATE, -1)){
lua_settop (LUA_STATE, 0);
throw LuaCallException( (std::string(functionName) + " is not a function in " + tableName).c_str() );
}
//Push arguments onto the Lua stack
LuaStack::pushStack<_ArgTypes...>(LUA_STATE, arguments...);
//Perform function call
if (lua_pcall(LUA_STATE, sizeof...(_ArgTypes), 0, 0) != 0)
throw LuaCallException(lua_tostring(LUA_STATE, -1));
}
};
template<typename _RetType, typename... _ArgTypes>
_RetType LuaScript::CallFunction(const char* fnName, _ArgTypes... args)
{
return LuaScript::Call<_RetType>::LuaFunction(fnName, args...);
}
template<typename _RetType, typename... _ArgTypes>
_RetType LuaScript::CallMethod(const char* className, const char* fnName, _ArgTypes... args)
{
return LuaScript::Call<_RetType>::LuaStaticMethod(className, fnName, args...);
}
#ifdef LUALINK_DEFINE
//Initialize static members
std::unique_ptr<lua_State> LuaScript::s_pLuaState;
//Constructor & destructor
LuaScript::LuaScript(const char* filename) : m_Filename(filename), InitializeEnvironment(nullptr) {}
LuaScript::~LuaScript(void) {}
//Methods
// // Opens and loads the file linked to this object
void LuaScript::Load(void(*initializeEnvironmentFn)(void), bool bOpenLibs, bool bResetState)
{
InitializeEnvironment = initializeEnvironmentFn; //Set initializer callback
//Allocate new lua_State if necessary
if(bResetState || !s_pLuaState){
s_pLuaState = std::unique_ptr<lua_State>(lua_newstate(&LuaAllocate, nullptr)); //Unique pointer automatically destroys previous lua_State
if(!s_pLuaState)
throw LuaLoadException("Error allocating new lua state");
}
//Opens commonly used libraries
if(bOpenLibs)
luaL_openlibs(LUA_STATE);
// Load a Lua script chunk without executing it
switch(luaL_loadfile(s_pLuaState.get(), m_Filename ))
{
case 0:
break;
case LUA_ERRFILE: // Unable to open the file from luaL_loadfile()
case LUA_ERRSYNTAX: // Syntax error in the lua code in the file from lua_load()
case LUA_ERRMEM: // Memory allocation error from lua_load()
throw LuaLoadException(lua_tostring(LUA_STATE, -1));
break;
default:
throw LuaLoadException(("An unknown error has occured while loading file " + std::string(m_Filename)).c_str());
}
}
// // Adds all registered C++ functions and classes to the environment and performs an initial run
void LuaScript::Initialize(void)
{
if(LUA_STATE == nullptr)
Load();
LuaFunction::Release(); //Release any leftover functions
LuaAutoFunction::RegisterAll();
LuaAutoClass::RegisterAll();
if (InitializeEnvironment)
InitializeEnvironment();
LuaFunction::Commit(LUA_STATE); //Commit all functions registered in 'InitializeEnvironment'
//Runs the script a first time to register functions and classes declared in the Lua script
switch(lua_pcall(LUA_STATE, 0, LUA_MULTRET, 0))
{
case 0:
break;
case LUA_ERRRUN:
case LUA_ERRMEM:
case LUA_ERRERR:
throw LuaCallException(lua_tostring(LUA_STATE, -1));
break;
default:
throw LuaCallException(("An unknown error has occured while executing file " + std::string(m_Filename)).c_str());
}
}
// The type of the memory-allocation function used by Lua states. The allocator function
// must provide a functionality similar to realloc, but not exactly the same. Its arguments
// are:
// ud, an opaque pointer passed to lua_newstate;
// ptr, a pointer to the block being allocated/reallocated/freed;
// osize, the original size of the block;
// nsize, the new size of the block.
//
// ptr is NULL if and only if osize is zero. When nsize is zero, the allocator
// must return NULL; if osize is not zero, it should free the block pointed to by ptr. When nsize
// is not zero, the allocator returns NULL if and only if it cannot fill the request. When nsize
// is not zero and osize is zero, the allocator should behave like malloc. When nsize and osize
// are not zero, the allocator behaves like realloc().
//
// Lua assumes that the allocator never fails when osize >= nsize.
//
// See also this documentation http://www.lua.org/manual/5.2/manual.html#lua_Alloc
//
void* LuaScript::LuaAllocate(void *ud, void *ptr, size_t osize, size_t nsize)
{
void *pOut = nullptr;
if (osize && nsize && ptr) {
if (osize < nsize)
pOut = realloc (ptr, nsize);
else
pOut = ptr;
}
else if (nsize == 0)
free(ptr);
else
pOut = malloc (nsize);
return pOut;
}
lua_State* LuaScript::GetLuaState(void)
{
return LUA_STATE;
}
#endif //LUALINK_DEFINE
#undef LUA_STATE
}