可存储的空数据声明

9
我正试图为一个C库创建一个Haskell封装。底层的结构体过于复杂,难以明确表示为类型,而且我实际上只是用它们在C函数之间传递,所以我使用EmptyDataDecls让GHC为我工作。
我需要的是这些数据类型中的一个指针,但当我尝试使用alloca创建一个时,它抱怨数据不是Storable类型。例如:
{-# LANGUAGE ForeignFunctionInterface, EmptyDataDecls #-}

module Main where

import Foreign.Marshal.Alloc
import Foreign.Ptr

data Struct

foreign import ccall "header.h get_struct"
    get_struct :: Ptr Struct -> IO ()

main = alloca $ \ptr -> get_struct ptr

GHC无法编译此代码,提示没有Storable Struct的实例。我可以自己实现:

instance Storable Struct where
    sizeOf _    = ...
    alignment _ = ...

但这样接近于打败了目的——如果我不关心结构体中有什么,我不想定义这些东西。
我注意到指向指针的指针可以很好地工作,因为Ptr类是可存储的(Storable)。所以,在调用get_struct之前,在ptr上使用peek,就可以实现我想要的效果。
main = alloca $ \ptr -> do
  ptr <- peek ptr
  get_struct ptr

虽然这种方法看起来像是个hack。

有没有一种方法可以使空数据声明被视为Storable而不需要定义一个实例?


2
这是一个 hack。你从未为内部指针分配空间;你只是随意指向某个内存。这样会导致段错误。 - John L
所以如果我真的想要一个指向指针的指针,我应该使用两个 alloca 调用,然后将一个指针插入另一个指针中,对吗? - zmthy
指向指针的最常见用途是指针输出值,此时绑定函数进行分配,并且字符串数组(例如 argv)也会使用指向指针。 - John L
2个回答

6
如果您不知道需要分配的大小,那么就无法进行分配。该函数会忽略其参数吗?那么请传递一个空指针。否则,您需要为结构体实际分配足够的空间 - 不要仅仅分配零字节或指针大小的缓冲区,因为调用的函数将会在您的缓冲区末尾写入,破坏内存。
要么完成数据声明,要么编写具有正确大小和对齐值的可储存实例;必须以某种形式提供大小/对齐数据,这是无法避免的。

2
你可以使用像 chs 这样方便的工具来完成这个任务 - 它可以帮助你找出这些值。 - fuz
3
请注意,您不必定义实例的每个方法。如果您不想将结构体编组到Haskell中,请仅定义 sizeOfalignment,但是保留 peekpoke 未定义(或调用错误)。 - John L

2

这里有另一种方法可能适合您。我假设您可以访问定义所需分配对象的所有C头文件。如果是这样,您可以编写一个薄的C代码层用于分配和释放C对象。然后,您的Haskell代码可以调用这些C函数,而不需要Haskell代码知道指针背后的内容。当Haskell的垃圾收集器知道不再需要这些对象时,Haskell还可以自动调用释放代码。


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