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 JeanHeyd Meneide once stated:
> Dear Mr. Conner,
> 
>      Apologies for it taking a while for me to get back to you. Here is
> what I am trying to accomplish. Given a C++ type, such as:
> 
>         struct Person
>         {
>           std::string name;
>           int age;
>           bool hired;
> 
>           Person(std::string name, int age) : name(name), age(age), hired(false) {}
> 
>           void hire() { hired = true; }
>           void fire() { hired = false; }
>           bool isHired() const { return hired; }
>           int getAge() const { return age; }
>           std::string getName() const { return name; }
>         };
> 
> (Note the change from what you wrote in that all fields are public). I am
> writing code that creates a class-like that is accessible in lua as so:
> 
>      local person = Person.new("Jay", 24)
>      person:hire() -- call member functions through this syntax
>      assert(person:isHired())
>      assert(person.hired) -- also be able to get/set member variables through the obj.variable_name syntax
>      person.age = 25 -- you can set variables this way too

  Okay, I see what you are trying to do.  First question, if all the fields
are public, why even have getter/setter functions?  I thought the whole
purpose of getter/setter functions was to avoid having public fields and
have more control over the data (i.e. the setter function can validate the
data, etc).  But I digress ... getting on to your problem ... 

	[ various approaches that don't work snipped ]

> Note that this second method, specifically at [2], results in MUCH less
> overhead (and is thusly much faster: refer to the benchmarks I sent
> earlier). However, this presents a distinct problem: it is impossible to
> bind the C++ userdata's member variables to the "print( obj.foo_var )"
> syntax I desire. The issue I was describing at the very beginning of this
> thread was my attempt to make the "Table __index" approach work for these
> member variables. My initial implementation to make that happen was a
> modification of the above steps, as follows (this approach does NOT work
> for member variables):
> 
>      - Make a userdata and bind a metatable to it
>      - Make a plain table with the necessary C functions bound to it
>      - Set the plain table to the Metatable's __index method
>      [ Additions start here ]
>      - Create another metatable table. Set it as the 1st plain table's metatable
>      - Set this inner metatable's __index function to a C function that can look up member variables
>           - Now, we have a chain of __index calls
>      - Call obj:my_func()
>      - Lua VM finds obj:my_func() on the metatable's __index table
>           - lua calls it without accessing a C function for lookup
>      - Access obj.foo_var
>           - Lookup fails on on first __index table, checks it for an __index value, finds the C function
>           - Calls the C function, but calls it with the first plain table and the key "foo_var"
>           - [ Problem I was asking about in original post ] Cannot find a way to get to original "userdata", fail
> 
> As you can see, because we chain __index tables/functions, the arguments we
> get are not (userdata, "foo_var"), but (some table, "foo_var"). The "C
> Function for __index" works because the lookup fails with the userdata
> value (so I can always reference the userdata). The modified "Table
> __index" approach fails the lookup with
> (table_nobody_cares_about_that's_just_for_indexing_and_is_not_specific_to_userdata,
> "foo_var").
> 
>  I am wondering if there is a way around this?

  Okay, how about:

	* make a userdata and bind a metatable (MTu) to it
	* make a plain table (PT) with the C functions in it
	* set a field in PT (say, '_@private') that contains the userdata
	* set PT to MTu[__index]

	* create another metatable (MTt) and bind it to PT
	* set MTt[__index] to function (more in a bit)

  On obj:method(), Lua wil use PT to look up the function and run it, which
is what you want.  

  On obj.field, Lua will use the PT to look up the field, not find it, find
PT's metatable MTu, find that __index function, and call it.  That function
will get PT and field as keys.

	static int PT_MTu___index(lua_State *L)
	{
	  if (lua_getfield(L,1,"_@private") != LUA_TUSERDATA)
	    error();
	  obj* o = luaL_getuserdata(L,-1,TYPE_OBJ);
	  const char *key = lua_tostring(L,2);

	  /* rest of code ... */
	}

  Would something like this work?

  -spc