lua-users home
lua-l archive

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


It was thus said that the Great Charles Melice once stated:
> I defined (with the C API) a Vector object based on full user data.
> 
> 	static Vec4 *Vget(lua_State *L, int i)
> 	{
> 		if (luaL_checkudata(L,i,MYTYPE)==NULL) ERROR(MYTYPE);
> 		return (Vec4*)lua_touserdata(L,i);
> 	}
> 
> Now, I'm interested to be able to index the 'x, y, z, t' coordinates with
> numeric indexes (1,2,3,4). For instance :
> 
> 	local v = Vec4(11,22,33,44)
> 	print(v.y) -- 22
> 	v:normalize()
> 	print(v) -- Vec4(0.27, 0.53, 0.80, 1.07)
> 	print(v[2]) -- 0.53 ???
> 
> 	v[2]=22.22 -- ???
> 	print(v.y) -- 22.22
> 
> I don't see how to do that ?

  First, you need to create a metatable to associate with your userdata. 
When you register the module, you create the metatable:

	#define MY_VEC4_TYPE	"Some String to denote a Vec4"
	
	/*----------------------------------------------------------------
	; I'm not sure how you actually define this, but this is a sample
	; anyway.
	;-----------------------------------------------------------------*/
	
	typedef struct
	{
	  double data[4];
	} Vec4;
	
	/*--------------------------------------------------------------------
	; This function will handle references in an expression.  The naming
	; convention I use is <modname>meta_<method>, so for instance, the
	; Lua method __index, for this example, will end up being named
	; vec4meta___index().  You don't have to follow this convention, but
	; I find it nice.
	;
	; Also, luaL_checkudata() will throw an error if the type is
	; incorrect, so you don't need to check the return code.
	;---------------------------------------------------------------------*/
	
	static int vec4meta___index(lua_State *L)
	{
	  Vec4 *vec = luaL_checkudata(L,1,MY_VEC_TYPE);
	  int   idx;
	  
	  if (lua_type(L,2) == LUA_TSTRING)
	  {
	    const char *name = lua_tostring(L,2);
	    if (strcmp(name,"x") == 0)
	      idx = 0;
	    else if (strcmp(name,"y") == 0)
	      idx = 1;
	    else if (strcmp(name,"z") == 0)
	      idx = 2;
	    else if (strcmp(name,"t") == 0)
	      idx = 3;
	    else
	      idx = -1;
	  }
	  else if (lua_type(L,2) == LUA_TNUMBER)
	    idx = lua_tointeger(L,2);
	  else
	    idx = -1;
	    
	  if ((idx < 0) || (idx > 3))
	    lua_pushnil(L);
	  else
	    lua_pushnumber(L,vec->data[idx]);
	  return 1;
	}
	
	/*------------------------------------------------------------------
	; This function will handle assignments.  It looks quite a bit like
	; the __index function.
	;-----------------------------------------------------------------*/

	static int vec4meta___newindex(lua_State *L)
	{
	  Vec4 *vec = luaL_checkudata(L,1,MY_VEC_TYPE);
	  int   idx;
	  
	  if (lua_type(L,2) == LUA_TSTRING)
	  {
	    const char *name = lua_tostring(L,2);
	    if (strcmp(name,"x") == 0)
	      idx = 0;
	    else if (strcmp(name,"y") == 0)
	      idx = 1;
	    else if (strcmp(name,"z") == 0)
	      idx = 2;
	    else if (strcmp(name,"t") == 0)
	      idx = 3;
	    else
	      idx = -1;
	  }
	  else if (lua_type(L,2) == LUA_TNUMBER)
	    idx = lua_tointeger(L,2);
	  else
	    idx = -1;
	  
	  if ((idx >= 0) && (idx <= 3))
	    vec->data[idx] = luaL_checknumber(L,3);
	  return 0;
	}
	
	static const luaL_Reg vec4meta[] =
	{
	  { "__index"		, vec4meta___index	},
	  { "__newindex"	, vec4meta___newindex	},
	  { NULL		, NULL			}
	};
	
	int luaopen_vec4(lua_State *L)	/* rename as appropriate */
	{
	  luaL_newmetatable(L,MY_VEC4_TYPE);
	#if LUA_VERSION_NUM == 501
	  /* Lua 5.1 */	
	  luaL_register(L,NULL,vec4meta);
	#else
	  /* Lua 5.2 or 5.3 */
	  luaL_setfuncs(L,vec4meta,0);
	#endif
	  /* rest of initialization */
	}

  Then, when you create a new userdata, you associate the metatable you
created with it:	

	Vec4* vec = lua_newuserdata(L,sizeof(Vec4));
	luaL_getmetatable(L,MY_VEC4_TYPE);
	lua_setmetatable(L,-2);
	
  And that's pretty much it.

  -spc