lua-users home
lua-l archive

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


Am 19.08.2015 um 09:53 schröbte Tim Hill:

I’m not clear what your actual problem is, as you seem to be
suggesting a solution without really saying what the underlying
problem you are trying to solve is. What I *think* you are saying is
that you wish to expose two distinct userdata values that have the
same lifetime from a GC standpoint, but I’m not clear why you need to
do this.

Imagine you have a struct type in C that you want to manipulate from Lua as a userdata:

    typedef struct {
      /* contents not really important here */
    } A;

The C library can give you an object of that type in many different ways:

1. The "usual" way: You allocate the memory yourself (via `lua_newuserdata` in this case) and the C library provides you with an "A_init" and an "A_clear" function that you call at appropriate times.

2. As a pointer: The constructor C function (let's call it "A_new") allocates the necessary memory itself and handles you an `A*`. "A_free" takes that pointer and destroys the object. You could have multiple "A_new"/"A_free" pairs (e.g. the `FILE*` type in the Lua io library).

3. As a preexisting C object: It could be a global variable in C or just an object that outlives the Lua state. This is similar to 2. in that you put a pointer in your userdata, but you don't have constructor/destructor functions (and if you store the destructor in the userdata, this case can be handled like 2. using a NULL pointer as the destructor).

4.  As part of another userdata:
    typedef struct {
      A a; /* could also be `A* a` */
    } B;
This is similar to 3., but the lifetime of the A pointer depends on the lifetime of the B userdata: The B userdata must not be collected as long as the A userdata is still in use. You don't need constructors/destructors, because the B userdata already handles this. This can get complicated if you bind a function to Lua that may indirectly invalidate the A userdata (e.g. if B is a tagged union and changes type, or via a "close"-style function for B): Then all further accesses to the A userdata should throw an error.

You could have all four cases *for one type* *in the same library*, and you want to treat all those A userdata the same (only one metatable). The current proposal tries to address the fourth case (one object embedded in another), but ...

*   It doesn't handle cases 2 and 3.
* It doesn't handle embedding via pointer (the offset approach fails one level down).
*   It doesn't handle invalidation of the container userdata.
* Just an offset is not enough (may be the same in unions anyway), you also need different metatables, which means that you'll have extra memory allocation for the embedded userdata anyway, because you can't fit the handle, the offset, and the metatable pointer in one TValue.


—Tim


Philipp