Sean
Sean

Reputation: 3045

How do I pass Haskell data through a C FFI as an opaque data type?

I'm trying to pass some data through a C library that doesn't read or modify that data.

foreign import ccall "lua.h lua_pushlightuserdata"
  c_lua_pushlightuserdata :: LuaState -> Ptr a -> IO ()

foreign import ccall "lua.h lua_touserdata"
  c_lua_touserdata :: LuaState -> CInt -> IO (Ptr a)

data MyData =
  MyData
  { dataIds = TVar [Int]
  , dataSomethingElse = [String]
  }

calledFromRunLuaState :: LuaState -> IO ()
calledFromRunLuaState luaState = do
  dataPtr <- c_lua_touserdata luaState (-1)
  myData <- dataFromPtr dataPtr
  doSomethingWith myData

main = do
  luaState <- Lua.newstate
  ids <- atomically $ newTVar []
  c_lua_pushlightuserdata luaState (dataToPtr (MyData ids []))
  runLuaState luaState

I'm trying to figure out how to define dataFromPtr and dataToPtr.

Upvotes: 4

Views: 241

Answers (1)

redneb
redneb

Reputation: 23920

This is what StablePtr is for. So you have to use newStablePtr in place of your dataFromPtr and deRefStablePtr in place of your dataToPtr. Note that deRefStablePtr operates in the IO monad, so you would have to ajust your code accordingly. Also, you would have to adjust the foreign imports to use stable pointers, e.g.:

foreign import ccall "lua.h lua_pushlightuserdata"
  c_lua_pushlightuserdata :: LuaState -> StablePtr MyData -> IO ()

and similarly for lua_touserdata.

Finally, when you create a stable pointer with newStablePtr, the garbage collector will not deallocate that value automatically. So it is your responsibility to deallocate it by calling freeStablePtr.

Upvotes: 4

Related Questions