本文介绍了我怎样才能让手动分配的数据在Haskell中被垃圾收集?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧! 问题描述 29岁程序员,3月因学历无情被辞! 如果一个内存缓冲区被用来保存一些数据并被手动分配,然后它在Haskell计算中使用,我可以以某种方式依赖垃圾收集器来释放它,当它不再需要时。 至于手动分配,基本上有两个方式(但差异似乎并不是我的问题必不可少的): 在Haskell中分配缓冲区,然后将它传递给C函数,如 fdRead 在C中分配缓冲区(使用 malloc ,就像在GNU的 asprintf 中),然后将指针返回给Haskell在这两个例子中( fdRead 或 asprintf ) / code>)还有一个存储在缓冲区中的数据类型不适合Haskell程序的问题,因此它被复制和转换以用于Haskell(具有 peekCString )。 (我将把下面的代码)。复制和转换发生后,缓冲区被释放(在这两种情况下)。 然而,我正在考虑一个更有效的接口,Haskell可以直接使用C函数存储的数据(无需转换)。 (我还没有探索 String 和其他相关函数的替代实现:是否有其中一个可以直接与某种C字符串一起工作) 如果遵循这条路线,那么存在一个全局性问题:如何控制分配缓冲区的处置。 (对于无副作用的函数 - 除了分配 - 我甚至可以将调用包装在 unsafePerformIO 中,或者声明它们以使它们不是 转换和立即释放的示例 分配哈斯克尔: fdRead (这里 - ----------------------------------- ------------------------------------------ - fd {阅读,写} - |从'Fd'读取数据并使用语言环境编码将其转换为'String'。 - 如果这是一个无效的描述符,或者EOF达到,则抛出异常。 fdRead :: Fd - > ByteCount - ^要读取多少个字节 - > IO(String,ByteCount) - ^读取的字节数,读取的字节数。 fdRead _fd 0 = return(,0) fdRead fd nbytes = do allocaBytes(fromIntegral nbytes)$ \ buf - > do rc case rc of 0 - >> ioError(ioeSetErrorString(mkIOError EOFfdReadNothing Nothing)EOF) n - > s< - peekCStringLen(castPtr buf,fromInteral n) return(s,n) - |将数据从'Fd'读入内存。这与POSIX @ read @函数完全等价于。 fdReadBuf :: Fd - > Ptr Word8 - ^存放数据的存储区 - > ByteCount - ^读取的最大字节数 - > IO ByteCount - ^读取的字节数(EOF为零) fdReadBuf _fd _buf 0 =返回0 fdReadBuf fd buf nbytes = fmap fromIn $ throwErrnoIfMinus1RetryfdReadBuf$ c_safe_read(fromIntegral fd)(castPtr buf)nbytes 外部导入ccall saferead c_safe_read :: CInt - > Ptr CChar - > CSize - > IO CSsize 在C中分配 getValue.c : #define _GNU_SOURCE #include< ; stdio.h中> #includegetValue.h char * getValue(int key){ char * value; asprintf(& value,%d,key); // TODO:没有错误处理! //如果内存分配不可能或发生其他错误,这些函数将 //返回-1,并且strp的内容未定义。 返回值; } GetValue.hs (在转换实际完成之后,我明确地调用 free >) : { - #LANGUAGE ForeignFunctionInterface# - } 导入外部隐藏(unsafePerformIO) import Foreign.Ptr import Foreign.C.Types import Foreign.C.String(peekCString) import System.IO.Unsafe getValue :: Int - > IO字符串 getValue key = do valptr< - c_safe_getValue(fromIntegral键)值< - peekCString valptr c_safe_free valptr 返回值 国外进口ccall安全getValue.h getValuec_safe_getValue :: CInt - > IO(Ptr CChar)外部导入ccall安全stdlib.h freec_safe_free :: Ptr a - > IO() value :: Int - >字符串值= unsafePerformIO。 getValue - getValue没有副作用,所以我们包装它。 { - 一个简单的测试: - } main1 = putStrLn(值2) { - 用无限列表测试,它使用懒惰: - } 键= [-5 ..] 结果=地图值键 $ b main = foldr(>>)(return())(map putStrLn(取20个结果)) 如果没有(无效)转换&复制步骤,我需要依靠垃圾收集器来释放,但不知道如何在Haskell中定义这样的事情。 解决方案 ForeignPtr 类型充当附加终结器的 Ptr 。当 ForeignPtr 被垃圾收集后,运行终结器,并且可以调用C端使用正确的函数释放指针。 由于指针不再可以从Haskell访问,这通常是释放它的正确时刻。 I'm thinking about a FFI calling some C functions from Haskell.If a memory buffer is used to hold some data and is allocated "manually" and then it is used in Haskell computations, can I somehow rely on the garbage collector to free it when it is not needed anymore.As for the manual allocations, there are basically two ways (but the difference doesn't seem to be essential for my question):allocating a buffer in Haskell, then passing it to C function, like in fdReadallocating a buffer in C (with malloc, like in GNU's asprintf), then returning the pointer to HaskellIn both examples (fdRead or asprintf) there is also a problem that the data type stored in the buffer is not suitable for a Haskell program, therefore it is copied&converted to be used in Haskell (with peekCString). (I'll put the code below.) After the copying&conversion happens, the buffer is freed (in both cases).However, I'm thinking about a more efficient interface, where the Haskell would directly use the data as it is stored by a C function (without a conversion). (I haven't yet explored, say, alternative implementations of String and related functions: whether there is one among them which can work directly with some kind of C strings.)If I follow this route, then there is one global problem: how to control the disposal of the allocated buffers. (For side-effects-free functions--except for the allocation--I could even wrap the calls in unsafePerformIO or declare them so that they are not an IO.)Examples with conversion and immediate freeingallocating in Haskell:fdRead (here allocaBytes must care for the freeing):-- ------------------------------------------------------------------------------- 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 0fdReadBuf fd buf nbytes = fmap fromIntegral $ throwErrnoIfMinus1Retry "fdReadBuf" $ c_safe_read (fromIntegral fd) (castPtr buf) nbytesforeign import ccall safe "read" c_safe_read :: CInt -> Ptr CChar -> CSize -> IO CSsizeallocating in CgetValue.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 (here I explicitly call free after the conversion is actually done):{-# LANGUAGE ForeignFunctionInterface #-}import Foreign hiding (unsafePerformIO)import Foreign.Ptrimport Foreign.C.Typesimport Foreign.C.String(peekCString)import System.IO.UnsafegetValue :: Int -> IO StringgetValue key = do valptr <- c_safe_getValue (fromIntegral key) value <- peekCString valptr c_safe_free valptr return valueforeign 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 -> Stringvalue = 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 keysmain = foldr (>>) (return ()) (map putStrLn (take 20 results))If there wasn't the (ineffective) conversion&copying step, I would need to rely on garbage collector for freeing, but have no idea how to define such things in Haskell. 解决方案 The ForeignPtr type acts as a Ptr with an attached finalizer. When the ForeignPtr gets garbage collected, the finalizer is run, and can call the C side to free the pointer using the proper function.Since the pointer is no longer accessible from Haskell, this is typically the right moment to free it. 这篇关于我怎样才能让手动分配的数据在Haskell中被垃圾收集?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持! 上岸,阿里云!
09-04 20:54