lua-users home
lua-l archive

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


Nick wrote:
I'm making a game engine, which is using Lua for scripting, using Lunar to export C++ classes to Lua.

I'd like to be able to add unique methods to individual instances in Lua, they don't need to be accessible to C++.

As an example, I have a Sprite class that is used for a lot of the objects in the game, I'd like the instances of these objects to define their own Update function, but this isn't currently possible with the set up that I seem to have (Update is NOT a function in the C++ object):

...
Update Functions can be called fine even though they don't exist in C++ however calls to PlayerSprite:Update call the same function as EnemySprite:Update, seems like its being added to the 'class' not the instance.

I've used Ignacio Burgueño's modifications to enable property style access, and uncommented this part which could be the problem based on this comment in spanish (in Lunar.h):


The wrapper you're using does not allow that. The one included here does, so you can add methods per instance. You'll have to make the same tweaks you surely needed to do to my other wrapper.

lcbBaseObject.h contains a lot of stuff you won't need at all. Is just there for the RegType struct, so you can rip this:
        typedef struct { T* pT; } userdataType;
	typedef int (T::*mfp)(lua_State* L);
	typedef struct {
		const char* name;
		mfp mfunc;
	} RegType;

and paste it somewhere else.

Hope this helps. And sorry, the comments in the code are still in spanish. Didn't have the time to change that.

Regards,
Ignacio Burgueño
#ifndef __luaCppBridge_HybridObjectWithProperties_h__
#define __luaCppBridge_HybridObjectWithProperties_h__

#include "LuaIncludes.h"
#include "lcbBaseObject.h"


#define LCB_HOWP_DECLARE_EXPORTABLE(classname) \
	static const classname##::RegType methods[];\
	static const classname##::RegType setters[];\
	static const classname##::RegType getters[];\
	static const char* className;

/**
Algunos macros útiles más que nada para la definición de propiedades.
*/
#define LCB_DECL_SETGET(fieldname) int set_##fieldname (lua_State* L); int get_##fieldname (lua_State*L);
#define LCB_DECL_GET(fieldname) int get_##fieldname (lua_State* L);
#define LCB_ADD_SET(fieldname) { #fieldname , set_##fieldname }
#define LCB_ADD_GET(fieldname) { #fieldname , get_##fieldname }
#define LCB_IMPL_SET(classname, fieldname) int classname::set_##fieldname (lua_State* L)
#define LCB_IMPL_GET(classname, fieldname) int classname::get_##fieldname (lua_State* L)

namespace LuaCppBridge {

/**
Un HybridObjectWithProperties es una clase de C++ expuesta hacia Lua como una tabla, con un 
userdata en el índice 0 de la misma. Esto permite que Lua agregue funciones y demás 
elementos a la tabla y se pueden acceder desde el lado de C++. Además, se puede definir un conjunto 
de propiedades (con sus respectivos setters y getters) para acceder desde Lua.

TO-DO:
Con esta clase NO se puede hacer herencia. Desde Lua no logré que se viesen las propiedades del padre. 
Sí conseguí que se pudiese acceder a los métodos del padre nomás, pero no tenía sentido habilitarlo si no se 
puede acceder a las propiedades.
*/
template <typename T, bool allowNilProperties = false> class HybridObjectWithProperties : public BaseObject<T, HybridObjectWithProperties<T> > {
private:
	typedef struct ObjectWrapper {
		T* wrappedObject;
		bool collectable;
	} ObjectWrapper;

public:
	//////////////////////////////////////////////////////////////////////////
	///
	static void Register(lua_State* L, const char* parentClassName) {
		Register(L, parentClassName, true);
	}
	
	static void Register(lua_State* L, const char* parentClassName, bool isCreatableByLua) {
		int whereToRegister = g_luaCppBridge_config.libraryTablePosition;
		lua_newtable(L);
		int methods = lua_gettop(L);
		
		luaL_newmetatable(L, T::className);
		int metatable = lua_gettop(L);
		
		// store method table in globals so that scripts can add functions written in Lua.
		lua_pushvalue(L, methods);
		set(L, whereToRegister, T::className);
		
		// hide metatable from Lua getmetatable()
		lua_pushvalue(L, methods);
		set(L, metatable, "__metatable");
		
		// lunar mio
		lua_pushliteral(L, "__index");
		lua_newtable(L);
		int index = lua_gettop(L);
		for(const RegType* l = T::getters; l->name; l++) {
			lua_pushstring(L, l->name);
			lua_pushlightuserdata(L, (void*)l);
			lua_settable(L, index);
		}
		lua_pushvalue(L, methods);
		lua_pushcclosure(L, thunk_index, 2);
		lua_settable(L, metatable);

		
		lua_pushliteral(L, "__newindex");
		lua_newtable(L);
		int newindex = lua_gettop(L);
		for (l = T::setters; l->name; l++) {
			lua_pushstring(L, l->name);
			lua_pushlightuserdata(L, (void*)(l));
			lua_settable(L, newindex);
		}
		lua_pushvalue(L, methods);
		lua_pushcclosure(L, thunk_newindex, 2);
		lua_settable(L, metatable);
		
		lua_pushcfunction(L, T::tostring_T);
		set(L, metatable, "__tostring");
		
		lua_pushcfunction(L, gc_T);
		set(L, metatable, "__gc");
		
		if(isCreatableByLua) {
			// hago que llamando al nombre de la clase, me construya un objeto
			lua_newtable(L);                // mt for method table
			lua_pushcfunction(L, T::new_T);
			lua_pushvalue(L, -1);           // dup new_T function
			set(L, methods, "new");         // add new_T to method table
			set(L, -3, "__call");           // mt.__call = new_T
			lua_setmetatable(L, methods);
		}
		else {
			// hago que llamando al nombre de la clase, me salte un error
			lua_newtable(L);                // mt for method table
			lua_pushcfunction(L, forbidden_new_T);
			lua_pushvalue(L, -1);           // dup new_T function
			set(L, methods, "new");         // add new_T to method table
			set(L, -3, "__call");           // mt.__call = new_T
			lua_setmetatable(L, methods);
		}
		
		// fill method table with methods from class T
		for (l = T::methods; l->name; l++) {
			lua_pushstring(L, l->name);
			lua_pushlightuserdata(L, (void*)l);
			lua_pushcclosure(L, thunk_methods, 1);
			lua_settable(L, methods);
		}
		lua_pop(L, 2);  // drop metatable and method table
	}

	// pcall named lua method from userdata method table
	static int pcall(lua_State* L, const char* method, int nargs = 0, int nresults = LUA_MULTRET, int errfunc = 0)
	{
		int base = lua_gettop(L) - nargs;  // userdata index
		if(!lua_istable(L, base)) {
			lua_settop(L, base-1);           // drop table and args
			luaL_error(L, "not a valid %s table", T::className);
			return -1;
		}
		/*if(!luaL_checkudata(L, base, T::className)) {
			lua_settop(L, base-1);           // drop userdata and args
			lua_pushfstring(L, "not a valid %s userdata", T::className);
			return -1;
		}*/
		
		lua_pushstring(L, method);         // method name
		lua_gettable(L, base);             // get method from userdata
		if(lua_isnil(L, -1)) {            // no method?
			lua_settop(L, base-1);           // drop userdata and args
			//lua_pushfstring(L, "%s missing method '%s'", T::className, method);
			return -1;
		}
		lua_insert(L, base);               // put method under userdata, args
		
		int status = lua_pcall(L, 1+nargs, nresults, errfunc);  // call method
		if(status) {
			const char* msg = lua_tostring(L, -1);
			if(msg == NULL) {
				msg = "(error with no message)";
			}
			lua_pushfstring(L, "%s:%s status = %d\n%s", T::className, method, status, msg);
			lua_remove(L, base);             // remove old message
			return -1;
		}
		return lua_gettop(L) - base + 1;   // number of results
	}

	// call named lua method from userdata method table
	static int call(lua_State* L, const char* method, int nargs = 0, int nresults = LUA_MULTRET)
	{
		int base = lua_gettop(L) - nargs;  // userdata index
		if(!lua_istable(L, base)) {
			lua_settop(L, base-1);           // drop table and args
			luaL_error(L, "not a valid %s table", T::className);
			return -1;
		}
		
		lua_pushstring(L, method);         // method name
		lua_gettable(L, base);             // get method from userdata
		if(lua_isnil(L, -1)) {            // no method?
			lua_settop(L, base - 1);           // drop userdata and args
			//lua_pushfstring(L, "%s missing method '%s'", T::className, method);
			return -1;
		}
		lua_insert(L, base);               // put method under userdata, args
		
		lua_pcall(L, 1 + nargs, nresults);  // call method
		return lua_gettop(L) - base + 1;   // number of results
	}
	
	// push onto the Lua stack a table containing a pointer to a T object at index 0
	static int push(lua_State* L, T* obj, bool gc = false) {
		if(!obj) {
			lua_pushnil(L);
			return 0;
		}
		luaL_getmetatable(L, T::className);  // lookup metatable in Lua registry
		if(lua_isnil(L, -1)) {
			luaL_error(L, "%s missing metatable", T::className);
		}
		// stack: metatabla
		int metatable = lua_gettop(L);
		subtable(L, metatable, "userdata", "v");
		// stack: metatabla, tabla userdata
		int newTable = pushtable(L, obj);	// pongo la tabla que le voy a devolver a Lua en el Stack
											// stack: metatabla, tabla userdata, tabla nueva
		lua_pushnumber(L, 0);	// y en el índice 0 guardo un puntero al objeto

		// creo un userdata, cuyo valor es un wrapper liviano a mi objeto
		ObjectWrapper* wrapper = static_cast<ObjectWrapper*>(lua_newuserdata(L, sizeof(ObjectWrapper)));
		wrapper->wrappedObject = obj;
		wrapper->collectable = gc;

		// y le asigno la metatabla (solo me interesa el método __gc)
		lua_pushvalue(L, metatable);
		lua_setmetatable(L, -2);

		lua_settable(L, newTable);

		lua_pushvalue(L, metatable);	// copio la metatabla
		lua_setmetatable(L, -2);		// y la asigno como metatable a la tabla Lua

		lua_replace(L, metatable); // dejo la tabla nueva en lugar de la metatabla en el stack
		lua_settop(L, metatable);
		return metatable;  // index of new table
	}

	static T* checkopt(lua_State* L, int narg) {
		if(!lua_isnil(L, narg)) {
			return check(L, narg);
		}
		return NULL;
	};
	
	// get table from Lua stack and return pointer to T object
	static T* check(lua_State* L, int narg) {
		luaL_checktype(L, narg, LUA_TTABLE);
		//userdataType *ud = static_cast<userdataType*>(luaL_checkudata(L, narg, T::className));
		//if(!ud) luaL_typerror(L, narg, T::className);
		lua_pushnumber(L, 0);
		lua_rawget(L, narg);
		luaL_checktype(L, -1, LUA_TUSERDATA);
		T* pT = static_cast<ObjectWrapper*>(lua_touserdata(L, -1))->wrappedObject;
		if(!pT) {
			luaL_typerror(L, narg, T::className);
		}
		lua_pop(L, 1);
		return pT;  // pointer to T object
	}
	
protected:
	static int thunk_index(lua_State* L) {
		// stack: tabla, clave
		const T* obj = check(L, 1);  // get 'self', or if you prefer, 'this'
		lua_pushvalue(L, 2);	// stack: tabla, clave clave
		lua_rawget(L, lua_upvalueindex(1));	// upvalue 1 = tabla con getters
		if(lua_isnil(L, -1)) {	// no es una propiedad, buscar en los métodos
			lua_pop(L, 1);	// stack: tabla, clave (argumentos??)
			lua_pushvalue(L, 2);	// tabla, clave, argumentos ... clave
			lua_rawget(L, lua_upvalueindex(2));
			if(!lua_isnil(L, -1)) {
				// le dejo la función thunk en el stack y que lua la llame
				// no la puedo llamar derecho porque patea (con que intenté indexar un string o algo así)
				return 1;
			}
			else {
				lua_pop(L, 1);
				// Aca debería seguir buscando para arriba en la metatabla del padre (si es que estoy)
				// heredando, pero NPI de cómo se hace, así que queda por esta
				// Mando un error y que se vayan a cagar
				if(allowNilProperties) {
					lua_pushnil(L);
				}
				else {
					luaL_error(L, "__index: el valor '%s' no existe para objetos de tipo %s", lua_tostring(L, 2), T::className);
				}
				return 1; // para que el compilador no joda
			}
		}
		else {
			// stack: clave getter
			RegType* l = static_cast<RegType*>(lua_touserdata(L, -1));
			lua_settop(L, 0);
			return (obj->*(l->mfunc))(L);  // call member function
		}
		return 0;
	}
	
	static int thunk_newindex(lua_State* L) {
		// entro con stack: tabla, clave, valor
		const T* obj = check(L, 1);  // get 'self', or if you prefer, 'this'

		// busco en la tabla de setters si hay una propiedad con esta clave
		lua_pushvalue(L, 2);	// stack: tabla, clave, valor, clave
		lua_rawget(L, lua_upvalueindex(1));	// upvalue 1 = tabla con setters
		if(!lua_isnil(L, -1)) {
			// stack: tabla, clave, valor, setter
			RegType* p = static_cast<RegType*>(lua_touserdata(L, -1));
			lua_pop(L, 1);	// stack: tabla, clave, valor
			lua_insert(L, -2);	// stack: tabla, valor, clave
			lua_pop(L, 1);	// stack: tabla, valor
			return (obj->*(p->mfunc))(L);  // call member function
		}
		else {
			// seteo el valor en la propia tabla. luego de hacer esto Lua ya no va a pasar
			// más por acá a buscar este valor
			lua_pop(L, 1);	// stack: tabla, clave, valor
			lua_rawset(L, 1);
		}
		return 0;
	}
	
	// create a new T object and push onto the Lua stack a userdata containing a pointer to T object
	static int new_T(lua_State* L) {
		lua_remove(L, 1);   // use classname:new(), instead of classname.new()
		T* obj = new T(L);  // call constructor for T objects
		int newTable = push(L, obj, true); // gc_T will delete this object
		if(s_trackingEnabled) {
			obj->KeepTrack(L);
		}

		// si me llamaron con una tabla como parámetro, copio los valores de la misma a la nueva tabla
		if(lua_gettop(L) == 2 && lua_istable(L, 1)) {
			lua_pushnil(L);
			while(lua_next(L, 1)) { // stack: tabla, clave, valor
				lua_pushvalue(L, -2);	// stack: tabla, clave, valor, clave
				lua_insert(L, -2);		// stack: tabla, clave, clave, valor
				lua_settable(L, newTable);	// stack: tabla, clave
			}
		}

		// un último paso en la creación del objeto. llamo a un método para que pueda acceder a la tabla
		// que se la va a pasar a Lua
		obj->PostConstruct(L);
		return 1;           // userdata containing pointer to T object
	}

	// garbage collection metamethod, viene con un userdata al tope del stack
	static int gc_T(lua_State* L) {
#ifdef ENABLE_TRACE
		OutputDebugString("attempting to collect object of type ");
		OutputDebugString(T::className);
		OutputDebugString("\n");
#endif
		ObjectWrapper* wrapper = static_cast<ObjectWrapper*>(lua_touserdata(L, -1));
		if(wrapper->collectable && wrapper->wrappedObject) {
#ifdef ENABLE_TRACE
			char buff[256];
			sprintf(buff, "collected %s (%p)\n", T::className, wrapper->wrappedObject);
			OutputDebugString(buff);
#endif
			delete wrapper->wrappedObject;	// call destructor for wrapped objects
		}
		return 0;
	}
	
	static int tostring_T(lua_State* L) {
		// cuidado, tanto el userdata como la tabla comparten este método
		char buff[32];
		if(lua_istable(L, 1)) {
			lua_pushnumber(L, 0);
			lua_rawget(L, 1);
			luaL_checktype(L, -1, LUA_TUSERDATA);
		}
		const T* pT = static_cast<ObjectWrapper*>(lua_touserdata(L, -1))->wrappedObject;
		sprintf(buff, "%p", pT);
		lua_pushfstring(L, "%s (%s)", T::className, buff);
		return 1;
	}

public:
	void PostConstruct(lua_State* L) {};
};

}; // fin del namespace

#endif
#ifndef __luaCppBridge_BaseObject_h__
#define __luaCppBridge_BaseObject_h__

#include "LuaIncludes.h"
#include "lcbBridgeConfig.h"

#if defined(ENABLE_TRACE)
	#include <typeinfo>
#endif

namespace LuaCppBridge {

typedef void* ReferenceKey;

/**
Un BaseObject es la clase base para los wrappers hacia Lua.
Se instancia con T (la clase a exponer) y Base (el wrapper a usar).

Por defecto, de todos los objetos creados se guarda una referencia en Lua, de forma que desde 
C++ se pueda acceder al objeto Lua y operar sobre él.
Si no se quiere que una determinada clase guarde referencias, antes de Registrarla llamar a 
'EnableTracking' con false.

*/
template <typename T, typename Base>
class BaseObject {
public:
	typedef struct { T* pT; } userdataType;
	typedef int (T::*mfp)(lua_State* L);
	typedef struct {
		const char* name;
		mfp mfunc;
	} RegType;

	static bool s_trackingEnabled;

	static void EnableTracking(bool value) {
		s_trackingEnabled = value;
	}

	// se encarga de hacer el new, el push hacia Lua y hacer el tracking
	static T* Construct(lua_State* L, bool gc = false) {
		T* newObject = new T(L);  // call constructor for T objects
		int newTable = Base::push(L, newObject, gc); // gc_T will delete this object
		if(s_trackingEnabled) {
			newObject->KeepTrack(L);
		}
		return newObject;
	}

protected:
	static int forbidden_new_T(lua_State* L) {
		luaL_error(L, "Constructing objects of type '%s' is not allowed from the Lua side", T::className);
		return 1;
	}

	// member function dispatcher
	static int thunk_methods(lua_State* L) {
		// stack has userdata, followed by method args
		T* obj = Base::check(L, 1);  // get 'self', or if you prefer, 'this'
		//lua_remove(L, 1);  // remove self so member function args start at index 1
		// get member function from upvalue
		RegType* l = static_cast<RegType*>(lua_touserdata(L, lua_upvalueindex(1)));
		return (obj->*(l->mfunc))(L);  // call member function
	}

	// garbage collection metamethod, viene con un userdata al tope del stack
	static int gc_T(lua_State* L) {
#ifdef ENABLE_TRACE
		OutputDebugString("attempting to collect object of type ");
		OutputDebugString(T::className);
		OutputDebugString("\n");
#endif
		if(luaL_getmetafield(L, 1, "do not trash")) {
			lua_pushvalue(L, 1);  // dup userdata
			lua_gettable(L, -2);
			if(!lua_isnil(L, -1)) {
				return 0;  // do not delete object
			}
		}
		const T* pT = *(static_cast<const T**>(lua_touserdata(L, -1)));
#ifdef ENABLE_TRACE
			char buff[256];
			sprintf(buff, "collected %s (%p)\n", T::className, pT);
			OutputDebugString(buff);
#endif
		if(pT) delete pT; // call destructor for T objects
		return 0;
	}

	static void set(lua_State* L, int table_index, const char* key) {
		lua_pushstring(L, key);
		lua_insert(L, -2);  // swap value and key
		lua_settable(L, table_index);
	}

	static void weaktable(lua_State* L, const char* mode) {
		lua_newtable(L);
		lua_pushvalue(L, -1);  // table is its own metatable
		lua_setmetatable(L, -2);
		lua_pushliteral(L, "__mode");
		lua_pushstring(L, mode);
		lua_settable(L, -3);   // metatable.__mode = mode
	}

	// a la tabla que está en 'tindex' le agrega una subtabla weak (según mode) de nombre 'name' 
	// y la retorna en el stack.
	static void subtable(lua_State* L, int tindex, const char* name, const char* mode) {
		lua_pushstring(L, name);
		lua_gettable(L, tindex);
		if (lua_isnil(L, -1)) {
#ifdef ENABLE_TRACE
			char buffer[128];
			const char* typeName = typeid(T).name();
			sprintf(buffer, "creating new subtable (%s) with mode '%s' for '%s'\n", name, mode, typeName);
			OutputDebugString(buffer);
#endif
			lua_pop(L, 1);
			lua_checkstack(L, 3);
			weaktable(L, mode);
			lua_pushstring(L, name);
			lua_pushvalue(L, -2);
			lua_settable(L, tindex);
		}
	}

	// se fija en la tabla al tope del stack si existe algún elemento de clave 'key'
	// si no, crea un userdata nuevo y lo agrega con dicha clave.
	// Deja el userdata al tope del stack y lo devuelve.
	static void* pushuserdata(lua_State* L, void* key, size_t sz) {
		void* ud = 0;
		lua_pushlightuserdata(L, key);
		lua_gettable(L, -2);     // lookup[key]
		if(lua_isnil(L, -1)) {
			lua_pop(L, 1);         // drop nil
			lua_checkstack(L, 3);
			ud = lua_newuserdata(L, sz);  // create new userdata
			lua_pushlightuserdata(L, key);
			lua_pushvalue(L, -2);  // dup userdata
			lua_settable(L, -4);   // lookup[key] = userdata
		}
		return ud;
	}

	// se fija en la tabla al tope del stack si existe algún elemento de clave 'key'
	// si no, crea una tabla nueva y la agrega con dicha clave.
	// Deja la tabla al tope del stack y devuelve su índice.
	static int pushtable(lua_State* L, void* key) {
		lua_pushlightuserdata(L, key);
		lua_gettable(L, -2);
		if(lua_isnil(L, -1)) { // si no está en la tabla
			lua_pop(L, 1);
			lua_checkstack(L, 3);
			lua_newtable(L);
			lua_pushlightuserdata(L, key);
			lua_pushvalue(L, -2);	// me copio la tabla
			lua_settable(L, -4);	// lookup[key] = table
		}
		return lua_gettop(L);
	}

public:
	mutable ReferenceKey m_selfReference;
	void KeepTrack(lua_State* L) const {
		m_selfReference = (ReferenceKey)this;
		lua_getfield(L, g_luaCppBridge_config.trackingIndex, g_luaCppBridge_config.trackingName);	// stack-> self, instances
		lua_pushlightuserdata(L, m_selfReference);	 // uso a 'this' como clave // stack-> self, instances, clave
		lua_pushvalue(L, -3);	// stack-> self, instances, clave, self
		lua_settable(L, -3);	// stack-> self, instances
		lua_pop(L, 1);			// stack-> self
	}

	ReferenceKey GetSelfReference() const {
		return m_selfReference;
	}

	//////////////////////////////////////////////////////////////////////////
	/// Busca la tabla asociada a una instancia de la clase
	void GetSelf(lua_State* L) {
		if(!s_trackingEnabled) {
			luaL_error(L, "class %s is not being tracked", T::className);
		}
		lua_getfield(L, g_luaCppBridge_config.trackingIndex, g_luaCppBridge_config.trackingName);
		lua_assert(lua_istable(L, -1));
		lua_pushlightuserdata(L, m_selfReference);
		lua_gettable(L, -2);
		if(lua_isnil(L, -1)) {
			luaL_error(L, "'%p' has no bound userdata or table", m_selfReference);
		}
		lua_remove(L, -2);	// saco la tabla de instancias
		// me quedo con la tabla (o userdata) asociada con this en el tope del stack
	}
};

template <typename T, typename Base>
bool BaseObject<T, Base>::s_trackingEnabled = true;

//////////////////////////////////////////////////////////////////////////
/// Busca la tabla asociada a una clave y la deja en el stack
static void GetReference(lua_State* L, ReferenceKey key) {
	//char buffer[128];
	//const char* n = typeid(T).name();
	//sprintf(buffer, "retrieving reference to '%p' as type '%s'\n", key, n);
	//OutputDebugString(buffer);
	lua_getfield(L, g_luaCppBridge_config.trackingIndex, g_luaCppBridge_config.trackingName);
	lua_pushlightuserdata(L, key);
	lua_gettable(L, -2);
	if(lua_isnil(L, -1)) {
		/*while(lua_next(L, -2) != 0) {
			char clave[256];
			char valor[256];
			switch(lua_type(L, -2)) {
				case LUA_TSTRING:
					strcpy(clave, lua_tostring(L, -2));
				break;
				case LUA_TNUMBER:
					sprintf(clave, "%d", (int)lua_tonumber(L, -2));
					//itoa((int)lua_tonumber(L, -2), clave, 10);
				break;
				case LUA_TTABLE:
					sprintf(clave, "table: (%p)", lua_topointer(L, -2));
				break;
				case LUA_TUSERDATA:
					sprintf(clave, "userdata: (%p)", lua_topointer(L, -2));
				break;
				case LUA_TLIGHTUSERDATA:
					sprintf(clave, "lightuserdata: (%p)", lua_topointer(L, -2));
				break;
				default:
					strcpy(clave, lua_typename(L, lua_type(L, -2)));
				break;
			}
			switch(lua_type(L, -1)) {
				case LUA_TSTRING:
					strcpy(valor, lua_tostring(L, -1));
				break;
				case LUA_TNUMBER:
					sprintf(valor, "%d", (int)lua_tonumber(L, -1));
					//itoa((int)lua_tonumber(L, -1), valor, 10);
				break;
				case LUA_TTABLE:
					sprintf(valor, "table: (%p)", lua_topointer(L, -1));
				break;
				case LUA_TUSERDATA:
					sprintf(valor, "userdata: (%p)", lua_topointer(L, -1));
				break;
				case LUA_TLIGHTUSERDATA:
					sprintf(valor, "lightuserdata: (%p)", lua_topointer(L, -1));
				break;
				default:
					strcpy(valor, lua_typename(L, lua_type(L, -1)));
				break;
			}
			sprintf(buffer, "'%s', '%s'\n", clave, valor);
			OutputDebugString(buffer);
			lua_pop(L, 1);
		}*/
		luaL_error(L, "'%p' has no bound userdata or table", key);
	}
	lua_remove(L, -2);	// saco la tabla de instancias
	// me quedo con la tabla (o userdata) asociada con this en el tope del stack
}


}; // fin del namespace

#endif