Luna Four Code

lua-users home
wiki

template < class T > class Luna {
    typedef struct {
	T              *pT;
    } userdataType;
    
  public:

    enum {
	NUMBER,
	STRING
    };

    struct PropertyType {
	const char     *name;
	int             (T::*getter) (lua_State *);
	int             (T::*setter) (lua_State *);
    };

    struct FunctionType {
	const char     *name;
	int             (T::*function) (lua_State *);
    };

/*
  @ check
  Arguments:
    * L - Lua State
    * narg - Position to check

  Description:
    Retrieves a wrapped class from the arguments passed to the function, specified by narg (position).
    This function will raise an exception if the argument is not of the correct type.
*/
    static T       *check(lua_State * L, int narg) {
	// Check to see whether we are a table
	    if (lua_istable(L,narg+1))
	    {
		    lua_gettablevalue(L,narg+1,0);
		    userdataType   *ud =
				    static_cast <userdataType * >(luaL_checkudata(L, -1, T::className));
		    if (!ud)
			    luaL_typerror(L, narg+1, T::className);
		    lua_pop(L,1);
		    return ud->pT;		// pointer to T object
	    }
	    else
	    {
		    luaL_typerror(L, narg+1, T::className);
	    } 
    }

/*
  @ lightcheck
  Arguments:
    * L - Lua State
    * narg - Position to check

  Description:
    Retrieves a wrapped class from the arguments passed to the function, specified by narg (position).
    This function will return NULL if the argument is not of the correct type.  Useful for supporting
    multiple types of arguments passed to the function
*/ 
	static T       *lightcheck(lua_State * L, int narg) {
	// Check to see whether we are a table
	    if (lua_istable(L,narg+1))
	    {
		    lua_gettablevalue(L,narg+1,0);
		    userdataType   *ud =
				    static_cast <userdataType * >(luaL_testudata(L, -1, T::className));
		    if (!ud)
			    return NULL; // lightcheck returns NULL if not found.
		    lua_pop(L,1);
		    return ud->pT;		// pointer to T object
	    }
	    else
	    {
		    return NULL;
	    } 
    }

/*
  @ Register
  Arguments:
    * L - Lua State
    * namespac - Namespace to load into

  Description:
    Registers your class with Lua.  Leave namespac "" if you want to load it into the global space.
*/
    // REGISTER CLASS AS A GLOBAL TABLE 
    static void     Register(lua_State * L, const char *namespac) {

	if (strcmp(namespac, "") != 0) {
	    lua_getglobal(L, namespac);
	    lua_pushcfunction(L, &Luna < T >::constructor);
	    lua_setfield(L, -2, T::className);
	    lua_pop(L, 1);
	} else {
	    lua_pushcfunction(L, &Luna < T >::constructor);
	    lua_setglobal(L, T::className);
	}

	luaL_newmetatable(L, T::className);
	int             metatable = lua_gettop(L);

	lua_pushstring(L, "__gc");
	lua_pushcfunction(L, &Luna < T >::gc_obj);
	lua_settable(L, metatable);

	lua_pushstring(L, "__index");
	lua_pushcfunction(L, &Luna < T >::property_getter);
	lua_settable(L, metatable);

	lua_pushstring(L, "__newindex");
	lua_pushcfunction(L, &Luna < T >::property_setter);
	lua_settable(L, metatable);

    }

/*
  @ constructor (internal)
  Arguments:
    * L - Lua State
*/
    static int      constructor(lua_State * L) {

	lua_newtable(L);

	int             newtable = lua_gettop(L);

	lua_pushnumber(L, 0);

	T             **a = (T **) lua_newuserdata(L, sizeof(T *));
	T              *obj = new T(L, true);
	*a = obj;

	int             userdata = lua_gettop(L);

	luaL_getmetatable(L, T::className);

	lua_setmetatable(L, userdata);

	lua_settable(L, newtable);

	luaL_getmetatable(L, T::className);
	lua_setmetatable(L, newtable);

	luaL_getmetatable(L, T::className);

	for (int i = 0; T::Properties[i].name; i++) {
	    lua_pushstring(L, T::Properties[i].name);
	    lua_pushnumber(L, i);
	    lua_settable(L, -3);
	}

	lua_pop(L, 1);

	for (int i = 0; T::Functions[i].name; i++) {
	    lua_pushstring(L, T::Functions[i].name);
	    lua_pushnumber(L, i);
	    lua_pushcclosure(L, &Luna < T >::function_dispatch, 1);
	    lua_settable(L, newtable);
	}

	return 1;
    }

/*
  @ createNew
  Arguments:
    * L - Lua State

  Description:
    Loads an instance of the class into the Lua stack, and provides you a pointer so you can modify it.
*/
    static T       *createNew(lua_State * L) {

	lua_newtable(L);

	int             newtable = lua_gettop(L);

	lua_pushnumber(L, 0);

	T             **a = (T **) lua_newuserdata(L, sizeof(T *));
	T              *obj = new T(L, false);
	obj->isExisting = false;
	*a = obj;

	int             userdata = lua_gettop(L);

	luaL_getmetatable(L, T::className);

	lua_setmetatable(L, userdata);

	lua_settable(L, newtable);

	luaL_getmetatable(L, T::className);
	lua_setmetatable(L, newtable);

	luaL_getmetatable(L, T::className);

	for (int i = 0; T::Properties[i].name; i++) {
	    // ADD NAME KEY 
	    lua_pushstring(L, T::Properties[i].name);
	    lua_pushnumber(L, i);
	    lua_settable(L, -3);
	}

	lua_pop(L, 1);

	for (int i = 0; T::Functions[i].name; i++) {
	    lua_pushstring(L, T::Functions[i].name);
	    lua_pushnumber(L, i);
	    lua_pushcclosure(L, &Luna < T >::function_dispatch, 1);
	    lua_settable(L, newtable);
	}

	return obj;
    }

/*
  @ createFromExisting
  Arguments:
    * L - Lua State
    * existingobj - Existing instance of object

  Description:
    Loads an instance of the class into the Lua stack, instead using an existing object rather than creating a new one.
    Returns the existing object.
*/
    static T       *createFromExisting(lua_State * L, T * existingobj) {

	lua_newtable(L);

	int             newtable = lua_gettop(L);

	lua_pushnumber(L, 0);

	T             **a = (T **) lua_newuserdata(L, sizeof(T *));
	T              *obj = existingobj;
	obj->isExisting = true;
	*a = obj;

	int             userdata = lua_gettop(L);


	luaL_getmetatable(L, T::className);

	lua_setmetatable(L, userdata);

	lua_settable(L, newtable);

	luaL_getmetatable(L, T::className);
	lua_setmetatable(L, newtable);

	luaL_getmetatable(L, T::className);

	for (int i = 0; T::Properties[i].name; i++) {
	    lua_pushstring(L, T::Properties[i].name);
	    lua_pushnumber(L, i);
	    lua_settable(L, -3);
	}

	lua_pop(L, 1);

	for (int i = 0; T::Functions[i].name; i++) {
	    lua_pushstring(L, T::Functions[i].name);
	    lua_pushnumber(L, i);
	    lua_pushcclosure(L, &Luna < T >::function_dispatch, 1);
	    lua_settable(L, newtable);
	}

	return obj;
    }

/*
  @ property_getter (internal)
  Arguments:
    * L - Lua State
*/
    static int      property_getter(lua_State * L) {

	lua_pushvalue(L, 2);

	lua_getmetatable(L, 1);

	lua_pushvalue(L, 2);
	lua_rawget(L, -2);

	if (lua_isnumber(L, -1)) {

	    int             _index = lua_tonumber(L, -1);

	    lua_pushnumber(L, 0);
	    lua_rawget(L, 1);

	    T             **obj =
		static_cast < T ** >(lua_touserdata(L, -1));

	    lua_pushvalue(L, 3);

	    const PropertyType *_properties = (*obj)->T::Properties;

		int result = ((*obj)->*(T::Properties[_index].getter)) (L);

	    return result;

	}
	// PUSH NIL 
	lua_pushnil(L);

	return 1;

    }

/*
  @ property_setter (internal)
  Arguments:
    * L - Lua State
*/
    static int      property_setter(lua_State * L) {

	lua_getmetatable(L, 1);

	lua_pushvalue(L, 2);
	lua_rawget(L, -2);

	if (lua_isnil(L, -1)) {

	    lua_pop(L, 2);

	    lua_rawset(L, 1);

	    return 0;
	} else {

	    int             _index = lua_tonumber(L, -1);

	    lua_pushnumber(L, 0);
	    lua_rawget(L, 1);

	    T             **obj =
		static_cast < T ** >(lua_touserdata(L, -1));

	    lua_pushvalue(L, 3);

	    const PropertyType *_properties = (*obj)->T::Properties;

	    return ((*obj)->*(T::Properties[_index].setter)) (L);

	}

    }

/*
  @ function_dispatch (internal)
  Arguments:
    * L - Lua State
*/
    static int      function_dispatch(lua_State * L) {

	int             i = (int) lua_tonumber(L, lua_upvalueindex(1));

	lua_pushnumber(L, 0);
	lua_rawget(L, 1);

	T             **obj = static_cast < T ** >(lua_touserdata(L, -1));

	lua_pop(L, 1);

	return ((*obj)->*(T::Functions[i].function)) (L);
    }

/*
  @ gc_obj (internal)
  Arguments:
    * L - Lua State
*/
    static int      gc_obj(lua_State * L) {

	T             **obj =
	    static_cast < T ** >(luaL_checkudata(L, -1, T::className));

	if (!(*obj)->isExisting && !(*obj)->isPrecious)
	{
		cout << "Cleaning up a " << T::className << "." << endl;
		delete(*obj);
	}

	return 0;
    }

};

RecentChanges · preferences
edit · history
Last edited December 21, 2012 11:52 pm GMT (diff)