Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

I know there are examples of creating pointers using the LuaJIT FFI, but most of these aren't pointed to existing data. One such example of this is here: How to pass a pointer to LuaJIT ffi to be used as out argument?

One thing that I haven't been able to successfully do is create a pointer to an existing value. In order to have a pointer type as far as I know, I have to know that I want to have a pointer point to it at some point in the future, as in:

local vao = ffi.new("GLuint[1]")
gl.GenVertexArrays(1, vao)
gl.BindVertexArray(vao[0])

Here, I know that glGenVertexArrays needs a pointer to vao, so I specify it as a GLuint[1]. In C, I would be doing something like the following:

GLuint vao;
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);

Here, I have no idea that I'll be needing a pointer to vao, so I can just specify it normally.

In other words, is there a way to get the address of, or create a pointer to, an existing value? Do I have to foresee what I'll be doing with the value before I create it?

Thanks!

like image 836
Lucien Greathouse Avatar asked Mar 19 '23 09:03

Lucien Greathouse


1 Answers

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
like image 140
gilbo Avatar answered Apr 26 '23 15:04

gilbo