lua-users home
lua-l archive

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


On Nov 30, 2007 12:02 AM, John Dunn <John_Dunn@qscaudio.com> wrote:
[...]
> I haven't had any luck attempting to do the above. My object is userdata
> and if I attempt to access a field on the userdata with the '.' it
> errors with the following -
>
>         error : [string "Controls.Inputs[1].value = 1111"]:1: attempt to
> index field '?' (a userdata value)
>
> I assume there is some issue with the way I'm registering my tables but
> I haven't figured out exactly what the problem is. My code is below - if
> you have any suggestions they'd be much appreciated.

[...]

>   // this doesn't work
>   check_status( L, luaL_dostring( L, "print( MyObject:Value )" ));

No, because it is not valid syntax. The colon operator corresponds to
method invocation. The following equivalence holds:

 object:method(...) <--> object.method(object, ...) -- object passed
as self argument

I modified your code into a working example:

print(myObject:getValue()) --> 1804289383
myObject:setValue(1234); print(myObject:getValue()) --> 1234
print(myObject.value) --> 1234
myObject.value = 3333; print(myObject.value) --> 3333

The metatable is created by the following function:

/////////////////////////////////////////////////////////
static const struct luaL_reg MyObjectInstanceMethods[] =
{
 { "getValue", getValue },
 { "setValue", setValue },
 {NULL,NULL}
};

void create_metatable( lua_State* L )
{
 // setup the metatable
 const char* setup_mt =

   " local mt, methods = ...      \n"

   " function mt:__index(k)       \n"
   "   local m = methods[k]       \n"
   "   if m then return m end     \n"
   "   if k == 'value' then       \n"
   "     return self:getValue()   \n"
   "   end                        \n"
   " end                          \n"

   " function mt:__newindex(k, v) \n"
   "   if k == 'value' then       \n"
   "     self:setValue(v)         \n"
   "   end                        \n"
   " end                          \n";

 assert(luaL_loadstring(L, setup_mt) == 0);       // [chunk

 // register metatable for MyObject
 luaL_newmetatable(L, MyObjectID );               // [chunk mt

 lua_newtable(L);                                 // [chunk mt methods
 luaL_register(L, NULL, MyObjectInstanceMethods );

 assert(lua_pcall(L, 2, 0, 0) == 0);              // [
}
/////////////////////////////////////////////////////////

I wrote the metamethods in Lua since it's easier to do, read, and
discuss (people with a C background tend to overuse the C API, writing
tedious code for tasks easily dealt with in pure Lua).

When you do have to use the API, it's easy to get things wrong. That
is why I strongly recommend you annotate every operation with the
relevant part of the stack, like I did in this code (the top item is
the rightmost one in my convention).

Be sure to read Programming In Lua chapter 13 on metatables. You may
also check the examples in chapter 28 on the C API, but like I said
most of the stuff can be expressed in pure Lua.

You will find the complete code as an attachment.

Cheers,

-- 
-- Thomas
#include <iostream>

extern "C" {
  #include <lauxlib.h>
  #include <lualib.h>
  #include <lua.h>
}

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

struct MyObject
{
 double value;
 MyObject() : value( rand() )
 {
 }
 double get_value() { return value; }
 void set_value( double d ) { value = d; }
};

static int getValue( lua_State* L )
{
 MyObject* obj = *(MyObject**)luaL_checkudata(L,1, MyObjectID);;
 lua_pushnumber(L,obj->value);
 return 1;
}

static int setValue( lua_State* L )
{
 MyObject* obj = *(MyObject**)luaL_checkudata(L,1, MyObjectID);;
 obj->value = luaL_checknumber(L,2);
 return 1;
}

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;
 }
}

void exec(lua_State* L, const char* stat)
{
  std::cout << stat << " --> ";
  check_status(L, luaL_dostring(L, stat));
}

static const struct luaL_reg MyObjectInstanceMethods[] =
{
 { "getValue", getValue },
 { "setValue", setValue },
 {NULL,NULL}
};


void create_metatable( lua_State* L )
{
 // setup the metatable
 const char* setup_mt = 

   " local mt, methods = ...      \n"

   " function mt:__index(k)       \n"
   "   local m = methods[k]       \n"
   "   if m then return m end     \n"
   "   if k == 'value' then       \n"
   "     return self:getValue()   \n"
   "   end                        \n"
   " end                          \n"

   " function mt:__newindex(k, v) \n"
   "   if k == 'value' then       \n"
   "     self:setValue(v)         \n"
   "   end                        \n"
   " end                          \n";

 assert(luaL_loadstring(L, setup_mt) == 0);       // [chunk mt

 // register metatable for MyObject
 luaL_newmetatable(L, MyObjectID );               // [chunk mt

 lua_newtable(L);                                 // [chunk mt methods
 luaL_register(L, NULL, MyObjectInstanceMethods );
 
 assert(lua_pcall(L, 2, 0, 0) == 0);              // [
}


int main( int argc, char** argv ) {
 lua_State* L = luaL_newstate();
 luaL_openlibs(L);


 // add an object
 MyObject* o = new MyObject();

 // expose as userdata
 void* u = lua_newuserdata(L, sizeof(MyObject*)); // [u
 *static_cast<MyObject**>(u) = o;

 // set metatable
 create_metatable(L);               
 luaL_getmetatable(L,MyObjectID);  // [u mt
 lua_setmetatable(L,-2);           // [u
 lua_setglobal(L, "myObject");     // [

 // these work
 exec(L, "print(myObject:getValue())");
 exec(L, "myObject:setValue(1234); print(myObject:getValue())");

 exec(L, "print(myObject.value)");
 exec(L, "myObject.value = 3333; print(myObject.value)");

 // cleanup
 delete o;
 lua_close(L);

 return 0;
}