gpt4 book ai didi

haskell - 如何在 Haskell 中对手动分配的数据进行垃圾收集?

转载 作者:行者123 更新时间:2023-12-04 15:43:26 28 4
gpt4 key购买 nike

我正在考虑一个 FFI 从 Haskell 调用一些 C 函数。

如果内存缓冲区用于保存一些数据并“手动”分配,然后在 Haskell 计算中使用,我是否可以依靠垃圾收集器在不再需要时释放它。

至于手动分配,基本上有两种方法(但差异似乎对我的问题并不重要):

  • 在 Haskell 中分配一个缓冲区,然后将其传递给 C 函数,如 fdRead
  • 在 C 中分配一个缓冲区(使用 malloc ,就像在 GNU 的 asprintf 中一样),然后返回指向 Haskell 的指针

  • 在这两个例子中( fdReadasprintf )还有一个问题是存储在缓冲区中的数据类型不适合 Haskell 程序,因此它被复制并转换为在 Haskell 中使用(使用 peekCString )。 (我将把代码放在下面。)复制和转换发生后,缓冲区被释放(在这两种情况下)。

    但是,我正在考虑一个更有效的接口(interface),其中 Haskell 将直接使用由 C 函数存储的数据(无需转换)。 (例如,我还没有探索过 String 和相关函数的替代实现:其中是否有一个可以直接使用某种 C 字符串。)

    如果我遵循这条路线,那么就会出现一个全局问题:如何控制分配的缓冲区的处置。 (对于无副作用的函数——除了分配——我什至可以将调用包装在 unsafePerformIO 中,或者声明它们不是 IO。)

    转换和立即释放的示例

    在 Haskell 中分配:

    fdRead (这里 allocaBytes 必须关心释放):
    -- -----------------------------------------------------------------------------
    -- fd{Read,Write}

    -- | Read data from an 'Fd' and convert it to a 'String' using the locale encoding.
    -- Throws an exception if this is an invalid descriptor, or EOF has been
    -- reached.
    fdRead :: Fd
    -> ByteCount -- ^How many bytes to read
    -> IO (String, ByteCount) -- ^The bytes read, how many bytes were read.
    fdRead _fd 0 = return ("", 0)
    fdRead fd nbytes = do
    allocaBytes (fromIntegral nbytes) $ \ buf -> do
    rc <- fdReadBuf fd buf nbytes
    case rc of
    0 -> ioError (ioeSetErrorString (mkIOError EOF "fdRead" Nothing Nothing) "EOF")
    n -> do
    s <- peekCStringLen (castPtr buf, fromIntegral n)
    return (s, n)

    -- | Read data from an 'Fd' into memory. This is exactly equivalent
    -- to the POSIX @read@ function.
    fdReadBuf :: Fd
    -> Ptr Word8 -- ^ Memory in which to put the data
    -> ByteCount -- ^ Maximum number of bytes to read
    -> IO ByteCount -- ^ Number of bytes read (zero for EOF)
    fdReadBuf _fd _buf 0 = return 0
    fdReadBuf fd buf nbytes =
    fmap fromIntegral $
    throwErrnoIfMinus1Retry "fdReadBuf" $
    c_safe_read (fromIntegral fd) (castPtr buf) nbytes

    foreign import ccall safe "read"
    c_safe_read :: CInt -> Ptr CChar -> CSize -> IO CSsize

    在 C 中分配
    getValue.c :
    #define _GNU_SOURCE
    #include <stdio.h>

    #include "getValue.h"

    char * getValue(int key) {
    char * value;
    asprintf(&value, "%d", key); // TODO: No error handling!
    // If memory allocation wasn't possible, or some other error occurs, these functions will
    // return -1, and the contents of strp is undefined.
    return value;
    }
    GetValue.hs (这里我明确调用 free 在转换后 is actually done ):
    {-# LANGUAGE ForeignFunctionInterface #-}

    import Foreign hiding (unsafePerformIO)
    import Foreign.Ptr
    import Foreign.C.Types

    import Foreign.C.String(peekCString)

    import System.IO.Unsafe

    getValue :: Int -> IO String
    getValue key = do
    valptr <- c_safe_getValue (fromIntegral key)
    value <- peekCString valptr
    c_safe_free valptr
    return value

    foreign import ccall safe "getValue.h getValue" c_safe_getValue :: CInt -> IO (Ptr CChar)
    foreign import ccall safe "stdlib.h free" c_safe_free :: Ptr a -> IO ()

    value :: Int -> String
    value = unsafePerformIO . getValue -- getValue has no side-effects, so we wrap it.

    {- A simple test: -}
    main1 = putStrLn (value 2)

    {- A test with an infinite list, which employs laziness: -}
    keys = [-5..]
    results = map value keys

    main = foldr (>>)
    (return ())
    (map putStrLn (take 20 results))

    如果没有(无效的)转换和复制步骤,我需要依靠垃圾收集器来释放,但不知道如何在 Haskell 中定义这些东西。

    最佳答案

    ForeignPtr 类型充当 Ptr带有一个附加的终结器。当ForeignPtr收集垃圾,运行终结器,并可以调用 C 端使用适当的函数释放指针。

    由于不再可以从 Haskell 访问指针,因此这通常是释放它的正确时机。

    关于haskell - 如何在 Haskell 中对手动分配的数据进行垃圾收集?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28563342/

    28 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com