cleaned up script wrapper and bindings
[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 #define IS_TYPE(T, N) \
131 bool is_##N() const \
132 { \
133 return (bool)lua_is##T(script_->state_, index); \
134 }//
135 IS_TYPE(boolean, boolean);
136 IS_TYPE(cfunction, imported_function);
137 IS_TYPE(function, function);
138 IS_TYPE(lightuserdata, light_data);
139 IS_TYPE(nil, nil);
140 IS_TYPE(none, none);
141 IS_TYPE(noneornil, none_or_nil);
142 IS_TYPE(number, number);
143 IS_TYPE(string, string);
144 IS_TYPE(table, table);
145 IS_TYPE(thread, thread);
146 IS_TYPE(userdata, data);
147 #undef IS_TYPE
148
149 /**
150 * Check the value and throw an error if its the wrong type.
151 */
152 const slot& require_type(type t) const
153 {
154 if (t != type()) raise_type_error(type_name(t));
155 return *this;
156 }
157
158 #define REQUIRE_TYPE(T) \
159 const slot& require_##T(const std::string& what = #T) const \
160 { \
161 if (!is_##T()) raise_type_error(what); \
162 return *this; \
163 }//
164 REQUIRE_TYPE(boolean);
165 REQUIRE_TYPE(number);
166 REQUIRE_TYPE(string);
167 REQUIRE_TYPE(table);
168 REQUIRE_TYPE(function);
169 REQUIRE_TYPE(data);
170 REQUIRE_TYPE(nil);
171 REQUIRE_TYPE(thread);
172 #undef REQUIRE_TYPE
173
174 template <class T>
175 const slot&
176 require_object(const std::string& what = typeid(T).name()) const
177 {
178 if (!is_data()) raise_type_error(what);
179
180 slot metatable = push_metatable();
181 if (!metatable.is_table())
182 {
183 metatable.pop();
184 raise_type_error(what);
185 }
186
187 slot type = metatable.push_field("__cxxtype");
188 std::type_info* typeinfo;
189 if (!type.get(typeinfo))
190 {
191 metatable.pop();
192 raise_type_error(what);
193 }
194
195 metatable.pop();
196 if (*typeinfo != typeid(T)) raise_type_error(what);
197 return *this;
198 }
199
200 /**
201 * Get the type of the value.
202 */
203 enum type type() const
204 {
205 return (enum type)lua_type(script_->state_, index);
206 }
207
208 /**
209 * Get the name of the type of the value as a string.
210 */
211 std::string type_name() const
212 {
213 if (is_none())
214 {
215 return "none";
216 }
217 else if (is_data() && !is_light_data())
218 {
219 slot metatable = push_metatable();
220 if (!metatable.is_table())
221 {
222 metatable.pop();
223 return "userdata";
224 }
225
226 slot type = metatable.push_field("__cxxtype");
227 std::type_info* typeinfo;
228 if (!type.get(typeinfo))
229 {
230 metatable.pop();
231 return "userdata";
232 }
233
234 metatable.pop();
235 return typeinfo->name();
236 }
237 return luaL_typename(script_->state_, index);
238 }
239
240 /**
241 * Get the length of the value according to the definition
242 * given by Lua.
243 */
244 size_t size() const
245 {
246 return lua_objlen(script_->state_, index);
247 }
248 size_t length() const
249 {
250 return size();
251 }
252
253 int positive_index() const
254 {
255 if (index < 0)
256 return index + lua_gettop(script_->state_) + 1;
257 else
258 return index;
259 }
260
261 /**
262 * Get a pointer value (for userdata, tables, threads, and
263 * functions).
264 */
265 const void* id() const
266 {
267 return lua_topointer(script_->state_, index);
268 }
269
270 bool is_identical(const slot& rhs) const
271 {
272 return script_ == rhs.script_ && index == rhs.index;
273 }
274
275 operator bool () const
276 {
277 return !is_none();
278 }
279
280 bool operator == (const slot& rhs) const
281 {
282 return (bool)lua_equal(script_->state_,
283 index, rhs.index);
284 }
285
286 bool operator < (const slot& rhs) const
287 {
288 return (bool)lua_lessthan(script_->state_,
289 index, rhs.index);
290 }
291
292 /**
293 * Convert the underlying value to a C++ type.
294 */
295
296 #define GET_INT_OF_TYPE(T) \
297 bool get(T& value) const \
298 { \
299 if (is_number()) \
300 { \
301 value = static_cast<T>(lua_tointeger(script_->state_, index)); \
302 return true; \
303 } \
304 return false; \
305 }//
306 GET_INT_OF_TYPE(char);
307 GET_INT_OF_TYPE(unsigned char);
308 GET_INT_OF_TYPE(short);
309 GET_INT_OF_TYPE(unsigned short);
310 GET_INT_OF_TYPE(int);
311 GET_INT_OF_TYPE(unsigned int);
312 GET_INT_OF_TYPE(long);
313 GET_INT_OF_TYPE(unsigned long);
314 GET_INT_OF_TYPE(long long);
315 GET_INT_OF_TYPE(unsigned long long);
316 #undef GET_INT_OF_TYPE
317
318 bool get(float& value) const
319 {
320 if (is_number())
321 {
322 value = (float)lua_tonumber(script_->state_, index);
323 return true;
324 }
325 return false;
326 }
327 bool get(double& value) const
328 {
329 if (is_number())
330 {
331 value = (double)lua_tonumber(script_->state_, index);
332 return true;
333 }
334 return false;
335 }
336
337 bool get(bool& value) const
338 {
339 if (is_boolean())
340 {
341 value = (bool)lua_toboolean(script_->state_, index);
342 return true;
343 }
344 return false;
345 }
346
347 bool get(const char*& value, size_t& size) const
348 {
349 if (is_string())
350 {
351 value = lua_tolstring(script_->state_, index, &size);
352 return true;
353 }
354 return false;
355 }
356 bool get(std::string& value) const
357 {
358 const char* str;
359 size_t size;
360 if (get(str, size))
361 {
362 value.assign(str, size);
363 return true;
364 }
365 return false;
366 }
367
368 template <class T>
369 bool get(T*& value) const
370 {
371 if (is_data())
372 {
373 value = reinterpret_cast<T*>(lua_touserdata(script_->state_, index));
374 return true;
375 }
376 return false;
377 }
378 template <class T>
379 bool get(T& value) const
380 {
381 if (is_data())
382 {
383 value = *reinterpret_cast<T*>(lua_touserdata(script_->state_, index));
384 return true;
385 }
386 return false;
387 }
388 void* get_data() const
389 {
390 return lua_touserdata(script_->state_, index);
391 }
392
393 template <class T>
394 bool get(std::vector<T>& array) const
395 {
396 if (!is_table()) return false;
397
398 array.clear();
399
400 slot value = (*script_)[-1];
401 int realIndex = positive_index();
402
403 bool done = false;
404 for (int i = 1; !done; ++i)
405 {
406 lua_rawgeti(script_->state_, realIndex, i);
407
408 T v;
409 if (value.get(v))
410 array.push_back(v);
411 else
412 done = true;
413
414 script_->pop();
415 }
416
417 return true;
418 }
419
420 template <class T>
421 bool get(std::map<std::string,T>& dictionary) const
422 {
423 if (!is_table()) return false;
424
425 dictionary.clear();
426
427 slot key = (*script_)[-2];
428 slot value = (*script_)[-1];
429 int realIndex = positive_index();
430
431 script_->push_nil();
432 while (lua_next(script_->state_, realIndex) != 0)
433 {
434 std::string k;
435 if (!key.is_number() && key.get(k))
436 {
437 T v;
438 if (value.get(v)) dictionary[k] = v;
439 }
440 script_->pop();
441 }
442 script_->pop();
443
444 return true;
445 }
446
447 /**
448 * Get the value of a field from the table.
449 */
450 template <class T, class V>
451 bool get(T& value, const V& field) const
452 {
453 bool ret = push_field(field).get(value);
454 script_->pop();
455 return ret;
456 }
457
458 template <class T, class V>
459 void set_field(const T& field, const V& value)
460 {
461 script_->push(field);
462 script_->push(value);
463 set_field();
464 }
465
466 void set_field()
467 {
468 lua_settable(script_->state_, index);
469 }
470
471 void set_field(const std::string& field)
472 {
473 set_field(field.c_str());
474 }
475 void set_field(const char* field)
476 {
477 lua_setfield(script_->state_, index, field);
478 }
479
480 template <class T>
481 void set_field(const std::string& field, const T& value)
482 {
483 set_field(field.c_str(), value);
484 }
485 template <class T>
486 void set_field(const char* field, const T& value)
487 {
488 script_->push(value);
489 set_field(field);
490 }
491
492 /**
493 * Set the top value to be the metatable of this value.
494 */
495 void set_metatable()
496 {
497 lua_setmetatable(script_->state_, index);
498 }
499
500 /**
501 * This set method, as opposed to the others, sets the value of the
502 * actual slot. The others set table values.
503 */
504 template <class T>
505 void set(T value)
506 {
507 script_->push(value);
508 set();
509 }
510
511 /**
512 * Replace this value with the value at the top of the stack.
513 */
514 void set()
515 {
516 lua_replace(script_->state_, index);
517 }
518
519 void set_nil()
520 {
521 script_->push_nil();
522 set();
523 }
524
525 void remove()
526 {
527 lua_remove(script_->state_, index);
528 }
529
530 /**
531 * Remove this value and everything above it.
532 */
533 void pop()
534 {
535 if (index < 0) script_->pop(-index);
536 else script_->pop(script_->stack_size() - index + 1);
537 }
538
539 /**
540 * Inserts the top-most value on the stack at position index,
541 * shifting other values as needed.
542 */
543 void insert_top_here()
544 {
545 lua_insert(script_->state_, index);
546 }
547
548 /**
549 * Copy the value and push the copy to the stack.
550 */
551 slot push_copy() const
552 {
553 lua_pushvalue(script_->state_, index);
554 return script_->top();
555 }
556
557 slot push_metatable() const
558 {
559 lua_getmetatable(script_->state_, index);
560 return script_->top();
561 }
562
563 slot push_environment() const
564 {
565 lua_getfenv(script_->state_, index);
566 return script_->top();
567 }
568
569 slot push_field() const
570 {
571 lua_gettable(script_->state_, index);
572 return script_->top();
573 }
574
575 template <class T>
576 slot push_field(T index) const
577 {
578 script_->push(index);
579 return push_field();
580 }
581
582 slot push_field(const std::string& name) const
583 {
584 return push_field(name.c_str());
585 }
586 slot push_field(const char* name) const
587 {
588 lua_getfield(script_->state_, index, name);
589 return script_->top();
590 }
591
592 class script& script()
593 {
594 return *script_;
595 }
596
597 const class script& script() const
598 {
599 return *script_;
600 }
601
602 /**
603 * Throw an exception with a message formatted to communicate
604 * a type mismatch with the argument represented by this slot.
605 */
606 int raise_type_error(const std::string& expected) const
607 {
608 lua_Debug ar;
609 lua_getstack(script_->state_, 0, &ar);
610 lua_getinfo(script_->state_, "n", &ar);
611 const char* func = ar.name ? ar.name : "unknown function";
612
613 std::ostringstream stream;
614 stream << "bad argument " << index << " to '" << func
615 << "' (" << expected << " expected, got "
616 << type_name() << ")";
617
618 throw std::invalid_argument(stream.str());
619 return 0;
620 }
621
622 /**
623 * Throw a generic error concerning this particular slot.
624 */
625 int raise(const std::string& message) const
626 {
627 lua_Debug ar;
628 lua_getstack(script_->state_, 0, &ar);
629 lua_getinfo(script_->state_, "n", &ar);
630 const char* func = ar.name ? ar.name : "unknown function";
631
632 std::ostringstream stream;
633 stream << "bad argument " << index << " to '" << func
634 << "' (" << message << ")";
635
636 throw std::invalid_argument(stream.str());
637 return 0;
638 }
639
640 private:
641
642 mutable class script* script_;
643 };
644
645
646 script() :
647 state_(0)
648 {
649 reset();
650 }
651
652 ~script()
653 {
654 destroy();
655 }
656
657 static script_ptr alloc()
658 {
659 return script_ptr(new script);
660 }
661
662 void reset()
663 {
664 if (state_) destroy();
665 state_ = luaL_newstate();
666 }
667
668 void import_standard_libraries()
669 {
670 luaL_openlibs(state_);
671 }
672
673 void import_safe_standard_libraries()
674 {
675 import_base_library();
676 import_string_library();
677 import_table_library();
678 import_math_library();
679 import_os_library();
680 import_debug_library();
681
682 slot g = globals();
683
684 push_nil(); g.set_field("dofile");
685 push_nil(); g.set_field("loadfile");
686 push_nil(); g.set_field("require");
687 push_nil(); g.set_field("io");
688 push_nil(); g.set_field("package");
689 slot os = g.push_field("os");
690 push_nil(); os.set_field("execute");
691 push_nil(); os.set_field("exit");
692 push_nil(); os.set_field("getenv");
693 push_nil(); os.set_field("remove");
694 push_nil(); os.set_field("rename");
695 push_nil(); os.set_field("tmpname");
696 pop();
697 }
698
699 #define IMPORT_LIBRARY(L, K) \
700 void import_##L##_library() \
701 { \
702 push(luaopen_##L); \
703 push(LUA_##K##LIBNAME); \
704 call(1, 0); \
705 }//
706 IMPORT_LIBRARY(base, CO);
707 IMPORT_LIBRARY(debug, DB);
708 IMPORT_LIBRARY(io, IO);
709 IMPORT_LIBRARY(math, MATH);
710 IMPORT_LIBRARY(os, OS);
711 IMPORT_LIBRARY(package, LOAD);
712 IMPORT_LIBRARY(string, STR);
713 IMPORT_LIBRARY(table, TAB);
714 #undef IMPORT_LIBRARY
715
716 void
717 import_function(const std::string& name, const function& function)
718 {
719 push(function);
720 lua_setglobal(state_, name.c_str());
721 }
722
723 status do_string(const std::string& commands)
724 {
725 return status(luaL_dostring(state_, commands.c_str()));
726 }
727
728 status do_file(const std::string& file)
729 {
730 return status(luaL_dofile(state_, file.c_str()));
731 }
732
733 /*
734 * Thread-handling methods.
735 */
736
737 script push_new_thread()
738 {
739 return script(state_);
740 }
741
742 slot push_thread()
743 {
744 lua_pushthread(state_);
745 return top();
746 }
747
748 status resume(int nargs)
749 {
750 return status(lua_resume(state_, nargs));
751 }
752
753 status thread_status() const
754 {
755 return status(lua_status(state_));
756 }
757
758 int yield(int results)
759 {
760 return lua_yield(state_, results);
761 }
762
763 bool is_main_thread() const
764 {
765 bool is_main = lua_pushthread(state_);
766 lua_pop(state_, 1);
767 return is_main;
768 }
769
770 /**
771 * Throw an error with the value at the top of the stack. If this is
772 * called from an imported function, the error will be caught and
773 * returned on the stack when the execution aborts.
774 */
775 int raise()
776 {
777 throw std::runtime_error("");
778 return 0;
779 }
780
781 /**
782 * Throw an error with a given message describing the problem. If this
783 * is called from an imported function, the error will be caught and
784 * returned on the stack when the execution aborts.
785 */
786 int raise(const std::string& message)
787 {
788 throw std::runtime_error(message);
789 return 0;
790 }
791
792 /*
793 * Get significant values.
794 */
795
796 slot globals() const
797 {
798 return slot(*this, globals_index);
799 }
800 slot registry() const
801 {
802 return slot(*this, registry_index);
803 }
804 slot environment() const
805 {
806 return slot(*this, environment_index);
807 }
808 slot top() const
809 {
810 return slot(*this, stack_size());
811 }
812
813 /**
814 * Set the size of the stack.
815 * \param size The stack size.
816 */
817 void stack_size(int size)
818 {
819 lua_settop(state_, size);
820 }
821
822 /**
823 * Get the size of the stack; this is also the index of the top-most
824 * value.
825 * \return The stack size.
826 */
827 int stack_size() const
828 {
829 return lua_gettop(state_);
830 }
831
832 /**
833 * Clear the stack, setting its size to zero.
834 */
835 void clear_stack()
836 {
837 stack_size(0);
838 }
839
840 /**
841 * Makes sure there is at least extra more places on the stack.
842 * Returns false if space couldn't be created. Just like with the
843 * regular Lua API, you are responsible to make sure the stack is big
844 * enough to hold whatever you want to push on it. This is usually
845 * only an issue if you're pushing stuff in a loop.
846 */
847 bool check_stack(int extra)
848 {
849 return (bool)lua_checkstack(state_, extra);
850 }
851
852 /**
853 * Concatenates the top-most n slots on the stack.
854 */
855 void concatenate(int n = 2)
856 {
857 lua_concat(state_, n);
858 }
859
860 /*
861 * Push some values onto the stack.
862 */
863
864 #define PUSH_INT_OF_TYPE(T) \
865 slot push(T value) \
866 { \
867 lua_pushinteger(state_, static_cast<lua_Integer>(value)); \
868 return top(); \
869 }//
870 PUSH_INT_OF_TYPE(char);
871 PUSH_INT_OF_TYPE(unsigned char);
872 PUSH_INT_OF_TYPE(short);
873 PUSH_INT_OF_TYPE(unsigned short);
874 PUSH_INT_OF_TYPE(int);
875 PUSH_INT_OF_TYPE(unsigned int);
876 PUSH_INT_OF_TYPE(long);
877 PUSH_INT_OF_TYPE(unsigned long);
878 PUSH_INT_OF_TYPE(long long);
879 PUSH_INT_OF_TYPE(unsigned long long);
880 #undef PUSH_INT_OF_TYPE
881
882 slot push(bool value)
883 {
884 lua_pushboolean(state_, int(value));
885 return top();
886 }
887
888 slot push(float value)
889 {
890 lua_pushnumber(state_, lua_Number(value));
891 return top();
892 }
893 slot push(double value)
894 {
895 lua_pushnumber(state_, lua_Number(value));
896 return top();
897 }
898
899 slot push(const std::string& value)
900 {
901 lua_pushlstring(state_, value.c_str(), value.length());
902 return top();
903 }
904 slot push(const char* value)
905 {
906 lua_pushstring(state_, value);
907 return top();
908 }
909 slot push(const char* value, size_t length)
910 {
911 lua_pushlstring(state_, value, length);
912 return top();
913 }
914
915 slot push(const function& function)
916 {
917 push<script::function>(function);
918 push_pointer(this);
919 push(call_functor, 2);
920 return top();
921 }
922 slot push(cfunction function)
923 {
924 push_pointer(function);
925 push_pointer(this);
926 push(call_function, 2);
927 return top();
928 }
929
930 template <class T>
931 slot push(const T& object)
932 {
933 void* storage;
934 slot copy = push_data(storage, sizeof(T));
935 new(storage) T(object);
936
937 push_class_metatable<T>();
938 copy.set_metatable();
939 return copy;
940 }
941
942 template <class T>
943 slot push_class(const function& ctor)
944 {
945 slot metatable = push_class_metatable<T>();
946
947 slot constructor = push_table();
948 push(ctor);
949 constructor.set_field("__call");
950 metatable.set_metatable();
951
952 return metatable;
953 }
954 template <class T>
955 slot push_class(cfunction ctor)
956 {
957 slot metatable = push_class_metatable<T>();
958
959 slot constructor = push_table();
960 push(ctor);
961 constructor.set_field("__call");
962 metatable.set_metatable();
963
964 return metatable;
965 }
966
967 slot push_nil()
968 {
969 lua_pushnil(state_);
970 return top();
971 }
972
973 slot push_from_thread(script& thread, int n)
974 {
975 lua_xmove(thread.state_, state_, n);
976 return top();
977 }
978
979 slot push_code(const std::string& file, status& result)
980 {
981 result = status(luaL_loadfile(state_, file.c_str()));
982 return top();
983 }
984
985 slot push_code(const std::string& name,
986 const char* buffer, size_t size, status& result)
987 {
988 result = status(luaL_loadbuffer(state_,
989 buffer, size, name.c_str()));
990 return top();
991 }
992
993 slot push_data(void*& data, size_t size)
994 {
995 data = lua_newuserdata(state_, size);
996 return top();
997 }
998
999 template <class T>
1000 slot push_pointer(const T* ptr)
1001 {
1002 lua_pushlightuserdata(state_,
1003 const_cast<void*>((const void*)ptr));
1004 return top();
1005 }
1006 slot push_pointer(cfunction function)
1007 {
1008 return push_pointer((void*)function);
1009 }
1010
1011 slot push_table(const std::string& name, int narr = 0, int nrec = 0)
1012 {
1013 if (name.empty()) return globals().push_field("_G");
1014
1015 slot table = globals().push_field(name);
1016 if (table.is_table()) return table;
1017
1018 pop();
1019 push_table(narr, nrec);
1020 globals().set_field(name);
1021
1022 return globals().push_field(name);
1023 }
1024 slot push_table(int narr = 0, int nrec = 0)
1025 {
1026 lua_createtable(state_, narr, nrec);
1027 return top();
1028 }
1029
1030 slot push_metatable(const std::string& type, bool& is_new)
1031 {
1032 is_new = luaL_newmetatable(state_, type.c_str());
1033 return top();
1034 }
1035 slot push_metatable(const std::string& type)
1036 {
1037 luaL_newmetatable(state_, type.c_str());
1038 return top();
1039 }
1040
1041 template <class T>
1042 slot push_type()
1043 {
1044 return push_pointer(&typeid(T));
1045 }
1046
1047 /**
1048 * Call a function on the stack. The correct procedure is to push a
1049 * function onto the stack followed by nargs arguments. This method
1050 * will pop them off upon return, leaving up to nresults return values
1051 * (default is any number of return values, depending on the callee).
1052 */
1053 status call(int nargs = 0, int nresults = LUA_MULTRET)
1054 {
1055 return status(lua_pcall(state_, nargs, nresults, 0));
1056 }
1057
1058 /**
1059 * Pops n values from the top of the stack.
1060 */
1061 void pop(int n = 1)
1062 {
1063 lua_pop(state_, n);
1064 }
1065
1066 /**
1067 * Index into the stack to get a slot.
1068 */
1069 slot operator [] (int index) const
1070 {
1071 return slot(*this, index);
1072 }
1073
1074 /*
1075 * Control over the garbage collection process.
1076 */
1077
1078 void collect_garbage()
1079 {
1080 lua_gc(state_, LUA_GCCOLLECT, 0);
1081 }
1082 void collect_garbage(int step)
1083 {
1084 lua_gc(state_, LUA_GCSTEP, step);
1085 }
1086
1087 void disable_garbage_collector()
1088 {
1089 lua_gc(state_, LUA_GCSTOP, 0);
1090 }
1091 void enable_garbage_collector()
1092 {
1093 lua_gc(state_, LUA_GCRESTART, 0);
1094 }
1095
1096 float memory_used() const
1097 {
1098 // in kilobytes
1099 return lua_gc(state_, LUA_GCCOUNT, 0) +
1100 lua_gc(state_, LUA_GCCOUNTB, 0) / 1024.0f;
1101 }
1102
1103 void tune_garbage_collector(int pause, int step = 200)
1104 {
1105 lua_gc(state_, LUA_GCSETPAUSE, pause);
1106 lua_gc(state_, LUA_GCSETSTEPMUL, step);
1107 }
1108
1109 private:
1110
1111 script(lua_State* state) :
1112 state_(lua_newthread(state)) {}
1113
1114 slot push(lua_CFunction function, int upvalues = 0)
1115 {
1116 lua_pushcclosure(state_, function, upvalues);
1117 return top();
1118 }
1119
1120 template <class T>
1121 slot push_class_metatable()
1122 {
1123 bool is_new;
1124 slot metatable = push_metatable(typeid(T).name(), is_new);
1125 if (is_new)
1126 {
1127 metatable.push_copy(); // class behavior
1128 metatable.set_field("__index");
1129 push_type<T>();
1130 metatable.set_field("__cxxtype"); // type_info
1131 push(object_finalizer_<T>);
1132 metatable.set_field("__gc"); // finalizer
1133 //push(object_tostring_<T>);
1134 //metatable.set_field("__tostring"); // tostring
1135 }
1136 return metatable;
1137 }
1138
1139 template <class T>
1140 static int object_tostring_(lua_State* state)
1141 {
1142 std::ostringstream stream;
1143 stream << *reinterpret_cast<T*>(lua_touserdata(state, 1));
1144 lua_pushlstring(state,
1145 stream.str().c_str(), stream.str().length());
1146 return 1;
1147 }
1148
1149 template <class T>
1150 static int object_finalizer_(lua_State* state)
1151 {
1152 reinterpret_cast<T*>(lua_touserdata(state, 1))->~T();
1153 return 0;
1154 }
1155
1156 static int call_functor(lua_State* state)
1157 {
1158 function* function = (script::function*)lua_touserdata(state,
1159 lua_upvalueindex(1));
1160
1161 script* script = (moof::script*)lua_touserdata(state,
1162 lua_upvalueindex(2));
1163
1164 try
1165 {
1166 return (*function)(*script);
1167 }
1168 catch (const std::exception& e)
1169 {
1170 if (0 < std::strlen(e.what()))
1171 {
1172 luaL_where(state, 1);
1173 lua_pushstring(state, e.what());
1174 lua_concat(state, 2);
1175 }
1176 return lua_error(state);
1177 }
1178 catch (const char* e)
1179 {
1180 luaL_where(state, 1);
1181 lua_pushstring(state, e);
1182 lua_concat(state, 2);
1183 return lua_error(state);
1184 }
1185 catch (...)
1186 {
1187 return lua_error(state);
1188 }
1189 }
1190
1191 static int call_function(lua_State* state)
1192 {
1193 cfunction function = (cfunction)lua_touserdata(state,
1194 lua_upvalueindex(1));
1195
1196 script* script = (moof::script*)lua_touserdata(state,
1197 lua_upvalueindex(2));
1198
1199 try
1200 {
1201 return function(*script);
1202 }
1203 catch (const std::exception& e)
1204 {
1205 if (0 < std::strlen(e.what()))
1206 {
1207 luaL_where(state, 1);
1208 lua_pushstring(state, e.what());
1209 lua_concat(state, 2);
1210 }
1211 return lua_error(state);
1212 }
1213 catch (const char* e)
1214 {
1215 luaL_where(state, 1);
1216 lua_pushstring(state, e);
1217 lua_concat(state, 2);
1218 return lua_error(state);
1219 }
1220 catch (...)
1221 {
1222 return lua_error(state);
1223 }
1224 }
1225
1226 void destroy()
1227 {
1228 if (is_main_thread()) lua_close(state_);
1229 }
1230
1231 lua_State* state_;
1232 };
1233
1234 using namespace std::rel_ops;
1235
1236 /**
1237 * Output a script value to a stream.
1238 */
1239 inline std::ostream&
1240 operator << (std::ostream& stream, const script::slot& slot)
1241 {
1242 std::string str;
1243 bool boolean;
1244
1245 if (slot.get(str))
1246 {
1247 stream << str;
1248 }
1249 else if (slot.get(boolean))
1250 {
1251 if (boolean) stream << "true";
1252 else stream << "false";
1253 }
1254 else if (slot.is_nil())
1255 {
1256 stream << "nil";
1257 }
1258 else
1259 {
1260 stream << slot.type_name() << " (" << slot.id() << ")";
1261 }
1262
1263 return stream;
1264 }
1265
1266
1267 } // namespace moof
1268
1269 #endif // _MOOF_SCRIPT_HH_
1270
This page took 0.083996 seconds and 4 git commands to generate.