How can I create a pointer to existing data using the LuaJIT FFI?

孤街浪徒 提交于 2019-12-01 18:58:00

There's no way to get a pointer to a cdata object in the FFI.

I remember reading in the LuaJIT mailing list that this was done intentionally for some optimizations to work, though I can't find the exact message in the archive.

So far, I haven't had a need for getting the pointer of a cdata object; LuaJIT refers to cdata by reference (similar to tables) and the type[1] trick works for out parameters.

The reason that you can't get the address of a cdata object is that all cdata objects are garbage collected. If you stop and think that through to its logical conclusion, you'll see this means that they must be allocated on the Lua Heap, rather than the general C heap that malloc would use. It's highly unsafe to just return pointers into the Lua heap because the garbage collector could come along and move the object at any time.

One consequence of this is that you absolutely should not do what rraallvv suggested, since you're likely to cause a seg-fault.

When you call ffi.new("GLuint[1]") you allocate an array of GLuints on the Lua heap (LuaJIT calls this a 'reference' type) This is ok because when you call into GenVertexArrays() (1) the GC cannot run, since you're busy executing C-code, (2) GenVertexArrays() doesn't retain the pointer, so you don't have to worry about a stale pointer being accessed later.

However, LuaJIT's FFI provides enough features that we can build our own allocation contraptions. The following code should (haven't tested this version exactly) allocate data on the C heap and install a default finalizer to free it. If you look up all the FFI functions used, you should get a better sense of things.

local function SafeHeapAlloc(typestr, finalizer)
  -- use free as the default finalizer
  if not finalizer then finalizer = ffi.C.free end

  -- automatically construct the pointer type from the base type
  local ptr_typestr = ffi.typeof("$ *", typestr)

  -- how many bytes to allocate?
  local typesize    = ffi.sizeof(typestr)

  -- do the allocation and cast the pointer result
  local ptr = ffi.cast(ptr_typestr, ffi.C.malloc(typesize))

  -- install the finalizer
  ffi.gc( ptr, finalizer )

  return ptr
end

I was able to do that with a C function that copies the cdata pointer like this

void cdataToPointer(void *cdata, void **pointer) {
    *pointer = cdata;
}

// ...

void *mycdata = NULL;

lua_pushlightuserdata(L, &mycdata);
lua_setglobal(L, "__TEMP_USERDATA__");

luaL_dostring(L,
     "local ffi = require'ffi'\n"
     "ffi.cdef[[\n"
     "  void cdataToPointer(void *cdata, void **pointer);\n"
     "]]\n"
     "ffi.C.cdataToPointer(mycdata, __TEMP_USERDATA__)\n");
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!