ae582fcbe07f566005370383335e2bbbedf72911
[chaz/yoink] / src / moof / script.hh
1
2 /*] Copyright (c) 2009-2011, Charles McGarvey [*****************************
3 **] All rights reserved.
4 *
5 * Distributable under the terms and conditions of the 2-clause BSD license;
6 * see the file COPYING for a complete text of the license.
7 *
8 *****************************************************************************/
9
10 #ifndef _MOOF_SCRIPT_HH_
11 #define _MOOF_SCRIPT_HH_
12
13 #include <cstring>
14 #include <iostream>
15 #include <map>
16 #include <stdexcept>
17 #include <string>
18 #include <sstream>
19 #include <utility>
20 #include <vector>
21
22 #include <boost/bind.hpp>
23 #include <boost/function.hpp>
24 #include <boost/shared_ptr.hpp>
25 #include <lua.hpp>
26
27
28 /**
29 * \file script.hh
30 * A thin wrapper over Lua 5.1. This is not meant as a complicated binding
31 * package between C++ and Lua. It is not meant to obscure the division
32 * between C++ and Lua but rather to clarify it and make it more manageable.
33 * It does not hide the concept of the Lua stack, but rather provides that
34 * mechanism with a certain level of abstraction while also providing a
35 * cleaner, more consistent API.
36 */
37
38 namespace moof {
39
40
41 class script;
42 typedef boost::shared_ptr<script> script_ptr;
43
44 class script
45 {
46 public:
47
48 typedef boost::function<int(script&)> function;
49 typedef int (*cfunction)(script&);
50
51 enum status
52 {
53 success = 0,
54 yielding = LUA_YIELD,
55 runtime_error = LUA_ERRRUN,
56 syntax_error = LUA_ERRSYNTAX,
57 memory_error = LUA_ERRMEM,
58 handler_error = LUA_ERRERR,
59 file_error = LUA_ERRFILE
60 };
61
62 enum pseudoindex
63 {
64 registry_index = LUA_REGISTRYINDEX,
65 environment_index = LUA_ENVIRONINDEX,
66 globals_index = LUA_GLOBALSINDEX
67 };
68
69 template <class T>
70 static int object_finalizer(script& script)
71 {
72 reinterpret_cast<T*>(script[1].get_data())->~T();
73 return 0;
74 }
75
76 /**
77 * This is the most prominent abstraction on top of the standard Lua
78 * API. A slot object represents a value on the stack. More
79 * specifically, it represents a position on the stack. The
80 * distinction is only important when objects are moved around on the
81 * stack or if the slot represents a negative index on the stack (the
82 * value of which will change as things are pushed onto and popped
83 * from the stack).
84 */
85 struct slot
86 {
87 /**
88 * You have direct access to the index of the value on the
89 * stack being represented.
90 */
91 int index;
92
93 enum type
94 {
95 none = LUA_TNONE,
96 nil = LUA_TNIL,
97 boolean = LUA_TBOOLEAN,
98 light_data = LUA_TLIGHTUSERDATA,
99 number = LUA_TNUMBER,
100 string = LUA_TSTRING,
101 table = LUA_TTABLE,
102 function = LUA_TFUNCTION,
103 data = LUA_TUSERDATA,
104 thread = LUA_TTHREAD
105 };
106
107 static std::string type_name(type type)
108 {
109 switch (type)
110 {
111 case none: return "none";
112 case nil: return "nil";
113 case boolean: return "boolean";
114 case light_data:
115 case data: return "userdata";
116 case number: return "number";
117 case string: return "string";
118 case table: return "table";
119 case function: return "function";
120 case thread: return "thread";
121 }
122 return "?";
123 }
124
125 slot(const class script& s, int i = 0) :
126 index(i),
127 script_(const_cast<class script*>(&s)) {}
128
129 // check the type of the value
130 bool is_boolean() const
131 {
132 return (bool)lua_isboolean(script_->state_, index);
133 }
134 bool is_imported_function() const
135 {
136 return (bool)lua_iscfunction(script_->state_, index);
137 }
138 bool is_function() const
139 {
140 return (bool)lua_isfunction(script_->state_, index);
141 }
142 bool is_nil() const
143 {
144 return (bool)lua_isnil(script_->state_, index);
145 }
146 bool is_none() const
147 {
148 return (bool)lua_isnone(script_->state_, index);
149 }
150 bool is_none_or_nil() const
151 {
152 return (bool)lua_isnoneornil(script_->state_, index);
153 }
154 bool is_number() const
155 {
156 return (bool)lua_isnumber(script_->state_, index);
157 }
158 bool is_string() const
159 {
160 return (bool)lua_isstring(script_->state_, index);
161 }
162 bool is_table() const
163 {
164 return (bool)lua_istable(script_->state_, index);
165 }
166 bool is_thread() const
167 {
168 return (bool)lua_isthread(script_->state_, index);
169 }
170 bool is_data() const
171 {
172 return (bool)lua_isuserdata(script_->state_, index);
173 }
174 bool is_light_data() const
175 {
176 return (bool)lua_islightuserdata(script_->state_, index);
177 }
178
179 /**
180 * Check the value and throw an error if its the wrong type.
181 */
182 const slot& require_type(type t) const
183 {
184 if (t != type()) raise_type_error(type_name(t));
185 return *this;
186 }
187
188 const slot&
189 require_boolean(const std::string& what = "boolean") const
190 {
191 if (!is_boolean()) raise_type_error(what);
192 return *this;
193 }
194 const slot&
195 require_number(const std::string& what = "number") const
196 {
197 if (!is_number()) raise_type_error(what);
198 return *this;
199 }
200 const slot&
201 require_string(const std::string& what = "string") const
202 {
203 if (!is_string()) raise_type_error(what);
204 return *this;
205 }
206 const slot&
207 require_table(const std::string& what = "table") const
208 {
209 if (!is_table()) raise_type_error(what);
210 return *this;
211 }
212 const slot&
213 require_function(const std::string& what = "function") const
214 {
215 if (!is_function()) raise_type_error(what);
216 return *this;
217 }
218 const slot&
219 require_data(const std::string& what = "userdata") const
220 {
221 if (!is_data()) raise_type_error(what);
222 return *this;
223 }
224 const slot&
225 require_nil(const std::string& what = "nil") const
226 {
227 if (!is_nil()) raise_type_error(what);
228 return *this;
229 }
230 const slot&
231 require_thread(const std::string& what = "thread") const
232 {
233 if (!is_thread()) raise_type_error(what);
234 return *this;
235 }
236
237 template <class T>
238 const slot&
239 require_object(const std::string& what = typeid(T).name()) const
240 {
241 if (!is_data()) raise_type_error(what);
242
243 slot metatable = push_metatable();
244 if (!metatable.is_table())
245 {
246 metatable.pop();
247 raise_type_error(what);
248 }
249
250 slot type = metatable.push_field("__cxxtype");
251 std::type_info* typeinfo;
252 if (!type.get(typeinfo))
253 {
254 metatable.pop();
255 raise_type_error(what);
256 }
257
258 metatable.pop();
259 if (*typeinfo != typeid(T)) raise_type_error(what);
260 return *this;
261 }
262
263 /**
264 * Get the type of the value.
265 */
266 enum type type() const
267 {
268 return (enum type)lua_type(script_->state_, index);
269 }
270
271 /**
272 * Get the name of the type of the value as a string.
273 */
274 std::string type_name() const
275 {
276 if (is_none())
277 {
278 return "none";
279 }
280 else if (is_data() && !is_light_data())
281 {
282 slot metatable = push_metatable();
283 if (!metatable.is_table())
284 {
285 metatable.pop();
286 return "userdata";
287 }
288
289 slot type = metatable.push_field("__cxxtype");
290 std::type_info* typeinfo;
291 if (!type.get(typeinfo))
292 {
293 metatable.pop();
294 return "userdata";
295 }
296
297 metatable.pop();
298 return typeinfo->name();
299 }
300 return luaL_typename(script_->state_, index);
301 }
302
303 /**
304 * Get the length of the value according to the definition
305 * given by Lua.
306 */
307 size_t size() const
308 {
309 return lua_objlen(script_->state_, index);
310 }
311 size_t length() const
312 {
313 return size();
314 }
315
316 int positive_index() const
317 {
318 if (index < 0)
319 return index + lua_gettop(script_->state_) + 1;
320 else
321 return index;
322 }
323
324 /**
325 * Get a pointer value (for userdata, tables, threads, and
326 * functions).
327 */
328 const void* id() const
329 {
330 return lua_topointer(script_->state_, index);
331 }
332
333 bool is_identical(const slot& rhs) const
334 {
335 return script_ == rhs.script_ && index == rhs.index;
336 }
337
338 operator bool () const
339 {
340 return !is_none();
341 }
342
343 bool operator == (const slot& rhs) const
344 {
345 return (bool)lua_equal(script_->state_,
346 index, rhs.index);
347 }
348
349 bool operator < (const slot& rhs) const
350 {
351 return (bool)lua_lessthan(script_->state_,
352 index, rhs.index);
353 }
354
355 /**
356 * Convert the underlying value to a C++ type.
357 */
358 template <class T>
359 bool get(T& value) const
360 {
361 if (is_number())
362 {
363 value = (T)lua_tointeger(script_->state_, index);
364 return true;
365 }
366 return false;
367 }
368
369 bool get(float& value) const
370 {
371 if (is_number())
372 {
373 value = (float)lua_tonumber(script_->state_, index);
374 return true;
375 }
376 return false;
377 }
378 bool get(double& value) const
379 {
380 if (is_number())
381 {
382 value = (double)lua_tonumber(script_->state_, index);
383 return true;
384 }
385 return false;
386 }
387
388 bool get(bool& value) const
389 {
390 if (is_boolean())
391 {
392 value = (bool)lua_toboolean(script_->state_, index);
393 return true;
394 }
395 return false;
396 }
397
398 bool get(const char*& value, size_t& size) const
399 {
400 if (is_string())
401 {
402 value = lua_tolstring(script_->state_, index, &size);
403 return true;
404 }
405 return false;
406 }
407
408 bool get(std::string& value) const
409 {
410 const char* str;
411 size_t size;
412 if (get(str, size))
413 {
414 value.assign(str, size);
415 return true;
416 }
417 return false;
418 }
419
420 template <class T>
421 bool get(T*& value) const
422 {
423 if (is_data())
424 {
425 value = reinterpret_cast<T*>(lua_touserdata(script_->state_, index));
426 return true;
427 }
428 return false;
429 }
430 void* get_data() const
431 {
432 return lua_touserdata(script_->state_, index);
433 }
434
435 template <class T>
436 bool get(std::vector<T>& array) const
437 {
438 if (!is_table()) return false;
439
440 array.clear();
441
442 slot value = (*script_)[-1];
443 int realIndex = positive_index();
444
445 bool done = false;
446 for (int i = 1; !done; ++i)
447 {
448 lua_rawgeti(script_->state_, realIndex, i);
449
450 T v;
451 if (value.get(v))
452 array.push_back(v);
453 else
454 done = true;
455
456 script_->pop();
457 }
458
459 return true;
460 }
461
462 template <class T>
463 bool get(std::map<std::string,T>& dictionary) const
464 {
465 if (!is_table()) return false;
466
467 dictionary.clear();
468
469 slot key = (*script_)[-2];
470 slot value = (*script_)[-1];
471 int realIndex = positive_index();
472
473 script_->push_nil();
474 while (lua_next(script_->state_, realIndex) != 0)
475 {
476 std::string k;
477 if (!key.is_number() && key.get(k))
478 {
479 T v;
480 if (value.get(v)) dictionary[k] = v;
481 }
482 script_->pop();
483 }
484 script_->pop();
485
486 return true;
487 }
488
489 /**
490 * Get the value of a field from the table.
491 */
492 template <class T, class V>
493 bool get(T& value, const V& field) const
494 {
495 bool ret = push_field(field).get(value);
496 script_->pop();
497 return ret;
498 }
499
500 template <class T, class V>
501 void set_field(const T& field, const V& value)
502 {
503 script_->push(field);
504 script_->push(value);
505 set_field();
506 }
507
508 void set_field()
509 {
510 lua_settable(script_->state_, index);
511 }
512
513 void set_field(const std::string& field)
514 {
515 set_field(field.c_str());
516 }
517 void set_field(const char* field)
518 {
519 lua_setfield(script_->state_, index, field);
520 }
521
522 template <class T>
523 void set_field(const std::string& field, const T& value)
524 {
525 set_field(field.c_str(), value);
526 }
527 template <class T>
528 void set_field(const char* field, const T& value)
529 {
530 script_->push(value);
531 set_field(field);
532 }
533
534 /**
535 * Set the top value to be the metatable of this value.
536 */
537 void set_metatable()
538 {
539 lua_setmetatable(script_->state_, index);
540 }
541
542 /**
543 * This set method, as opposed to the others, sets the value of the
544 * actual slot. The others set table values.
545 */
546 template <class T>
547 void set(T value)
548 {
549 script_->push(value);
550 set();
551 }
552
553 /**
554 * Replace this value with the value at the top of the stack.
555 */
556 void set()
557 {
558 lua_replace(script_->state_, index);
559 }
560
561 void set_nil()
562 {
563 script_->push_nil();
564 set();
565 }
566
567 void remove()
568 {
569 lua_remove(script_->state_, index);
570 }
571
572 /**
573 * Remove this value and everything above it.
574 */
575 void pop()
576 {
577 if
578 (index < 0) script_->pop(-index);
579 else
580 script_->pop(script_->stack_size() -
581 index + 1);
582 }
583
584 /**
585 * Inserts the top-most value on the stack at position index,
586 * shifting other values as needed.
587 */
588 void insert_top_here()
589 {
590 lua_insert(script_->state_, index);
591 }
592
593 /**
594 * Copy the value and push the copy to the stack.
595 */
596 slot push_copy() const
597 {
598 lua_pushvalue(script_->state_, index);
599 return script_->top();
600 }
601
602 slot push_metatable() const
603 {
604 lua_getmetatable(script_->state_, index);
605 return script_->top();
606 }
607
608 slot push_environment() const
609 {
610 lua_getfenv(script_->state_, index);
611 return script_->top();
612 }
613
614 slot push_field() const
615 {
616 lua_gettable(script_->state_, index);
617 return script_->top();
618 }
619
620 template <class T>
621 slot push_field(T index) const
622 {
623 script_->push(index);
624 return push_field();
625 }
626
627 slot push_field(const std::string& name) const
628 {
629 return push_field(name.c_str());
630 }
631 slot push_field(const char* name) const
632 {
633 lua_getfield(script_->state_, index, name);
634 return script_->top();
635 }
636
637 class script& script()
638 {
639 return *script_;
640 }
641
642 const class script& script() const
643 {
644 return *script_;
645 }
646
647 /**
648 * Throw an exception with a message formatted to communicate
649 * a type mismatch with the argument represented by this slot.
650 */
651 int raise_type_error(const std::string& expected) const
652 {
653 lua_Debug ar;
654 lua_getstack(script_->state_, 0, &ar);
655 lua_getinfo(script_->state_, "n", &ar);
656 const char* func = ar.name ? ar.name : "unknown function";
657
658 std::ostringstream stream;
659 stream << "bad argument " << index << " to '" << func
660 << "' (" << expected << " expected, got "
661 << type_name() << ")";
662
663 throw std::invalid_argument(stream.str());
664 return 0;
665 }
666
667 /**
668 * Throw a generic error concerning this particular slot.
669 */
670 int raise(const std::string& message) const
671 {
672 lua_Debug ar;
673 lua_getstack(script_->state_, 0, &ar);
674 lua_getinfo(script_->state_, "n", &ar);
675 const char* func = ar.name ? ar.name : "unknown function";
676
677 std::ostringstream stream;
678 stream << "bad argument " << index << " to '" << func
679 << "' (" << message << ")";
680
681 throw std::invalid_argument(stream.str());
682 return 0;
683 }
684
685 private:
686
687 mutable class script* script_;
688 };
689
690 script() :
691 state_(0)
692 {
693 reset();
694 }
695
696 ~script()
697 {
698 destroy();
699 }
700
701 static script_ptr alloc()
702 {
703 return script_ptr(new script);
704 }
705
706 void reset()
707 {
708 if (state_) destroy();
709 state_ = luaL_newstate();
710 }
711
712 void import_standard_libraries()
713 {
714 luaL_openlibs(state_);
715 }
716
717 void import_safe_standard_libraries()
718 {
719 import_base_library();
720 import_string_library();
721 import_table_library();
722 import_math_library();
723 import_os_library();
724 import_debug_library();
725
726 slot g = globals();
727
728 push_nil(); g.set_field("dofile");
729 push_nil(); g.set_field("loadfile");
730 push_nil(); g.set_field("require");
731 push_nil(); g.set_field("io");
732 push_nil(); g.set_field("package");
733 slot os = g.push_field("os");
734 push_nil(); os.set_field("execute");
735 push_nil(); os.set_field("exit");
736 push_nil(); os.set_field("getenv");
737 push_nil(); os.set_field("remove");
738 push_nil(); os.set_field("rename");
739 push_nil(); os.set_field("tmpname");
740 pop();
741 }
742
743 void import_base_library()
744 {
745 push(luaopen_base);
746 push(LUA_COLIBNAME);
747 call(1, 0);
748 }
749 void import_package_library()
750 {
751 push(luaopen_package);
752 push(LUA_LOADLIBNAME);
753 call(1, 0);
754 }
755 void import_string_library()
756 {
757 push(luaopen_string);
758 push(LUA_STRLIBNAME);
759 call(1, 0);
760 }
761 void import_table_library()
762 {
763 push(luaopen_table);
764 push(LUA_TABLIBNAME);
765 call(1, 0);
766 }
767 void import_math_library()
768 {
769 push(luaopen_math);
770 push(LUA_MATHLIBNAME);
771 call(1, 0);
772 }
773 void import_io_library()
774 {
775 push(luaopen_io);
776 push(LUA_IOLIBNAME);
777 call(1, 0);
778 }
779 void import_os_library()
780 {
781 push(luaopen_os);
782 push(LUA_OSLIBNAME);
783 call(1, 0);
784 }
785 void import_debug_library()
786 {
787 push(luaopen_debug);
788 push(LUA_DBLIBNAME);
789 call(1, 0);
790 }
791
792 void
793 import_function(const std::string& name, const function& function)
794 {
795 push(function);
796 lua_setglobal(state_, name.c_str());
797 }
798
799 status do_string(const std::string& commands)
800 {
801 return status(luaL_dostring(state_, commands.c_str()));
802 }
803
804 status do_file(const std::string& file)
805 {
806 return status(luaL_dofile(state_, file.c_str()));
807 }
808
809 /*
810 * Thread-handling methods.
811 */
812
813 script push_new_thread()
814 {
815 return script(state_);
816 }
817
818 slot push_thread()
819 {
820 lua_pushthread(state_);
821 return top();
822 }
823
824 status resume(int nargs)
825 {
826 return status(lua_resume(state_, nargs));
827 }
828
829 status thread_status() const
830 {
831 return status(lua_status(state_));
832 }
833
834 int yield(int results)
835 {
836 return lua_yield(state_, results);
837 }
838
839 bool is_main_thread() const
840 {
841 bool is_main = lua_pushthread(state_);
842 lua_pop(state_, 1);
843 return is_main;
844 }
845
846 /**
847 * Throw an error with the value at the top of the stack. If this is
848 * called from an imported function, the error will be caught and
849 * returned on the stack when the execution aborts.
850 */
851 int raise()
852 {
853 throw std::runtime_error("");
854 return 0;
855 }
856
857 /**
858 * Throw an error with a given message describing the problem. If this
859 * is called from an imported function, the error will be caught and
860 * returned on the stack when the execution aborts.
861 */
862 int raise(const std::string& message)
863 {
864 throw std::runtime_error(message);
865 return 0;
866 }
867
868 /*
869 * Get significant values.
870 */
871
872 slot globals() const
873 {
874 return slot(*this, globals_index);
875 }
876 slot registry() const
877 {
878 return slot(*this, registry_index);
879 }
880 slot environment() const
881 {
882 return slot(*this, environment_index);
883 }
884 slot top() const
885 {
886 return slot(*this, stack_size());
887 }
888
889 /**
890 * Set the size of the stack.
891 * \param size The stack size.
892 */
893 void stack_size(int size)
894 {
895 lua_settop(state_, size);
896 }
897
898 /**
899 * Get the size of the stack; this is also the index of the top-most
900 * value.
901 * \return The stack size.
902 */
903 int stack_size() const
904 {
905 return lua_gettop(state_);
906 }
907
908 /**
909 * Clear the stack, setting its size to zero.
910 */
911 void clear_stack()
912 {
913 stack_size(0);
914 }
915
916 /**
917 * Makes sure there is at least extra more places on the stack.
918 * Returns false if space couldn't be created. Just like with the
919 * regular Lua API, you are responsible to make sure the stack is big
920 * enough to hold whatever you want to push on it. This is usually
921 * only an issue if you're pushing stuff in a loop.
922 */
923 bool check_stack(int extra)
924 {
925 return (bool)lua_checkstack(state_, extra);
926 }
927
928 /**
929 * Concatenates the top-most n slots on the stack.
930 */
931 void concatenate(int n = 2)
932 {
933 lua_concat(state_, n);
934 }
935
936 /*
937 * Push some values onto the stack.
938 */
939
940 slot push(char value)
941 {
942 lua_pushinteger(state_, lua_Integer(value));
943 return top();
944 }
945 slot push(unsigned char value)
946 {
947 lua_pushinteger(state_, lua_Integer(value));
948 return top();
949 }
950 slot push(short value)
951 {
952 lua_pushinteger(state_, lua_Integer(value));
953 return top();
954 }
955 slot push(unsigned short value)
956 {
957 lua_pushinteger(state_, lua_Integer(value));
958 return top();
959 }
960 slot push(int value)
961 {
962 lua_pushinteger(state_, lua_Integer(value));
963 return top();
964 }
965 slot push(unsigned int value)
966 {
967 lua_pushinteger(state_, lua_Integer(value));
968 return top();
969 }
970 slot push(long value)
971 {
972 lua_pushinteger(state_, lua_Integer(value));
973 return top();
974 }
975 slot push(unsigned long value)
976 {
977 lua_pushinteger(state_, lua_Integer(value));
978 return top();
979 }
980 slot push(long long value)
981 {
982 lua_pushinteger(state_, lua_Integer(value));
983 return top();
984 }
985 slot push(unsigned long long value)
986 {
987 lua_pushinteger(state_, lua_Integer(value));
988 return top();
989 }
990
991 slot push(bool value)
992 {
993 lua_pushboolean(state_, int(value));
994 return top();
995 }
996
997 slot push(float value)
998 {
999 lua_pushnumber(state_, lua_Number(value));
1000 return top();
1001 }
1002 slot push(double value)
1003 {
1004 lua_pushnumber(state_, lua_Number(value));
1005 return top();
1006 }
1007
1008 slot push(const std::string& value)
1009 {
1010 lua_pushlstring(state_, value.c_str(), value.length());
1011 return top();
1012 }
1013 slot push(const char* value)
1014 {
1015 lua_pushstring(state_, value);
1016 return top();
1017 }
1018 slot push(const char* value, size_t length)
1019 {
1020 lua_pushlstring(state_, value, length);
1021 return top();
1022 }
1023
1024 slot push(const function& function)
1025 {
1026 push<script::function>(function);
1027 push_pointer(this);
1028 push(call_functor, 2);
1029 return top();
1030 }
1031 slot push(cfunction function)
1032 {
1033 push_pointer(function);
1034 push_pointer(this);
1035 push(call_function, 2);
1036 return top();
1037 }
1038
1039 template <class T>
1040 slot push(const T& object)
1041 {
1042 void* storage;
1043 slot copy = push_data(storage, sizeof(T));
1044 new(storage) T(object);
1045
1046 push_class_metatable<T>();
1047 copy.set_metatable();
1048 return copy;
1049 }
1050
1051 template <class T>
1052 slot push_class(const function& ctor)
1053 {
1054 slot metatable = push_class_metatable<T>();
1055
1056 slot constructor = push_table();
1057 push(ctor);
1058 constructor.set_field("__call");
1059 metatable.set_metatable();
1060
1061 return metatable;
1062 }
1063 template <class T>
1064 slot push_class(cfunction ctor)
1065 {
1066 slot metatable = push_class_metatable<T>();
1067
1068 slot constructor = push_table();
1069 push(ctor);
1070 constructor.set_field("__call");
1071 metatable.set_metatable();
1072
1073 return metatable;
1074 }
1075
1076 slot push_nil()
1077 {
1078 lua_pushnil(state_);
1079 return top();
1080 }
1081
1082 slot push_from_thread(script& thread, int n)
1083 {
1084 lua_xmove(thread.state_, state_, n);
1085 return top();
1086 }
1087
1088 slot push_code(const std::string& file, status& result)
1089 {
1090 result = status(luaL_loadfile(state_, file.c_str()));
1091 return top();
1092 }
1093
1094 slot push_code(const std::string& name,
1095 const char* buffer, size_t size, status& result)
1096 {
1097 result = status(luaL_loadbuffer(state_,
1098 buffer, size, name.c_str()));
1099 return top();
1100 }
1101
1102 slot push_data(void*& data, size_t size)
1103 {
1104 data = lua_newuserdata(state_, size);
1105 return top();
1106 }
1107
1108 template <class T>
1109 slot push_pointer(const T* ptr)
1110 {
1111 lua_pushlightuserdata(state_,
1112 const_cast<void*>((const void*)ptr));
1113 return top();
1114 }
1115 slot push_pointer(cfunction function)
1116 {
1117 return push_pointer((void*)function);
1118 }
1119
1120 slot push_table(const std::string& name, int narr = 0, int nrec = 0)
1121 {
1122 if (name.empty()) return globals().push_field("_G");
1123
1124 slot table = globals().push_field(name);
1125 if (table.is_table()) return table;
1126
1127 pop();
1128 push_table(narr, nrec);
1129 globals().set_field(name);
1130
1131 return globals().push_field(name);
1132 }
1133 slot push_table(int narr = 0, int nrec = 0)
1134 {
1135 lua_createtable(state_, narr, nrec);
1136 return top();
1137 }
1138
1139 slot push_metatable(const std::string& type, bool& is_new)
1140 {
1141 is_new = luaL_newmetatable(state_, type.c_str());
1142 return top();
1143 }
1144 slot push_metatable(const std::string& type)
1145 {
1146 luaL_newmetatable(state_, type.c_str());
1147 return top();
1148 }
1149
1150 template <class T>
1151 slot push_type()
1152 {
1153 return push_pointer(&typeid(T));
1154 }
1155
1156 /**
1157 * Call a function on the stack. The correct procedure is to push a
1158 * function onto the stack followed by nargs arguments. This method
1159 * will pop them off upon return, leaving up to nresults return values
1160 * (default is any number of return values, depending on the callee).
1161 */
1162 status call(int nargs = 0, int nresults = LUA_MULTRET)
1163 {
1164 return status(lua_pcall(state_, nargs, nresults, 0));
1165 }
1166
1167 /**
1168 * Pops n values from the top of the stack.
1169 */
1170 void pop(int n = 1)
1171 {
1172 lua_pop(state_, n);
1173 }
1174
1175 /**
1176 * Index into the stack to get a slot.
1177 */
1178 slot operator [] (int index) const
1179 {
1180 return slot(*this, index);
1181 }
1182
1183 /*
1184 * Control over the garbage collection process.
1185 */
1186
1187 void collect_garbage()
1188 {
1189 lua_gc(state_, LUA_GCCOLLECT, 0);
1190 }
1191 void collect_garbage(int step)
1192 {
1193 lua_gc(state_, LUA_GCSTEP, step);
1194 }
1195
1196 void disable_garbage_collector()
1197 {
1198 lua_gc(state_, LUA_GCSTOP, 0);
1199 }
1200 void enable_garbage_collector()
1201 {
1202 lua_gc(state_, LUA_GCRESTART, 0);
1203 }
1204
1205 float memory_used() const
1206 {
1207 // in kilobytes
1208 return lua_gc(state_, LUA_GCCOUNT, 0) +
1209 lua_gc(state_, LUA_GCCOUNTB, 0) / 1024.0f;
1210 }
1211
1212 void tune_garbage_collector(int pause, int step = 200)
1213 {
1214 lua_gc(state_, LUA_GCSETPAUSE, pause);
1215 lua_gc(state_, LUA_GCSETSTEPMUL, step);
1216 }
1217
1218 private:
1219
1220 script(lua_State* state) :
1221 state_(lua_newthread(state)) {}
1222
1223 slot push(lua_CFunction function, int upvalues = 0)
1224 {
1225 lua_pushcclosure(state_, function, upvalues);
1226 return top();
1227 }
1228
1229 template <class T>
1230 slot push_class_metatable()
1231 {
1232 bool is_new;
1233 slot metatable = push_metatable(typeid(T).name(), is_new);
1234 if (is_new)
1235 {
1236 metatable.push_copy(); // class behavior
1237 metatable.set_field("__index");
1238 push_type<T>();
1239 metatable.set_field("__cxxtype"); // type_info
1240 push(object_finalizer_<T>);
1241 metatable.set_field("__gc"); // finalizer
1242 //push(object_tostring_<T>);
1243 //metatable.set_field("__tostring"); // tostring
1244 }
1245 return metatable;
1246 }
1247
1248 template <class T>
1249 static int object_tostring_(lua_State* state)
1250 {
1251 std::ostringstream stream;
1252 stream << *reinterpret_cast<T*>(lua_touserdata(state, 1));
1253 lua_pushlstring(state,
1254 stream.str().c_str(), stream.str().length());
1255 return 1;
1256 }
1257
1258 template <class T>
1259 static int object_finalizer_(lua_State* state)
1260 {
1261 reinterpret_cast<T*>(lua_touserdata(state, 1))->~T();
1262 return 0;
1263 }
1264
1265 static int call_functor(lua_State* state)
1266 {
1267 function* function = (script::function*)lua_touserdata(state,
1268 lua_upvalueindex(1));
1269
1270 script* script = (moof::script*)lua_touserdata(state,
1271 lua_upvalueindex(2));
1272
1273 try
1274 {
1275 return (*function)(*script);
1276 }
1277 catch (const std::exception& e)
1278 {
1279 if (0 < std::strlen(e.what()))
1280 {
1281 luaL_where(state, 1);
1282 lua_pushstring(state, e.what());
1283 lua_concat(state, 2);
1284 }
1285 return lua_error(state);
1286 }
1287 catch (const char* e)
1288 {
1289 luaL_where(state, 1);
1290 lua_pushstring(state, e);
1291 lua_concat(state, 2);
1292 return lua_error(state);
1293 }
1294 catch (...)
1295 {
1296 return lua_error(state);
1297 }
1298 }
1299
1300 static int call_function(lua_State* state)
1301 {
1302 cfunction function = (cfunction)lua_touserdata(state,
1303 lua_upvalueindex(1));
1304
1305 script* script = (moof::script*)lua_touserdata(state,
1306 lua_upvalueindex(2));
1307
1308 try
1309 {
1310 return function(*script);
1311 }
1312 catch (const std::exception& e)
1313 {
1314 if (0 < std::strlen(e.what()))
1315 {
1316 luaL_where(state, 1);
1317 lua_pushstring(state, e.what());
1318 lua_concat(state, 2);
1319 }
1320 return lua_error(state);
1321 }
1322 catch (const char* e)
1323 {
1324 luaL_where(state, 1);
1325 lua_pushstring(state, e);
1326 lua_concat(state, 2);
1327 return lua_error(state);
1328 }
1329 catch (...)
1330 {
1331 return lua_error(state);
1332 }
1333 }
1334
1335 void destroy()
1336 {
1337 if (is_main_thread()) lua_close(state_);
1338 }
1339
1340 lua_State* state_;
1341 };
1342
1343 using namespace std::rel_ops;
1344
1345 /**
1346 * Output a script value to a stream.
1347 */
1348 inline std::ostream&
1349 operator << (std::ostream& stream, const script::slot& slot)
1350 {
1351 std::string str;
1352 bool boolean;
1353
1354 if (slot.get(str))
1355 {
1356 stream << str;
1357 }
1358 else if (slot.get(boolean))
1359 {
1360 if (boolean) stream << "true";
1361 else stream << "false";
1362 }
1363 else if (slot.is_nil())
1364 {
1365 stream << "nil";
1366 }
1367 else
1368 {
1369 stream << slot.type_name() << " (" << slot.id() << ")";
1370 }
1371
1372 return stream;
1373 }
1374
1375
1376 } // namespace moof
1377
1378 #endif // _MOOF_SCRIPT_HH_
1379
This page took 0.087479 seconds and 3 git commands to generate.