Lua: Refactor lots of stuff
[lsnes.git] / include / library / lua-class.hpp
blob3e83daeab8bdfee5f5093c3d52200a12190d00ad
1 #ifndef _library__lua_class__hpp__included__
2 #define _library__lua_class__hpp__included__
4 #include "lua-base.hpp"
5 #include "lua-pin.hpp"
7 namespace lua
9 struct class_ops
11 bool (*is)(state& _state, int index);
12 const std::string& (*name)();
13 std::string (*print)(state& _state, int index);
16 std::list<class_ops>& userdata_recogn_fns();
17 std::string try_recognize_userdata(state& _state, int index);
18 std::string try_print_userdata(state& _state, int index);
19 std::unordered_map<std::type_index, void*>& class_types();
21 /**
22 * Helper class containing binding data for Lua class call.
24 template<class T> struct class_binding
26 /**
27 * The pointer to call.
29 int (T::*fn)(state& lstate, const std::string& _fname);
30 /**
31 * The state to call it in.
33 state* _state;
34 /**
35 * The name of the method to pass.
37 char fname[];
40 template<class T> class _class;
42 /**
43 * Function to obtain class object for given Lua class.
45 template<class T> _class<T>& objclass()
47 auto& type = typeid(T);
48 if(!class_types().count(type))
49 throw std::runtime_error("Internal error: Lua class not found!");
50 return *reinterpret_cast<_class<T>*>(class_types()[type]);
53 template<class T> struct class_method
55 /**
56 * Name.
58 const char* name;
59 /**
60 * Function.
62 int (T::*fn)(state& LS, const std::string& fname);
65 /**
66 * The type of Lua classes.
68 template<class T> class _class
70 template<typename... U> T* _create(state& _state, U... args)
72 void* obj = _state.newuserdata(sizeof(T));
73 load_metatable(_state);
74 _state.setmetatable(-2);
75 T* _obj = reinterpret_cast<T*>(obj);
76 new(_obj) T(_state, args...);
77 return _obj;
80 static int class_bind_trampoline(lua_State* LS)
82 try {
83 class_binding<T>* b = (class_binding<T>*)lua_touserdata(LS, lua_upvalueindex(1));
84 state L(*b->_state, LS);
85 T* p = _class<T>::get(L, 1, b->fname);
86 return (p->*(b->fn))(L, b->fname);
87 } catch(std::exception& e) {
88 std::string err = e.what();
89 lua_pushlstring(LS, err.c_str(), err.length());
90 lua_error(LS);
94 T* _get(state& _state, int arg, const std::string& fname, bool optional = false)
96 if(_state.type(arg) == LUA_TNONE || _state.type(arg) == LUA_TNIL) {
97 if(optional)
98 return NULL;
99 else
100 goto badtype;
102 load_metatable(_state);
103 if(!_state.getmetatable(arg))
104 goto badtype;
105 if(!_state.rawequal(-1, -2))
106 goto badtype;
107 _state.pop(2);
108 return reinterpret_cast<T*>(_state.touserdata(arg));
109 badtype:
110 (stringfmt() << "argument #" << arg << " to " << fname << " must be " << name).throwex();
111 return NULL; //Never reached.
114 bool _is(state& _state, int arg)
116 if(_state.type(arg) != LUA_TUSERDATA)
117 return false;
118 load_metatable(_state);
119 if(!_state.getmetatable(arg)) {
120 _state.pop(1);
121 return false;
123 bool ret = _state.rawequal(-1, -2);
124 _state.pop(2);
125 return ret;
128 objpin<T> _pin(state& _state, int arg, const std::string& fname)
130 T* obj = get(_state, arg, fname);
131 _state.pushvalue(arg);
132 objpin<T> t(_state, obj);
133 _state.pop(1);
134 return t;
136 public:
138 * Create a new Lua class.
140 * Parameter _name: The name of the class.
142 _class(const std::string& _name)
144 name = _name;
145 class_ops m;
146 m.is = _class<T>::is;
147 m.name = _class<T>::get_name;
148 m.print = _class<T>::print;
149 userdata_recogn_fns().push_back(m);
150 auto& type = typeid(T);
151 class_types()[type] = this;
155 * Create a new instance of object.
157 * Parameter _state: The Lua state to create the object in.
158 * Parameter args: The arguments to pass to class constructor.
160 template<typename... U> static T* create(state& _state, U... args)
162 return objclass<T>()._create(_state, args...);
166 * Bind a method to class.
168 * Parameter _state: The state to do the binding in.
169 * Parameter keyname: The name of the method.
170 * Parameter fn: The method to call.
171 * Parameter force: If true, overwrite existing method.
173 void bind(state& _state, const char* keyname, int (T::*fn)(state& LS, const std::string& fname))
175 load_metatable(_state);
176 _state.pushstring(keyname);
177 std::string fname = name + std::string("::") + keyname;
178 void* ptr = _state.newuserdata(sizeof(class_binding<T>) + fname.length() + 1);
179 class_binding<T>* bdata = reinterpret_cast<class_binding<T>*>(ptr);
180 bdata->fn = fn;
181 bdata->_state = &_state.get_master();
182 std::copy(fname.begin(), fname.end(), bdata->fname);
183 bdata->fname[fname.length()] = 0;
184 _state.pushcclosure(class_bind_trampoline, 1);
185 _state.rawset(-3);
186 _state.pop(1);
189 * Bind multiple at once.
191 void bind_multi(state& _state, std::initializer_list<class_method<T>> list, void* doonce_key = NULL)
193 static char once_key;
194 if(_state.do_once(doonce_key ? doonce_key : &once_key))
195 for(auto i : list)
196 bind(_state, i.name, i.fn);
199 * Get a pointer to the object.
201 * Parameter _state: The Lua state.
202 * Parameter arg: Argument index.
203 * Parameter fname: The name of function for error messages.
204 * Parameter optional: If true and argument is NIL or none, return NULL.
205 * Throws std::runtime_error: Wrong type.
207 static T* get(state& _state, int arg, const std::string& fname, bool optional = false)
208 throw(std::bad_alloc, std::runtime_error)
210 return objclass<T>()._get(_state, arg, fname, optional);
214 * Identify if object is of this type.
216 * Parameter _state: The Lua state.
217 * Parameter arg: Argument index.
218 * Returns: True if object is of specified type, false if not.
220 static bool is(state& _state, int arg) throw()
222 try {
223 return objclass<T>()._is(_state, arg);
224 } catch(...) {
225 return false;
229 * Get name of class.
231 static const std::string& get_name()
233 try {
234 return objclass<T>().name;
235 } catch(...) {
236 static std::string foo = "???";
237 return foo;
241 * Format instance of this class as string.
243 static std::string print(state& _state, int index)
245 T* obj = get(_state, index, "__internal_print");
246 return obj->print();
249 * Get a pin of object against Lua GC.
251 * Parameter _state: The Lua state.
252 * Parameter arg: Argument index.
253 * Parameter fname: Name of function for error message purposes.
254 * Throws std::runtime_error: Wrong type.
256 static objpin<T> pin(state& _state, int arg, const std::string& fname) throw(std::bad_alloc,
257 std::runtime_error)
259 return objclass<T>()._pin(_state, arg, fname);
261 private:
262 static int dogc(lua_State* LS)
264 T* obj = reinterpret_cast<T*>(lua_touserdata(LS, 1));
265 obj->~T();
266 return 0;
269 static int newindex(lua_State* LS)
271 lua_pushstring(LS, "Writing metatables of classes is not allowed");
272 lua_error(LS);
273 return 0;
276 static int index(lua_State* LS)
278 lua_getmetatable(LS, 1);
279 lua_pushvalue(LS, 2);
280 lua_rawget(LS, -2);
281 return 1;
284 void load_metatable(state& _state)
286 again:
287 _state.pushlightuserdata(this);
288 _state.rawget(LUA_REGISTRYINDEX);
289 if(_state.type(-1) == LUA_TNIL) {
290 _state.pop(1);
291 _state.pushlightuserdata(this);
292 _state.newtable();
293 _state.pushvalue(-1);
294 _state.setmetatable(-2);
295 _state.pushstring("__gc");
296 _state.pushcfunction(&_class<T>::dogc);
297 _state.rawset(-3);
298 _state.pushstring("__newindex");
299 _state.pushcfunction(&_class<T>::newindex);
300 _state.rawset(-3);
301 _state.pushstring("__index");
302 _state.pushcfunction(&_class<T>::index);
303 _state.rawset(-3);
304 _state.rawset(LUA_REGISTRYINDEX);
305 goto again;
308 std::string name;
309 _class(const _class<T>&);
310 _class& operator=(const _class<T>&);
314 #endif