Re: metatype __gc inconsistency

  • From: Wolfgang Pupp <wolfgang.pupp@xxxxxxxxx>
  • To: luajit@xxxxxxxxxxxxx
  • Date: Fri, 19 Sep 2014 00:10:29 +0200

If malloc actually returned a struct, the __gc metamethod of the
corresponding type would not be called because you never "created" the
instance (malloc did), and the __gc function is only used for an "implicit
ffi.gc call during creation of an instance".

This makes sense, because objects created outside of ffi.new usually need
custom GC behavior anyway (malloc'd pointers need freeing, objects obtained
from some C-API usually need to be passed to a special free-function from
said API, etc.). Blindly invoking __gc functions for structs obtained from
C functions would probably be disastrous more often than not.

Note that passing/returning structs by value currently prevents JIT
compilation of surrounding code and is seldomly seen in C-APIs anyway.

If you want to use malloc instead of ffi.new, it is IMHO best to keep
your object as pointer, because you *need* the pointer when free'ing the
object, and there is no "ffi.addressof" (by design).
Dereferencing mallocs return value immediately is just not worth it IMO.

----------------------------------------------------------------------------
-- Here is a simple malloc example that shows how LuaJIT deals with
-- pointers-to-structs vs structs, and what the (few) differences in
-- behavior are:
----------------------------------------------------------------------------
local ffi = require'ffi'

ffi.cdef[[
void * malloc(size_t bytes);
void free(void *ptr);
]]

local typeof, sizeof = ffi.typeof, ffi.sizeof

local malloc
do
  local libc, fill, cast, gc = ffi.C, ffi.fill, ffi.cast, ffi.gc
  malloc = function(ctype)
    local ptr = cast(typeof('$ *', ctype), libc.malloc(sizeof(ctype)))
    fill(ptr, sizeof(ctype))  --zero-fill
    gc(ptr, libc.free)
    return ptr
  end
end

local PointF = typeof'struct {double x; double y;}'
do
  local sqrt = math.sqrt
  local mt = {
    __index = {
      distance = function(a, b) return sqrt((a.x-b.x)^2 + (a.y-b.y)^2) end,
    },
  }
  ffi.metatype(PointF, mt)
end

local p = PointF(1, 1)
local p2 = malloc(PointF)

print(p:distance{x=2, y=2})

-- LuaJIT dereferences the pointer for us.
-- C/C++ would force us to write "p2->distance" here.
print(p2:distance{x=2, y=2})

print(sizeof(p), sizeof(p2)) --> 16, <pointer size>

-- In C/C++, one would need to write "p2->x"
print(p.x, p2.x)  --> 1, 0

-- dereferencing before accessing the member only works with the pointer
print(p2[0].x) --> 0

Other related posts: