1 #ifndef __LUA_TEMPLATES_H
2 #define __LUA_TEMPLATES_H
3 
4 /*
5 LUA_TEMPLATES.H
6 
7 	Copyright (C) 2008 by Gregory Smith
8 
9 	This program is free software; you can redistribute it and/or modify
10 	it under the terms of the GNU General Public License as published by
11 	the Free Software Foundation; either version 3 of the License, or
12 	(at your option) any later version.
13 
14 	This program is distributed in the hope that it will be useful,
15 	but WITHOUT ANY WARRANTY; without even the implied warranty of
16 	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 	GNU General Public License for more details.
18 
19 	This license is contained in the file "COPYING",
20 	which is included with this source code; it is available online at
21 	http://www.gnu.org/licenses/gpl.html
22 
23 	Templates to help create the Lua/C interface
24 */
25 
26 #include "cseries.h"
27 
28 #ifdef HAVE_LUA
29 extern "C"
30 {
31 #include "lua.h"
32 #include "lauxlib.h"
33 #include "lualib.h"
34 }
35 
36 #include <boost/function.hpp>
37 #include "lua_script.h"
38 #include "lua_mnemonics.h" // for lang_def and mnemonics
39 #include <sstream>
40 #include <map>
41 #include <new>
42 
luaL_typerror(lua_State * L,int narg,const char * tname)43 static inline int luaL_typerror(lua_State* L, int narg, const char* tname)
44 {
45 	const char *msg = lua_pushfstring(L, "%s expected, got %s",
46 					  tname, luaL_typename(L, narg));
47 	(void)luaL_argerror(L, narg, msg); // does not actually return
48 	assert(false);  // satisfy clang static analyzer
49 	return 0;  // satisfy compiler
50 }
51 
52 // helper functions for finding the script's data path
53 // not in lua_script.h because they expose lua_State
54 extern void L_Set_Search_Path(lua_State* L, const std::string& path);
55 extern std::string L_Get_Search_Path(lua_State* L);
56 
57 extern bool L_Get_Proper_Item_Accounting(lua_State* L);
58 extern void L_Set_Proper_Item_Accounting(lua_State* L, bool value);
59 
60 extern bool L_Get_Nonlocal_Overlays(lua_State* L);
61 extern void L_Set_Nonlocal_Overlays(lua_State* L, bool value);
62 
63 // pushes a function that returns the parameterized function
64 template<lua_CFunction f>
L_TableFunction(lua_State * L)65 int L_TableFunction(lua_State *L)
66 {
67 	lua_pushcfunction(L, f);
68 	return 1;
69 }
70 
71 template<char *name, typename index_t = int16>
72 class L_Class {
73 public:
74 
75 	index_t m_index;
76 	typedef index_t index_type;
77 
78 	static void Register(lua_State *L, const luaL_Reg get[] = 0, const luaL_Reg set[] = 0, const luaL_Reg metatable[] = 0);
79 
80 	template<typename instance_t = L_Class /*or a derived class*/>
81 	static instance_t *Push(lua_State *L, index_t index);
82 
83 	static L_Class *Instance(lua_State *L, int index);
84 	static index_t Index(lua_State *L, int index);
85 	static bool Is(lua_State *L, int index);
86 	static void Invalidate(lua_State *L, index_t index);
87 	static boost::function<bool (index_t)> Valid;
88 	struct ValidRange
89 	{
ValidRangeValidRange90 		ValidRange(int32 max_index) : m_max(max_index) {}
operatorValidRange91 		bool operator() (int32 index)
92 		{
93 			return (index >= 0 && index < m_max);
94 		}
95 
96 		int32 m_max;
97 	};
98 
99 	// ghs: codewarrior chokes on this:
100 	//	template<index_t max_index> static bool ValidRange(index_t index) { return index >= 0 && index < max_index; }
101 private:
102 	// C functions for Lua
103 	static int _index(lua_State *L);
104 	static int _set(lua_State *L);
105 	static int _is(lua_State *L);
106 	static int _new(lua_State *L);
107 	static int _tostring(lua_State *L);
108 
109 protected:
110 	template<typename instance_t /*L_Class or a derived class*/>
111 	static instance_t *NewInstance(lua_State *L, index_t index);
112 
113 	static int _get(lua_State *L);
114 
115 	// registry keys
_push_get_methods_key(lua_State * L)116 	static void _push_get_methods_key(lua_State *L) {
117 		lua_pushlightuserdata(L, (void *) (&name[1]));
118 	}
119 
_push_set_methods_key(lua_State * L)120 	static void _push_set_methods_key(lua_State *L) {
121 		lua_pushlightuserdata(L, (void *) (&name[2]));
122 	}
123 
_push_instances_key(lua_State * L)124 	static void _push_instances_key(lua_State *L) {
125 		lua_pushlightuserdata(L, (void *) (&name[3]));
126 	}
127 
128 	// special tables
129 	static void _push_custom_fields_table(lua_State *L);
130 };
131 
132 struct always_valid
133 {
operatoralways_valid134 	bool operator()(int32 x) { return true; }
135 };
136 
137 template<char *name, typename index_t>
138 boost::function<bool (index_t)> L_Class<name, index_t>::Valid = always_valid();
139 
140 template<char *name, typename index_t>
Register(lua_State * L,const luaL_Reg get[],const luaL_Reg set[],const luaL_Reg metatable[])141 void L_Class<name, index_t>::Register(lua_State *L, const luaL_Reg get[], const luaL_Reg set[], const luaL_Reg metatable[])
142 {
143 	// create the metatable itself
144 	luaL_newmetatable(L, name);
145 
146 	// Lua 5.1 doesn't do this reverse mapping any more--do it ourselves
147 	lua_pushvalue(L, -1);
148 	lua_pushstring(L, name);
149 	lua_settable(L, LUA_REGISTRYINDEX);
150 
151 	// register metatable get
152 	lua_pushcfunction(L, _get);
153 	lua_setfield(L, -2, "__index");
154 
155 	// register metatable set
156 	lua_pushcfunction(L, _set);
157 	lua_setfield(L, -2, "__newindex");
158 
159 	// register metatable tostring
160 	lua_pushcfunction(L, _tostring);
161 	lua_setfield(L, -2, "__tostring");
162 
163 	lua_pushcfunction(L, _new);
164 	lua_setfield(L, -2, "__new");
165 
166 	if (metatable)
167 		luaL_setfuncs(L, metatable, 0);
168 
169 	// clear the stack
170 	lua_pop(L, 1);
171 
172 	// register get methods
173 	_push_get_methods_key(L);
174 	lua_newtable(L);
175 
176 	// always want index
177 	lua_pushcfunction(L, _index);
178 	lua_setfield(L, -2, "index");
179 
180 	if (get)
181 		luaL_setfuncs(L, get, 0);
182 	lua_settable(L, LUA_REGISTRYINDEX);
183 
184 	// register set methods
185 	_push_set_methods_key(L);
186 	lua_newtable(L);
187 
188 	if (set)
189 		luaL_setfuncs(L, set, 0);
190 	lua_settable(L, LUA_REGISTRYINDEX);
191 
192 	// register a table for instances
193 	_push_instances_key(L);
194 	lua_newtable(L);
195 	lua_settable(L, LUA_REGISTRYINDEX);
196 
197 	// register is_
198 	lua_pushcfunction(L, _is);
199 	std::string is_name = "is_" + std::string(name);
200 	lua_setglobal(L, is_name.c_str());
201 }
202 
203 template<char *name, typename index_t>
204 template<typename instance_t>
NewInstance(lua_State * L,index_t index)205 instance_t *L_Class<name, index_t>::NewInstance(lua_State *L, index_t index)
206 {
207 	// Storing an L_Class ptr at offset 0 in the userdata block lets
208 	// L_Class::Instance() avoid having to be type-dependent on instance_t
209 
210 	struct UserdataBlock // standard-layout
211 	{
212 		L_Class *basePtr; // offset 0
213 		alignas(instance_t) unsigned char instanceBuffer[sizeof(instance_t)];
214 	};
215 
216 	auto blockPtr = new (lua_newuserdata(L, sizeof(UserdataBlock))) UserdataBlock;
217 	auto instancePtr = new (blockPtr->instanceBuffer) instance_t();
218 
219 	blockPtr->basePtr = instancePtr;
220 	blockPtr->basePtr->m_index = index;
221 	luaL_getmetatable(L, name);
222 	lua_setmetatable(L, -2);
223 
224 	return instancePtr;
225 }
226 
227 template<char *name, typename index_t>
Instance(lua_State * L,int index)228 L_Class<name, index_t> *L_Class<name, index_t>::Instance(lua_State *L, int index)
229 {
230 	// The userdata block is assumed to start with an L_Class ptr; see L_Class::NewInstance()
231 	void * blockPtr = lua_touserdata(L, index);
232 	return blockPtr ? *static_cast<L_Class **>(blockPtr) : nullptr;
233 }
234 
235 template<char *name, typename index_t>
236 template<typename instance_t>
Push(lua_State * L,index_t index)237 instance_t *L_Class<name, index_t>::Push(lua_State *L, index_t index)
238 {
239 	instance_t* t = 0;
240 
241 	if (!Valid(index))
242 	{
243 		lua_pushnil(L);
244 		return 0;
245 	}
246 
247 	// look it up in the index table
248 	_push_instances_key(L);
249 	lua_gettable(L, LUA_REGISTRYINDEX);
250 
251 	lua_pushnumber(L, index);
252 	lua_gettable(L, -2);
253 
254 	if (lua_isnil(L, -1))
255 	{
256 		lua_pop(L, 1);
257 
258 		// create an instance
259 		t = NewInstance<instance_t>(L, index);
260 
261 		// insert it into the instance table
262 		lua_pushnumber(L, index);
263 		lua_pushvalue(L, -2);
264 		lua_settable(L, -4);
265 
266 	}
267 	else
268 	{
269 		t = static_cast<instance_t *>(Instance(L, -1));
270 	}
271 
272 	// remove the instance table
273 	lua_remove(L, -2);
274 
275 	return t;
276 }
277 
278 template<char *name, typename index_t>
Index(lua_State * L,int index)279 index_t L_Class<name, index_t>::Index(lua_State *L, int index)
280 {
281 	L_Class<name, index_t> *t = Instance(L, index);
282 	if (!t) luaL_typerror(L, index, name);
283 	return t->m_index;
284 }
285 
286 template<char *name, typename index_t>
Is(lua_State * L,int index)287 bool L_Class<name, index_t>::Is(lua_State *L, int index)
288 {
289 	L_Class<name, index_t>* t = Instance(L, index);
290 	if (!t) return false;
291 
292 	if (lua_getmetatable(L, index))
293 	{
294 		lua_getfield(L, LUA_REGISTRYINDEX, name);
295 		if (lua_rawequal(L, -1, -2))
296 		{
297 			lua_pop(L, 2);
298 			return true;
299 		}
300 		else
301 		{
302 			lua_pop(L, 2);
303 			return false;
304 		}
305 	}
306 
307 	return false;
308 }
309 
310 template<char *name, typename index_t>
Invalidate(lua_State * L,index_t index)311 void L_Class<name, index_t>::Invalidate(lua_State *L, index_t index)
312 {
313 	// remove it from the index table
314 	_push_instances_key(L);
315 	lua_gettable(L, LUA_REGISTRYINDEX);
316 
317 	lua_pushnumber(L, index);
318 	lua_pushnil(L);
319 	lua_settable(L, -3);
320 	lua_pop(L, 1);
321 
322 	// clear custom fields
323 	lua_pushlightuserdata(L, (void *) (L_Persistent_Table_Key()));
324 	lua_gettable(L, LUA_REGISTRYINDEX);
325 	lua_getfield(L, -1, name);
326 	if (!lua_isnil(L, -1))
327 	{
328 		lua_pushnumber(L, index);
329 		lua_pushnil(L);
330 		lua_settable(L, -3);
331 	}
332 	lua_pop(L, 2);
333 }
334 
335 template<char *name, typename index_t>
_index(lua_State * L)336 int L_Class<name, index_t>::_index(lua_State *L)
337 {
338 	lua_pushnumber(L, Index(L, 1));
339 	return 1;
340 }
341 
342 template<char *name, typename index_t>
_is(lua_State * L)343 int L_Class<name, index_t>::_is(lua_State *L)
344 {
345 	lua_pushboolean(L, Is(L, 1));
346 	return 1;
347 }
348 
349 template<char *name, typename index_t>
_get(lua_State * L)350 int L_Class<name, index_t>::_get(lua_State *L)
351 {
352 	if (lua_isstring(L, 2))
353 	{
354 		luaL_checktype(L, 1, LUA_TUSERDATA);
355 		luaL_checkudata(L, 1, name);
356 		if (!Valid(Index(L, 1)) && strcmp(lua_tostring(L, 2), "valid") != 0 && strcmp(lua_tostring(L, 2), "index") != 0)
357 			luaL_error(L, "invalid object");
358 
359 		if (lua_tostring(L, 2)[0] == '_')
360 		{
361 			_push_custom_fields_table(L);
362 			lua_pushnumber(L, Index(L, 1));
363 			lua_gettable(L, -2);
364 			if (lua_istable(L, -1))
365 			{
366 				lua_pushvalue(L, 2);
367 				lua_gettable(L, -2);
368 				lua_remove(L, -2);
369 			}
370 			else
371 			{
372 				lua_pop(L, 1);
373 				lua_pushnil(L);
374 			}
375 
376 			lua_remove(L, -2);
377 		}
378 		else
379 		{
380 			// pop the get table
381 			_push_get_methods_key(L);
382 			lua_gettable(L, LUA_REGISTRYINDEX);
383 
384 			// get the function from that table
385 			lua_pushvalue(L, 2);
386 			lua_gettable(L, -2);
387 			lua_remove(L, -2);
388 
389 			if (lua_isfunction(L, -1))
390 			{
391 				// execute the function with table as our argument
392 				lua_pushvalue(L, 1);
393 				if (lua_pcall(L, 1, 1, 0) == LUA_ERRRUN)
394 				{
395 					// report the error as being on this line
396 					luaL_where(L, 1);
397 					lua_pushvalue(L, -2);
398 					lua_concat(L, 2);
399 					lua_error(L);
400 				}
401 			}
402 			else
403 			{
404 				lua_pop(L, 1);
405 				lua_pushnil(L);
406 			}
407 		}
408 	}
409 	else
410 	{
411 		lua_pushnil(L);
412 	}
413 
414 	return 1;
415 }
416 
417 template<char *name, typename index_t>
_new(lua_State * L)418 int L_Class<name, index_t>::_new(lua_State *L)
419 {
420 	if (lua_isnumber(L, 1))
421 	{
422 		index_t index = static_cast<index_t>(lua_tonumber(L, 1));
423 		lua_pop(L, 1);
424 		Push(L, index);
425 		return 1;
426 	}
427 	else
428 	{
429 		return 0;
430 	}
431 }
432 
433 template<char *name, typename index_t>
_set(lua_State * L)434 int L_Class<name, index_t>::_set(lua_State *L)
435 {
436 	luaL_checktype(L, 1, LUA_TUSERDATA);
437 	luaL_checkudata(L, 1, name);
438 
439 	if (lua_isstring(L, 2) && lua_tostring(L, 2)[0] == '_')
440 	{
441 		_push_custom_fields_table(L);
442 		lua_pushnumber(L, Index(L, 1));
443 		lua_gettable(L, -2);
444 		if (lua_istable(L, -1))
445 		{
446 			lua_pushvalue(L, 2);
447 			lua_pushvalue(L, 3);
448 			lua_settable(L, -3);
449 			lua_pop(L, 1);
450 		}
451 		else
452 		{
453 			lua_pop(L, 1);
454 
455 			lua_newtable(L);
456 
457 			lua_pushnumber(L, Index(L, 1));
458 			lua_pushvalue(L, -2);
459 			lua_settable(L, -4);
460 
461 			lua_pushvalue(L, 2);
462 			lua_pushvalue(L, 3);
463 			lua_settable(L, -3);
464 			lua_pop(L, 1);
465 		}
466 
467 		lua_pop(L, 1);
468 	}
469 	else
470 	{
471 		// pop the set table
472 		_push_set_methods_key(L);
473 		lua_gettable(L, LUA_REGISTRYINDEX);
474 
475 		// get the function from that table
476 		lua_pushvalue(L, 2);
477 		lua_gettable(L, -2);
478 
479 		if (lua_isnil(L, -1))
480 		{
481 			luaL_error(L, "no such index");
482 		}
483 
484 		// execute the function with table, value as our arguments
485 		lua_pushvalue(L, 1);
486 		lua_pushvalue(L, 3);
487 		if (lua_pcall(L, 2, 0, 0) == LUA_ERRRUN)
488 		{
489 			// report the error as being on this line
490 			luaL_where(L, 1);
491 			lua_pushvalue(L, -2);
492 			lua_concat(L, 2);
493 			lua_error(L);
494 		}
495 
496 		lua_pop(L, 1);
497 	}
498 
499 	return 0;
500 }
501 
502 template<char *name, typename index_t>
_tostring(lua_State * L)503 int L_Class<name, index_t>::_tostring(lua_State *L)
504 {
505 	std::ostringstream s;
506 	s << name << " " << Index(L, 1);
507 	lua_pushstring(L, s.str().c_str());
508 	return 1;
509 }
510 
511 
512 template<char *name, typename index_t>
_push_custom_fields_table(lua_State * L)513 void L_Class<name, index_t>::_push_custom_fields_table(lua_State *L)
514 {
515 	lua_pushlightuserdata(L, (void *) (L_Persistent_Table_Key()));
516 	lua_gettable(L, LUA_REGISTRYINDEX);
517 
518 	lua_getfield(L, -1, name);
519 	if (lua_isnil(L, -1))
520 	{
521 		// remove the nil
522 		lua_pop(L, 1);
523 
524 		// push a new table
525 		lua_newtable(L);
526 		lua_pushvalue(L, -1);
527 		lua_setfield(L, -3, name);
528 	}
529 
530 	lua_remove(L, -2);
531 }
532 
533 
534 // enum classes define equality with numbers
535 template<char *name, typename index_t = int16>
536 class L_Enum : public L_Class<name, index_t>
537 {
538 public:
539 	static void Register(lua_State *L, const luaL_Reg get[] = 0, const luaL_Reg set[] = 0, const luaL_Reg metatable[] = 0, const lang_def mnemonics[] = 0);
540 	static index_t ToIndex(lua_State *L, int index);
541 	static void PushMnemonicTable(lua_State *L);
542 protected:
543 	static bool _lookup(lua_State *L, int index, index_t& to);
544 	static int _equals(lua_State *L);
545 private:
546 	static int _get_mnemonic(lua_State *L);
547 	static int _set_mnemonic(lua_State *L);
548 
_push_mnemonic_key(lua_State * L)549 	static void _push_mnemonic_key(lua_State *L) {
550 		lua_pushlightuserdata(L, (void *) (&name[4]));
551 	}
552 };
553 
554 // the justification for this specialization is long
555 template<char *name, typename index_t = int16>
556 class L_LazyEnum : public L_Enum<name, index_t>
557 {
558 public:
ToIndex(lua_State * L,int index)559 	static index_t ToIndex(lua_State *L, int index) {
560 		index_t to;
561 		if(lua_isnil(L, index)) return -1;
562 		else if(L_Enum<name, index_t>::_lookup(L, index, to)) return to;
563 		else {
564 			std::string error;
565 			if(lua_isnumber(L, index) || lua_isstring(L, index))
566 				error = std::string(name) + ": invalid index";
567 			else
568 				error = std::string(name) + ": incorrect argument type";
569 			return luaL_error(L, error.c_str());
570 		}
571 	}
572 };
573 
574 template<char *name, typename index_t>
Register(lua_State * L,const luaL_Reg get[],const luaL_Reg set[],const luaL_Reg metatable[],const lang_def mnemonics[])575 void L_Enum<name, index_t>::Register(lua_State *L, const luaL_Reg get[], const luaL_Reg set[], const luaL_Reg metatable[], const lang_def mnemonics[])
576 {
577 	L_Class<name, index_t>::Register(L, get, set, 0);
578 
579 	// add mnemonic accessors to get/set tables
580 	L_Class<name, index_t>::_push_get_methods_key(L);
581 	lua_gettable(L, LUA_REGISTRYINDEX);
582 	lua_pushcfunction(L, _get_mnemonic);
583 	lua_setfield(L, -2, "mnemonic");
584 	lua_pop(L, 1);
585 
586 	L_Class<name, index_t>::_push_set_methods_key(L);
587 	lua_gettable(L, LUA_REGISTRYINDEX);
588 	lua_pushcfunction(L, _set_mnemonic);
589 	lua_setfield(L, -2, "mnemonic");
590 	lua_pop(L, 1);
591 
592 	// add __eq and __tostring to metatable
593 	luaL_getmetatable(L, name);
594 
595 	lua_pushcfunction(L, _equals);
596 	lua_setfield(L, -2, "__eq");
597 
598 	lua_pushcfunction(L, _get_mnemonic);
599 	lua_setfield(L, -2, "__tostring");
600 
601 	if (metatable)
602 		luaL_setfuncs(L, metatable, 0);
603 
604 	lua_pop(L, 1);
605 
606 	// load mnemonics to mnemonic table
607 	if (mnemonics)
608 	{
609 		_push_mnemonic_key(L);
610 		lua_newtable(L);
611 
612 		const lang_def *mnemonic = mnemonics;
613 		while (mnemonic->name)
614 		{
615 			lua_pushstring(L, mnemonic->name);
616 			lua_pushnumber(L, mnemonic->value);
617 			lua_settable(L, -3);
618 
619 			lua_pushnumber(L, mnemonic->value);
620 			lua_pushstring(L, mnemonic->name);
621 			lua_settable(L, -3);
622 
623 			mnemonic++;
624 		}
625 
626 		lua_settable(L, LUA_REGISTRYINDEX);
627 	}
628 }
629 
630 template<char *name, typename index_t>
PushMnemonicTable(lua_State * L)631 void L_Enum<name, index_t>::PushMnemonicTable(lua_State *L)
632 {
633 	_push_mnemonic_key(L);
634 	lua_gettable(L, LUA_REGISTRYINDEX);
635 }
636 
637 template<char *name, typename index_t>
_lookup(lua_State * L,int index,index_t & to)638 bool L_Enum<name, index_t>::_lookup(lua_State *L, int index, index_t& to)
639 {
640 	if (L_Class<name, index_t>::Is(L, index))
641 	{
642 		to = L_Class<name, index_t>::Index(L, index);
643 		return true;
644 	}
645 	else if (lua_isnumber(L, index))
646 	{
647 		to = static_cast<index_t>(lua_tonumber(L, index));
648 		return L_Class<name, index_t>::Valid(to);
649 	}
650 	else if (lua_isstring(L, index))
651 	{
652 		// look for mnemonic
653 		PushMnemonicTable(L);
654 
655 		if (lua_istable(L, -1))
656 		{
657 			lua_pushvalue(L, index > 0 ? index : index - 1);
658 			lua_gettable(L, -2);
659 			if (lua_isnumber(L, -1))
660 			{
661 				to = static_cast<index_t>(lua_tonumber(L, -1));
662 				lua_pop(L, 2);
663 				return (L_Class<name, index_t>::Valid(to));
664 			}
665 			else
666 			{
667 				lua_pop(L, 2);
668 				return false;
669 			}
670 		}
671 		else
672 		{
673 			lua_pop(L, 1);
674 			return false;
675 		}
676 	}
677 	else
678 	{
679 		return false;
680 	}
681 }
682 
683 template<char *name, typename index_t>
ToIndex(lua_State * L,int index)684 index_t L_Enum<name, index_t>::ToIndex(lua_State *L, int index)
685 {
686 	index_t to;
687 	if (_lookup(L, index, to))
688 	{
689 		return to;
690 	}
691 	else
692 	{
693 		std::string error;
694 		if (lua_isnumber(L, index) || lua_isstring(L, index))
695 		{
696 			error = std::string(name) + ": invalid index";
697 		}
698 		else
699 		{
700 			error = std::string(name) + ": incorrect argument type";
701 		}
702 		return luaL_error(L, error.c_str());
703 	}
704 }
705 
706 template<char *name, typename index_t>
_equals(lua_State * L)707 int L_Enum<name, index_t>::_equals(lua_State *L)
708 {
709 	index_t a, b;
710 	lua_pushboolean(L, _lookup(L, 1, a) && _lookup(L, 2, b) && (a == b));
711 	return 1;
712 }
713 
714 template<char *name, typename index_t>
_get_mnemonic(lua_State * L)715 int L_Enum<name, index_t>::_get_mnemonic(lua_State *L)
716 {
717 	PushMnemonicTable(L);
718 	lua_pushnumber(L, ToIndex(L, 1));
719 	lua_gettable(L, -2);
720 	if (lua_isstring(L, -1))
721 	{
722 		lua_remove(L, -2);
723 		return 1;
724 	}
725 	else
726 	{
727 		lua_pop(L, 2);
728 		return 0;
729 	}
730 }
731 
732 template<char *name, typename index_t>
_set_mnemonic(lua_State * L)733 int L_Enum<name, index_t>::_set_mnemonic(lua_State *L)
734 {
735 	if (!lua_isstring(L, 2))
736 		return luaL_error(L, "mnemonic: incorrect argument type");
737 
738 	index_t index = ToIndex(L, 1);
739 	const char *new_mnemonic = lua_tostring(L, 2);
740 
741 	PushMnemonicTable(L);
742 
743 	// look up the old mnemonic
744 	lua_pushnumber(L, index);
745 	lua_gettable(L, -2);
746 	if (lua_isstring(L, -1))
747 	{
748 		// if it exists, remove the string key
749 		lua_pushnil(L);
750 		lua_settable(L, -3);
751 
752 	}
753 	else
754 	{
755 		lua_pop(L, 1);
756 	}
757 
758 	// update string key
759 	lua_pushstring(L, new_mnemonic);
760 	lua_pushnumber(L, index);
761 	lua_settable(L, -3);
762 
763 	// update index key
764 	lua_pushnumber(L, index);
765 	lua_pushstring(L, new_mnemonic);
766 	lua_settable(L, -3);
767 
768 	lua_pop(L, 1);
769 	return 0;
770 }
771 
772 template<char *name, class T>
773 class L_Container : public L_Class<name> {
774 public:
775 	static void Register(lua_State *L, const luaL_Reg get[] = 0, const luaL_Reg set[] = 0, const luaL_Reg metatable[] = 0);
776 	static boost::function<typename T::index_type (void)> Length;
777 	struct ConstantLength
778 	{
ConstantLengthConstantLength779 		ConstantLength(int32 length) : m_length(length) {}
operatorConstantLength780 		int32 operator() (void) { return m_length; }
781 		int32 m_length;
782 	};
783 private:
784 	static int _call(lua_State *);
785 	static int _iterator(lua_State *);
786 	static int _length(lua_State *);
787 protected:
788 	static int _get_container(lua_State *);
789 };
790 
791 template<char *name, class T>
792 boost::function<typename T::index_type (void)> L_Container<name, T>::Length = ConstantLength(1);
793 
794 template<char *name, class T>
Register(lua_State * L,const luaL_Reg get[],const luaL_Reg set[],const luaL_Reg metatable[])795 void L_Container<name, T>::Register(lua_State *L, const luaL_Reg get[], const luaL_Reg set[], const luaL_Reg metatable[])
796 {
797 	L_Class<name>::Register(L, get, set, metatable);
798 	luaL_getmetatable(L, name);
799 
800 	lua_pushcfunction(L, _get_container);
801 	lua_setfield(L, -2, "__index");
802 
803 	lua_pushcfunction(L, _call);
804 	lua_setfield(L, -2, "__call");
805 
806 	lua_pushcfunction(L, _length);
807 	lua_setfield(L, -2, "__len");
808 
809 	lua_pop(L, 1);
810 
811 	L_Class<name>::Push(L, 0);
812 	lua_setglobal(L, name);
813 }
814 
815 template<char *name, class T>
_get_container(lua_State * L)816 int L_Container<name, T>::_get_container(lua_State *L)
817 {
818 	if (lua_isnumber(L, 2))
819 	{
820 		int32 index = static_cast<int32>(lua_tonumber(L, 2));
821 		if (!T::Valid(index))
822 		{
823 			lua_pushnil(L);
824 		}
825 		else
826 		{
827 			T::Push(L, index);
828 		}
829 		return 1;
830 	}
831 
832 	return L_Class<name>::_get(L);
833 }
834 
835 template<char *name, class T>
_iterator(lua_State * L)836 int L_Container<name, T>::_iterator(lua_State *L)
837 {
838 	int32 index = static_cast<int32>(lua_tonumber(L, lua_upvalueindex(1)));
839 	while (index < Length())
840 	{
841 		if (T::Valid(index))
842 
843 		{
844 			T::Push(L, index);
845 			lua_pushnumber(L, ++index);
846 			lua_replace(L, lua_upvalueindex(1));
847 			return 1;
848 		}
849 		else
850 		{
851 			++index;
852 		}
853 	}
854 
855 	lua_pushnil(L);
856 	return 1;
857 }
858 
859 template<char *name, class T>
_call(lua_State * L)860 int L_Container<name, T>::_call(lua_State *L)
861 {
862 	lua_pushnumber(L, 0);
863 	lua_pushcclosure(L, _iterator, 1);
864 	return 1;
865 }
866 
867 template<char *name, class T>
_length(lua_State * L)868 int L_Container<name, T>::_length(lua_State *L)
869 {
870 	lua_pushnumber(L, Length());
871 	return 1;
872 }
873 
874 // enum containers will be able to look up by strings
875 template<char *name, class T>
876 class L_EnumContainer : public L_Container<name, T>
877 {
878 public:
879 	static void Register(lua_State *L, const luaL_Reg methods[] = 0, const luaL_Reg metatable[] = 0);
880 private:
881 	static int _get_enumcontainer(lua_State *);
882 };
883 
884 template <char *name, class T>
Register(lua_State * L,const luaL_Reg methods[],const luaL_Reg metatable[])885 void L_EnumContainer<name, T>::Register(lua_State *L, const luaL_Reg methods[], const luaL_Reg metatable[])
886 {
887 	L_Container<name, T>::Register(L, methods, 0, metatable);
888 
889 	luaL_getmetatable(L, name);
890 
891 	lua_pushcfunction(L, _get_enumcontainer);
892 	lua_setfield(L, -2, "__index");
893 
894 	lua_pop(L, 1);
895 }
896 
897 template <char *name, class T>
_get_enumcontainer(lua_State * L)898 int L_EnumContainer<name, T>::_get_enumcontainer(lua_State *L)
899 {
900 	if (lua_isnumber(L, 2))
901 	{
902 		int32 index = static_cast<int32>(lua_tonumber(L, 2));
903 		if (!T::Valid(index))
904 		{
905 			lua_pushnil(L);
906 			return 1;
907 		}
908 		else
909 		{
910 			T::Push(L, index);
911 			return 1;
912 		}
913 	}
914 	else if (lua_isstring(L, 2))
915 	{
916 		// try mnemonics
917 		T::PushMnemonicTable(L);
918 
919 		if (lua_istable(L, -1))
920 		{
921 			lua_pushvalue(L, 2);
922 			lua_gettable(L, -2);
923 			if (lua_isnumber(L, -1))
924 			{
925 				int32 index = static_cast<int32>(lua_tonumber(L, -1));
926 				lua_pop(L, 2);
927 				T::Push(L, index);
928 				return 1;
929 			}
930 			else
931 			{
932 				// should not happen
933 				lua_pop(L, 2);
934 			}
935 		}
936 		else
937 		{
938 			lua_pop(L, 1);
939 		}
940 
941 	}
942 
943 	// get the function from methods
944 	return L_Container<name, T>::_get_container(L);
945 }
946 
947 // object classes hold an object_t as well as numeric index_t
948 template<char *name, typename object_t, typename index_t = int16>
949 class L_ObjectClass : public L_Class<name, index_t> {
950 public:
951 	template<typename instance_t = L_ObjectClass /*or a derived class*/>
952 	static instance_t *Push(lua_State *L, object_t object);
953 
954 	static object_t ObjectAtIndex(lua_State *L, index_t index);
955 	static object_t Object(lua_State *L, int index);
956 	static void Invalidate(lua_State *L, index_t index);
957 
958 	static void Register(lua_State *L, const luaL_Reg get[] = 0, const luaL_Reg set[] = 0, const luaL_Reg metatable[] = 0) {
959 		return L_Class<name, index_t>::Register(L, get, set, metatable);
960 	}
Index(lua_State * L,int index)961 	static index_t Index(lua_State *L, int index) {
962 		return L_Class<name, index_t>::Index(L, index);
963 	}
Is(lua_State * L,int index)964 	static bool Is(lua_State *L, int index) {
965 		return L_Class<name, index_t>::Is(L, index);
966 	}
967 	static boost::function<bool (index_t)> Valid;
968 
969 	static std::map<index_t, object_t> _objects;
970 };
971 
972 template<char *name, typename object_t, typename index_t>
973 std::map<index_t, object_t> L_ObjectClass<name, object_t, index_t>::_objects;
974 
975 template<char *name, typename object_t, typename index_t>
976 struct object_valid
977 {
operatorobject_valid978 	bool operator()(index_t x) {
979 			return (L_ObjectClass<name, object_t, index_t>::_objects.find(x) !=
980 						  L_ObjectClass<name, object_t, index_t>::_objects.end());
981 	}
982 };
983 
984 template<char *name, typename object_t, typename index_t>
985 boost::function<bool (index_t)> L_ObjectClass<name, object_t, index_t>::Valid = object_valid<name, object_t, index_t>();
986 
987 
988 template<char *name, typename object_t, typename index_t>
989 template<typename instance_t>
Push(lua_State * L,object_t object)990 instance_t *L_ObjectClass<name, object_t, index_t>::Push(lua_State *L, object_t object)
991 {
992 	// find unused index in our map
993 	index_t idx = 0;
994 	while (_objects.find(++idx) != _objects.end()) { }
995 
996 	_objects[idx] = object;
997 
998 	// create an instance
999 	instance_t *t = L_Class<name, index_t>::template NewInstance<instance_t>(L, idx);
1000 
1001 	return t;
1002 }
1003 
1004 template<char *name, typename object_t, typename index_t>
ObjectAtIndex(lua_State * L,index_t index)1005 object_t L_ObjectClass<name, object_t, index_t>::ObjectAtIndex(lua_State *L, index_t index)
1006 {
1007 	if (_objects.find(index) == _objects.end())
1008 		luaL_typerror(L, index, name);
1009 
1010 	return _objects[index];
1011 }
1012 
1013 template<char *name, typename object_t, typename index_t>
Object(lua_State * L,int index)1014 object_t L_ObjectClass<name, object_t, index_t>::Object(lua_State *L, int index)
1015 {
1016 	return ObjectAtIndex(L, L_Class<name, index_t>::Index(L, index));
1017 }
1018 
1019 template<char *name, typename object_t, typename index_t>
Invalidate(lua_State * L,index_t index)1020 void L_ObjectClass<name, object_t, index_t>::Invalidate(lua_State *L, index_t index)
1021 {
1022 	// remove index from our object map
1023 	_objects.erase(index);
1024 }
1025 
1026 
1027 #endif
1028 
1029 #endif
1030