I'm using Love2d and trying to get libtcod to go along with it, so I'm wrapping its functions in Nim (with libtcod_nim). But there's two functions in the pathfinding API that require a callback. This might be straightforward if I was just using libtcod on its own with Nim, but in this case the callback will come in as a lua function, and it should be called from lua's C api.
The functions are pathNewUsingProcedure and dijkstraNewUsingProcedure. They both require a PathProc callback. (They're all in the same file, at lines 45, 101 and 31, respectively.)
31 type PathProc* = proc(xFrom, yFrom, xTo, yTo: cint; userData: pointer): cfloat {.cdecl.}
I know how to call a lua function from the C api, and I know I can't just keep a lua function in a variable, but I don't know lua's C api well enough for this.
My current incomplete code looks like this:
proc tcod_path_new_using_function*(L:PState):cint {.cdecl.} =
if L.isfunction(3):
let width = L.checkinteger(1)
let height = L.checkinteger(2)
let diagonal_cost = L.optnumber(5, 1.41)
let path_func:PathProc = ???
let user_data:pointer = ???
let path = pathNewUsingProcedure(width, height, path_func, user_data, diagonal_cost)
let path_id = next_path_id()
paths[path_id] = path
L.pushstring(path_id) # send id handler to lua
return 1
So the issue is whether it is possible to create a PathProc that has access to the lua state and calls that lua function with its arguments.
I'm not sure if that user_data pointer isn't also an issue, as I can't figure out what it's for. I'm considering the option of just leaving those two functions out entirely.
I tried something that I thought might work, but it doesn't. I'm not being able to capture the lua state in the PathProc.
_path.nim(81, 17) Error: illegal capture 'L' because ':anonymous'
has the calling convention: <cdecl>
I annotated line 81 below:
#===============================================================================
# Lua call params:
# 1 width : number
# 2 height : number
# 3 path_func : function
# 4 user_data = 0 : ???
# 5 diagonalCost = 1.41 : number
#===============================================================================
proc tcod_path_new_using_function*(L:PState):cint {.cdecl.} =
if L.isfunction(3):
let width = L.checkinteger(1)
let height = L.checkinteger(2)
let diagonal_cost = L.optnumber(5, 1.41)
var dummy_user_data = 0 # this isn't used, just fills the parameter
L.pushvalue(3)
var fref = L.reference(REGISTRYINDEX)
let path_func:PathProc =
proc(from_x, from_y, to_x, to_y: cint; user_data: pointer): cfloat {.cdecl.} =
81 --> L.getref(fref)
L.pushnumber(from_x)
L.pushnumber(from_y)
L.pushnumber(to_x)
L.pushnumber(to_y)
L.pushvalue(4) # user_data is retrived here
L.call(5, 0)
let path = pathNewUsingProcedure(width, height, path_func, dummy_user_data.addr, diagonal_cost)
L.unref(fref)
let path_id = next_path_id()
paths[path_id] = path
L.pushstring(path_id) # send 'path_id' handler to lua
return 1