not logged in | [Login]
When you have a C callback that uses pass by value semantics for structs, LuaJIT FFI cannot handle this case directly, but it can indirectly. When a function is called that has pass by value structs, the structs are placed on the stack and the function receives the stack addresses to the structs. With this in mind, it is possible to redefine the function so that the parameters of function are pointers to the struct, then in the callback dereference the pointers to get the struct values. Keep in mind that this data is on the stack and will go out of scope at the end of the function.
In the Clang library, the clang_visitChildren function accepts a callback that is defined as:
typedef enum CXChildVisitResult (*CXCursorVisitor)(CXCursor cursor, CXCursor parent, CXClientData client_data);
Note that the CXCursor parameters are passed in by value, and not address (CXClientData is a typedef for void*). If we redefine the function as:
typedef enum CXChildVisitResult (*CXCursorVisitor_WithPointers)(CXCursor* cursor, CXCursor* parent, CXClientData client_data);
This allows you to write a lua function to be used with luaJIT's FFI that will allow us to receive pointers to those structs. For example, we want to write a lua function that returns a table with all the children of a given cursor. One way to do this is to use a function generator that allows us to set an upvalue that will be consistent between runs of the callback.
function createCursorVisitor(storage)
local ret = storage
return function(cursorPtr, parentPtr, usr)
local c = ffi.new("CXCursor", cursorPtr[0]) --create a copy from the passed in pointer
local cursor = createCursor(c) --create a lua version
table.insert(ret, cursor) --add to the list children to be returned at the end
return lib.CXChildVisit_Continue
end
end
This lua function accepts a table and sets it as the upvalue for a returned anonymous function. This returned function is the function we will use for our callback. In this function we take the pointer to the child cursor and then create a copy of it and saves it in our upvalue table for later. We have to have a copy because the value pointed to by cursorPtr will go out of scope when this function returns.
To see how we use this callback we need to create our table to contain the children, then create the callback function with the function creator. Then we cast the lua function to a callback and call the clang_visitChildren function with our callback.
function clang_cursor.children(self)
local cptr = self.cursor
local ret = {} --create a table to hold all the children
local cursorVisitor = createCursorVisitor(ret) --generate a function that uses our tables an upvalue
local cb = ffi.cast("CXCursorVisitor_WithPointers", cursorVisitor) --this will use our redefined function definition
lib.clang_visitChildren(cptr, cb, nil)
cb:free() --release the callback object
return ret
end
When the clang_visitChildren returns, our table will contain all the children of the passed in cursor. We then clean up our callback and return our table of children cursors.