Haskell FFI如何获取局部值的地址

5

我尝试使用Haskell调用这个简单的C代码:

time_t rawTime = time( NULL );
struct tm* timeInfo = localtime( &rawTime );  /* Using address of a local variable */
printf( "The current date is: %s", asctime( timeInfo ) );

所以我写道:
data CTmStruct = CTmStruct
type CTmStructPtr = Ptr CTmStruct

foreign import ccall "time"      c_time      :: Ptr CTime    -> IO CTime
foreign import ccall "localtime" c_localtime :: Ptr CTime    -> IO CTmStructPtr
foreign import ccall "asctime"   c_asctime   :: CTmStructPtr -> CString

main :: IO ()
main = do
    rawTime <- c_time nullPtr
    tmStructPtr <- c_localtime ???  -- How to get an address of rawTime?
    print $ c_asctime tmStructPtr

那么我如何获取本地值rawTime的地址,以便将其作为c_localtime函数的参数?我已经阅读了Foreign模块的文档,但是没有找到这个问题的答案。非常感谢任何帮助。


我认为在 FFI 界限中没有办法传递 C 结构体。请传递指针,如果需要访问结构体中的字段,则需要编写执行此操作的 C 函数。这是为了让 GHC 不必了解您的 C 编译器为结构体选择的存储布局和对齐方式。 - Daniel Wagner
1个回答

4

我能够做到:

import Foreign.C.String
import Foreign.Marshal.Alloc

-- Your declarations and foreign bindings here

getTime :: IO String
getTime = do
    p <- malloc
    c_time p
    tmPtr <- c_localtime p
    str <- peekCString $ c_asctime tmPtr
    free p
    return str

main :: IO ()
main = do
    currentTime <- getTime
    putStrLn currentTime

关键在于使用mallocfree,你需要显式地分配空间,将其传递给c_time以设置内容,然后使用c_localtime将其格式化为CTmStruct,再将其传递给c_asctime转换为CString,使用peekCString将其转换回Haskell的String,最后释放用于获取时间的指针。
我想我早该意识到这一点了,更好的解决方案是使用alloca
getTime :: IO String
getTime = alloca $ \p -> do
    c_time p
    tm <- c_localtime p
    peekCString $ c_astime tm

哪种更安全,更短,更易于使用。 alloca 函数会为您分配和释放指针,您只需使用它即可。

非常感谢!我已经了解了alloca,但是你的例子让我明白了如何使用它。 - Denis Shevchenko

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接