#pragma comment(lib, "lua51.lib") #include #include #include #include #include #include namespace waaagh { // begin namespace WAAAGH // an enum that sets up constants for Lua basic types enum { NUMBER = LUA_TNUMBER, NIL = LUA_TNIL, TABLE = LUA_TTABLE, STRING = LUA_TSTRING }; // vm class encapsulates an lua state and related functions class vm { private: lua_State *state; // a pointer to the state class proxy; public: vm(); // constructor calls lua_open ~vm(); // destructor calls lua_close void run_string(const char *string); // execute a string given the string (using dostring for now) void run_file(const char *filename); // execute a file (using dofile for now) proxy operator [](const char *key); operator lua_State *() { return state; } // convert to state pointer }; // represents a value in a table that has a name or identifier // this proxy can be cast into anything or called as a function class vm::proxy { private: // the "parent" of the named value is the table that contains it // if the parent is set to null, then the parent is "_G" -- the global table proxy *parent; lua_State *state; // the Lua vm associated with this named value std::string identifier; // the "identifier" is the actual name // iterate through this named proxy's parents, pushing everything onto the stack until // eventually this named proxy is on the top of the stack // returns the amount of stack "slots" that have been used by the operation int push_self(int stack_slots = 0); // pushes something onto the stack, using template specialization template static void push_value(lua_State *state, T val); // using template specialization, gets something off the stack template static T get_value(lua_State *state, int idx); // we have to do a bit of partial template specialization in order handle functions whose return types // are "void" -- since you can't partially specialize functiosn (at least on my compiler), this is a hack // where we put the callbacks as static methods inside classes who we then specialize template struct function_wrapper0 { // Lua C functions that wrap around C++ functions and do all the dirty work! static int callback(lua_State *state) { // the function object is stored as a pointer in a closure boost::function *function_object = (boost::function *)lua_touserdata(state, lua_upvalueindex(1)); push_value(state, (*function_object)()); // call the function and push the result unto the stack return 1; } }; template<> struct function_wrapper0 { static int callback(lua_State *state) { boost::function *function_object = (boost::function *)lua_touserdata(state, lua_upvalueindex(1)); (*function_object)(); // call the function return 0; // no results, this is a template specialization for void return type } }; template struct function_wrapper1 { static int callback(lua_State *state) { boost::function *function_object = (boost::function *)lua_touserdata(state, lua_upvalueindex(1)); push_value(state, (*function_object)(get_value(state, -1))); return 1; } }; // yeah, and now function wrappers for everything else // copy and paste coding for teh win!! template struct function_wrapper1 { static int callback(lua_State *state) { boost::function *function_object = (boost::function *)lua_touserdata(state, lua_upvalueindex(1)); (*function_object)(get_value(state, -1)); return 0; } }; template struct function_wrapper2 { static int callback(lua_State *state) { boost::function *function_object = (boost::function *)lua_touserdata(state, lua_upvalueindex(1)); push_value(state, (*function_object)(get_value(state, -2), get_value(state, -1))); return 1; } }; template struct function_wrapper2 { static int callback(lua_State *state) { boost::function *function_object = (boost::function *)lua_touserdata(state, lua_upvalueindex(1)); (*function_object)(get_value(state, -2), get_value(state, -1)); return 0; } }; // a simple lua C function wrapper that will delete the userdata that is passed to it static int delete_wrapper(lua_State *state) { delete lua_touserdata(state, -1); return 0; } // given a boost::function object and a wrapper function (such as the ones above), this function // will wrap the boost::function in an lua C function and push the C function onto the stack template void push_function(FunctionT func, lua_CFunction wrapper) { // we store the actual function pointer in a closure as a malloc'd chunk of userdata // (i'm not sure if this whole memory allocation thing is one gigantic hack, and if there // is perhaps a better way to do this, like storing the raw 4 byte function pointer, but // i couldn't get anything to work, so i just went with this method) lua_pushlightuserdata(state, (void *)(new FunctionT(func))); // we need a metatable that responds to the __gc event in order to free the boost::function lua_newtable(state); lua_pushcfunction(state, delete_wrapper); lua_setfield(state, -2, "__gc"); lua_setmetatable(state, -2); // okay, now that we have the metatable tomfoolery over with, we push the function with its closure lua_pushcclosure(state, wrapper, 1); } public: bool is_nil(); // returns true if this is a nil value proxy(proxy *parent_, const char *identifier_, lua_State *state_) : parent(parent_), identifier(identifier_), state(state_) { } // TODO: implement this shit! /*template R call(); template R call(T0 arg1); template R call(T1 arg1, T2 arg2);*/ // the () operator is used when a function does not return anything void operator ()(); template void operator ()(T1 arg1); template void operator()(T1 arg1, T2 arg2); // conversion and stuffs! template operator T() { push_self(); T result = get_value(state, -1); lua_pop(state, 1); return result; } // assignment and stuffs! // this is basically a macro that generates the code for an assignment // function; the argument is an expression that is supposed to push the value // you want to assign onto the stack // there is probably a better way to do this, one that doesn't involve ugly // C macros, but BLAH! WAAAAAAAAAAAAAAAGH! #define GEN_ASSIGN_FUNC(PUSH_EXPRESSION) \ if(parent != 0) { \ int stack_slots = parent->push_self(); \ PUSH_EXPRESSION; \ lua_setfield(state, -2, identifier.c_str()); \ lua_pop(state, stack_slots); \ } else { \ PUSH_EXPRESSION; \ lua_setglobal(state, identifier.c_str()); \ } template void operator =(T val) { GEN_ASSIGN_FUNC( (push_value(state, val)) ); } template void operator =(boost::function func) { GEN_ASSIGN_FUNC( (push_function > (func, function_wrapper0::callback)) ); } template void operator =(boost::function func) { GEN_ASSIGN_FUNC( (push_function > (func, function_wrapper1::callback)) ); } template void operator =(boost::function func) { GEN_ASSIGN_FUNC( (push_function > (func, function_wrapper2::callback)) ); } #undef GEN_ASSIGN_FUNC // the [] operator is used to access table elements, if this proxy is a table proxy operator [](const char *key) { return proxy(this, key, state); } }; template<> void vm::proxy::push_value(lua_State *state, double val) { lua_pushnumber(state, val); } template<> void vm::proxy::push_value(lua_State *state, float val) { lua_pushnumber(state, val); } template<> void vm::proxy::push_value(lua_State *state, int val) { lua_pushnumber(state, val); } template<> void vm::proxy::push_value(lua_State *state, bool val) { lua_pushboolean(state, val); } template<> void vm::proxy::push_value(lua_State *state, const char *val) { lua_pushstring(state, val); } template<> double vm::proxy::get_value(lua_State *state, int idx) { return lua_tonumber(state, idx); } template<> float vm::proxy::get_value(lua_State *state, int idx) { return lua_tonumber(state, idx); } template<> int vm::proxy::get_value(lua_State *state, int idx) { return lua_tonumber(state, idx); } template<> bool vm::proxy::get_value(lua_State *state, int idx) { return lua_toboolean(state, idx); } template<> std::string vm::proxy::get_value(lua_State *state, int idx) { return lua_tostring(state, idx); } bool vm::proxy::is_nil() { int stack_slots = push_self(); bool result = (lua_type(state, -1) == LUA_TNIL); lua_pop(state, stack_slots); return result; } void vm::proxy::operator ()() { int stack_slots = push_self(); lua_pcall(state, 0, 0, 0); lua_pop(state, stack_slots - 1); } template void vm::proxy::operator ()(T1 arg1) { int stack_slots = push_self(); push_value(state, arg1); lua_pcall(state, 1, 0, 0); lua_pop(state, stack_slots - 1); } template void vm::proxy::operator ()(T1 arg1, T2 arg2) { int stack_slots = push_self(); push_value(state, arg1); push_value(state, arg2); lua_pcall(state, 2, 0, 0); lua_pop(state, stack_slots - 1); } int vm::proxy::push_self(int stack_slots) { if(parent != 0) { int stack_slots_ = parent->push_self(stack_slots); lua_getfield(state, -1, identifier.c_str()); return stack_slots_ + 1; } else { lua_getglobal(state, identifier.c_str()); return stack_slots + 1; } } vm::vm() { state = lua_open(); luaL_openlibs(state); } vm::~vm() { if(state) lua_close(state); } vm::proxy vm::operator [](const char *key) { return proxy(0, key, state); } void vm::run_string(const char *string) { if(luaL_dostring(state, string) != 0) // TODO: better errors std::cerr << lua_tostring(state, -1) << std::endl; } void vm::run_file(const char *filename) { if(luaL_dofile(state, filename) != 0) // TODO: better errors std::cerr << lua_tostring(state, -1) << std::endl; } } // end namespace WAAAGH float mult(float x, float y) { return x * y; } int main(int argc, char **argv) { waaagh::vm vm; // create the virtual machine instance // use operator [] to access an lua thing, and then set it to a function pointer vm["mult"] = boost::function(mult); // execute a string that uses our newly "registered" function vm.run_string("x = mult(10, 0.5)"); // access the variable "x" and add 2.5 to it float x = (float)vm["x"] + 2.5; std::cout << x << std::endl; // outputs 7.5 vm["x"] = 10.5; // set the variable "x" std::cout << (float)vm["x"] << std::endl; // outputs 10.5 }