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