Re: opaque pointers and metatype

  • From: Richard Hundt <richardhundt@xxxxxxxxx>
  • To: luajit@xxxxxxxxxxxxx
  • Date: Mon, 03 Mar 2014 09:12:53 +0100

On 3/2/14 11:22 PM, Cosmin Apreutesei wrote:
>> I may, of course, have misunderstood your question. Happens :/
> No pb, I'm usually too terse on the first try, it's a bad habit I have.
>
> So there are APIs (like malloc, cairo), where the objects are defined
> as opaque structs, and the functions take a pointer to that, and then
> there are APIs (like pthreads, fbclient, freetype), where the objects
> are defined as pointers to opaque structs, and the functions take a
> pointer to that (so the function takes a pointer to a pointer to an
> opaque struct). The case I wanted to discuss was the later.
>
> In C, there's not much difference between the two styles, because you
> can always take the address of everything with the `&` operator,
> regardless of whether that thing is in the stack or heap or whatever.
> If it's in RAM, it has an address and you can take it with `&`.
>
> AFAIK, in ffi, there's no addr() function, though I'm not sure why,
> because print(ffi.new('double')) does show an address, and I'm pretty
> sure that, at least for for structs and arrays, that address is
> stable, which is necessary because you can take and hold pointers
> pointing inside that struct/array.
Yeah, tricky.
>
> So if I have ptr = ffi.cast('void *', some_address), I can't pass &ptr
> to a function func(void**) because there's no ffi.addr(ptr). I can
> pass ffi.new('void*[1]', ptr) though, but that allocates memory, at
> least in some circumstances (someone please correct me if I'm wrong),
> plus it's ugly :) As an alternative, I can say that ptr =
> ffi.new(void*[1], some_address) and pass ptr around, but then I can't
> apply metatype to void*[1].
Well, you can have a metatype, just not for void*[1]. Just assume that
there's a struct hiding behind that void*[1]:

local ffi = require('ffi')
ffi.cdef"typedef struct foo_ foo_t;"

ffi.metatype(ffi.typeof('foo_t'), {
   __index = {
      greet = function(self)
         print("Hey, ima:", self)
      end
   }  
})

local f = ffi.new('foo_t*[1]')
local g = f[0]
g:greet()

>
> Yet another alternative is to make a type for void* like this:
>
>    typedef {void *p;} some_type;
Yep, but that means you needs to pass `self.p` around when delegating to
C functions from a method on your metatable. Which brings me back to my
original point. You don't really need to box it.

For the address of problem, I keep a foo_t*[1] lying around as an
upvalue and just reuse that to save allocations:

local foo_p_p = ffi.new('foo_t*[1]')
function destroy(foo)
   foo_p_p[0] = foo
   ffi.C.destroy(foo_p_p)
end

Not terribly pretty, but it probably doesn't make sense to try to take
the address of a cdata tagged value living somewhere on the Lua stack.

-R

Other related posts: