2016-11-03 76 views
1

read函数的定义是这样的:写作Haskell的FFI读系统调用

#include <unistd.h> 

ssize_t read(int fd, void *buf, size_t len) 

我想写一个Haskell FFI这一点。这是我尝试(不编译):

foreign import ccall unsafe "read" c_read :: CInt -> Ptr() -> CSize -> IO CSsize 

read :: Int --^file descriptor 
    -> Int64 --^Bytes to allocate 
    -> Word64 --^Read upto this many bytes 
    -> IO (String, Int64) 
read fd bytes len = do 
    allocaBytes (fromIntegral bytes) (\ptr -> do 
         size <- c_read (fromIntegral fd) ptr (fromIntegral len) 
         dat <- peekByteOff (castPtr ptr) (fromIntegral size) :: IO String 
         return (dat, (fromIntegral size)) 
        ) 

它不会编译,因为不存在String要了解可存储实例。有关如何为此编写FFI的任何提示?

+2

我建议你返回的'String'一个'ByteString'相反,随着后者意味着可能不是真实的字符编码。为了创建'ByteString',你可能希望''malloc'缓冲区而不是'allocaBytes',然后重用'ByteString'的指针。 –

+1

您的窥视线过于简单,实际上是不正确的,因为您正在使用'size'作为开始阅读的索引。考虑['packCStringLen'](http://hackage.haskell.org/package/bytestring-0.10.8.1/docs/Data-ByteString.html#v:packCStringLen) –

+0

此外,这个API似乎非常危险。至少你应该检查'bytes> = len',这样用户不能说'read fd 0 5000',对吧?编辑:其实它比这更棘手。 'fromIntegral bytes :: some type> = fromIntegralLen :: that type'这里的类型匹配你的casts/c绑定。 –

回答

1

按照迈克尔Snoyman和雷德巴顿的建议,我终于想出了这个解决方案作为一个开始:

foreign import ccall safe "read" c_read :: CInt -> Ptr() -> CSize -> IO CSsize 

read :: Int --^file descriptor 
    -> Int64 --^Bytes to allocate 
    -> Word64 --^Read upto this many bytes 
    -> IO (ByteString, Int64) 
read fd bytes len = do 
    (ptr :: Ptr()) <- mallocBytes (fromIntegral bytes) 
    size <- c_read (fromIntegral fd) ptr (fromIntegral len) 
    bstring <- packCStringLen (castPtr ptr, fromIntegral size) 
    free ptr 
    return (bstring, (fromIntegral size))