lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]


I need to implement user define event handlers in lua and wanted to make the
solution I came up with wasn't a horribly bad idea. The user will be able to
author a script. Before the script is executed some number of userdata
objects will be added to the script engine via global names. The user will
be able to attach a event handler ( function ) to one or more objects. When
the objects event is triggered ( internally from C++ ) the event handler
needs to be called. At this point I'm not 100% if I need to support multiple
handlers or adding/removing handlers so I'm ignoring that for now.
 
I'm using the LUA_REGISTRYINDEX to store the handler - the id that is
returned from luaL_ref is stashed in the C data structure. I also put the
userdata in LUA_REGISTRYINDEX, stashing the result in the data struct as
well, so I can quick get from my data struct to the lua userdata so I can
call the handler. When I need to call my trigger from C I use lua_rawgeti to
get both the handler and object from the registry and then call the handler.
 
It appears to be working but since this is my first attempt at embedding lua
I wanted to make sure I'm not doing something stupid. I'm sure I'm missing
some error handling and whatnot but is the approach I'm taking the right
one? Is there a better way? I've pasted my code below.

Thanks-

John

#include <iostream>
#include <lua/lauxlib.h>
#include <lua/lualib.h>
#include <lua/lua.h>

const char* MyObjectID = "Test.Object";
const char* MyObjectClass = "Object";

static lua_State* L;

struct MyObject
{
  double value;
  int handler;
  int obj_id;
  MyObject() : handler( 0 ), value( 23 ), obj_id(0)
  {
  }
  void trigger()
  {
    lua_rawgeti(L, LUA_REGISTRYINDEX, handler );
    lua_rawgeti(L, LUA_REGISTRYINDEX, obj_id );
    lua_call( L, 1, 0 );
  }
};

static MyObject* objGet(lua_State* lua)
{
  MyObject** ptr = (MyObject**)luaL_checkudata(lua,1, MyObjectID);
  return *ptr;
}

static int get_value( lua_State* lua )
{
  MyObject* obj = objGet(lua);
  lua_pushnumber(lua,obj->value);
  return 1;
}

static int set_value( lua_State* lua )
{
  MyObject* obj = objGet(lua);
  obj->value = luaL_checknumber(lua,2);
  return 1;
}

static int add_handler( lua_State* lua )
{
  MyObject* obj = objGet(lua);
  if( lua_isfunction(lua,2))
  {
    // 
    lua_pushvalue( lua, 2 ); // dup the function and place it in the
registry
    obj->handler = luaL_ref(lua, LUA_REGISTRYINDEX);
  }
  else
  {
    std::cout << "NOT A FUNCTION!" << std::endl;
  }
  return 1;
}

static const struct luaL_reg MyObjectClassMethods [] = 
{
	{ NULL, NULL }
};
static const struct luaL_reg MyObjectInstanceMethods[] = 
{
  { "get_value", get_value },
  { "set_value", set_value },
  { "add_handler", add_handler },
  {NULL,NULL}
};


int luaMyObjectOpen( lua_State* lua )
{
  luaL_newmetatable( lua, MyObjectID );
  lua_pushvalue( lua, -1 ); 
  lua_setfield( lua, -2, "__index");
  luaL_register(lua, NULL, MyObjectInstanceMethods );
  luaL_register(lua, MyObjectClass, MyObjectClassMethods);
  return 1;
}

void add_object( lua_State* lua, const char* name, MyObject* obj )
{
  MyObject** ptr = (MyObject**)lua_newuserdata(lua,sizeof(MyObject*));
  *ptr = obj;
  (*ptr)->value = 1;
  // set metatable
  luaL_getmetatable(lua,MyObjectID);
  lua_setmetatable(lua,-2);
  // make a copy of the userdata and place it in the registry
  // so we can get back to it via a MyObject ptr
  lua_pushvalue(lua,-1);
  (*ptr)->obj_id = luaL_ref(lua, LUA_REGISTRYINDEX);
  lua_setglobal(lua,name);
}

void check_status( lua_State* lua, int status )
{
  if( status )
  {
    const char* msg = lua_tostring(lua, -1);
    lua_pop(lua, 1);
    std::cout << "error : " << msg << std::endl;
  }
}

int main( int argc, char** argv ) {
  L = luaL_newstate();
  luaL_openlibs(L);
  luaMyObjectOpen(L);
  MyObject* obj = new MyObject();
  // add the object to the lua engine
  add_object( L, "crap", obj );
  // call some script
  check_status( L, luaL_dostring( L, "print( crap:get_value())" ));
  check_status( L, luaL_dostring( L, "crap:set_value(123)" ));
  check_status( L, luaL_dostring( L, "print( crap:get_value())" ));
  check_status( L, luaL_dostring( L, "crap:add_handler( function ( obj )
print( 'TRIGGER : value is ' .. obj:get_value()) end )" ));
  // trigger my handler
  obj->trigger();
  return 0;
}