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