2012-08-15 79 views
2

我是Haskell编程,外部函数接口和Stackoverflow的新手。我正在尝试为基于C的库构建一个Haskell FFI绑定。请找一个假设的例子,这是非常相似,我现在的问题如下:Haskell FFI - mallocForeignPtr用法

考虑我有一个C结构和功能是这样的:

typedef struct { 
     int someInt; 
     void *someInternalData; 
    } opaque_t; 

int bar (opaque_t *aPtr, int anArg); 

不透明的C结构是这里的out参数。我应该将其传递给其他API。调用者不需要去引用不透明的结构体。

查找下面myFFI.hsc文件与FFI进口:

{-# LANGUAGE CPP, ForeignFunctionInterface #-} 
module MyFFI where 
import Foreign 
import Foreign.Ptr 
import Foreign.ForeignPtr 
import Foreign.C.Types 
import Foreign.C 
import System.IO.Unsafe 
import Foreign.Marshal 
import qualified Foreign.ForeignPtr.Unsafe (unsafeForeignPtrToPtr) 
import qualified System.IO (putStrLn) 

#include "myclib.h" 

newtype OpaquePtr = OpaquePtr (ForeignPtr OpaquePtr) 

#let alignment t = "%lu", (unsigned long)offsetof(struct {char x__; t (y__); }, y__) 
instance Storable OpaquePtr where 
    sizeOf _ = #{size opaque_t} 
    alignment _ = #{alignment opaque_t} 
    peek _ = error "Cant peek" 

foreign import ccall unsafe "myclib.h bar" 
    c_bar :: Ptr OpaquePtr 
       -> CInt 
       -> CInt 

barWrapper :: Int -> (Int, ForeignPtr OpaquePtr) 
barWrapper anArg = System.IO.Unsafe.unsafePerformIO $ do 
    o <- mallocForeignPtr 
    let res = c_bar (fromIntegral anArg) (Foreign.ForeignPtr.Unsafe.unsafeForeignPtrToPtr o) 
    return ((fromIntegral res), o) 

在我实际的代码,类似于执行上述似乎工作。但是当我传递不透明的结构参考时,我得到了奇怪的输出,有时候ghci会激化。

我不确定在FFI调用中使用mallocForeignPtr和ForeignPtr。对于漫长的生活参考,我们应该使用ForeignPtr + mallocForeignPtr,但是我们无法在ccall中传递一个ForeignPtr。那么怎么做呢?我的上述逻辑正确吗? 任何形式的帮助都会非常棒。谢谢。

回答

0

嗯,我想我找到了ForeignPtr和mallocForeignPtr的正确用法的解决方案:

barWrapper :: Int -> (Int, Either error (ForeignPtr OpaquePtr)) 
barWrapper anArg = System.IO.Unsafe.unsafePerformIO $ do 
    o <- mallocForeignPtr 
    withForeignPtr o $ \opaque_ptr -> do 
     let res = c_bar (fromIntegral anArg) opaque_ptr 
     if res /= (-1) 
      then return ((fromIntegral res), Right o) 
     else 
      return ((fromIntegral res), Left $ error "some problem") 

的问题有:

  1. 我忽视哈斯克尔懒惰的评价啄:仅在访问'res'外线电话将被执行。所以,我不得不使用的if/else块将使其调用ccall
  2. 我应该使用,而不是unsafeForeignPtrtoPtr
4

我试图想出可能表现出一个典型的使用为例withForeignPtr 情况。由于使用阶乘作为历史悠久的传统,因此我决定不打破它。

下面这两个文件(factorial.h和factorial.c)使用一个表格来帮助计算整数的阶乘。他们首先构建并用012va填充表格;那么这个表格用于请求 阶乘;然后在不再需要时将其解除分配。 我们还将消息打印到标准输出只是为了能够知道我们的表何时被初始化和释放。

factorial.h:

/* A table of factorials. table[i] is the factorial of i. The 
* max field is calculated so that its factorial would not be an 
* integer overflow. 
*/ 

typedef struct { 
    unsigned max; 
    unsigned *table; 
} factorial_table; 

int factorial_table_init(factorial_table *t); 
int factorial_get(factorial_table *t, int n); 
void factorial_table_free(factorial_table *t); 

factorial.c:

#include <stdio.h> 
#include <stdlib.h> 
#include <limits.h> 
#include <factorial.h> 

/* Calculates max and allocate table. Returns !0 if 
* memory could not be allocated. 
*/ 

int factorial_table_init(factorial_table *t) 
{ 
    unsigned i, factorial; 

    t->max = factorial = 1; 
    while (INT_MAX/factorial > t->max + 1) 
     factorial *= ++t->max; 
    t->table = malloc((t->max + 1)*sizeof(unsigned)); 
    if (!t->table) return !0; 
    t->table[0] = 1; 
    for (i = 1; i <= t->max; i++) 
     t->table[i] = i * t->table[i-1]; 
    fprintf(stdout,"A factorial table was just allocated.\n"); 
    return 0; 
} 

/* Uses a table to get the factorial of an integer number n. Returns 
* (-1) if n is negative and (-2) if n is too big. 
*/ 

int factorial_get(factorial_table *t, int n) 
{ 
    if (n < 0) return (-1);  
    if (n > t->max) return (-2); 
    return t->table[n]; 
} 

/* Frees the table we used. */ 

void factorial_table_free(factorial_table *t) 
{ 
    free(t->table); 
    fprintf(stdout,"A factorial table was just freed.\n"); 
} 

现在,我们的Haskell代码。

{-# LANGUAGE CPP, ForeignFunctionInterface, EmptyDataDecls #-} 

#include <factorial.h> 

#let alignment t = "%lu", (unsigned long)offsetof(struct {char x__; t (y__); }, y__) 

module Factorial (factorial) where 
import Control.Monad 
import Foreign.Ptr 
import Foreign.ForeignPtr 
import Foreign.C 
import Foreign.Storable 
import System.IO.Unsafe 
import Foreign.Marshal 

data Factorial_table 

instance Storable Factorial_table where 
    sizeOf _ = #{size factorial_table} 
    alignment _ = #{alignment factorial_table} 
    peek _ = error "Cant peek" 

foreign import ccall factorial_table_init :: Ptr Factorial_table -> IO CInt 
foreign import ccall factorial_get :: Ptr Factorial_table -> CInt -> IO CInt 
foreign import ccall "&factorial_table_free" funptr_factorial_table_free 
    :: FunPtr (Ptr Factorial_table -> IO()) 

factorialIO :: IO (CInt -> IO CInt) 
factorialIO = do 
    tableFgnPtr <- mallocForeignPtr :: IO (ForeignPtr Factorial_table) 
    withForeignPtr tableFgnPtr $ \ptr -> do 
     status <- factorial_table_init ptr 
     when (status /= 0) $ fail "No memory for factorial table" 
    addForeignPtrFinalizer funptr_factorial_table_free tableFgnPtr 
    let factorialFunction n = do 
     r <- withForeignPtr tableFgnPtr $ \ptr -> factorial_get ptr n 
     when (r == (-1)) $ fail 
      "Factorial was requested for a negative number" 
     when (r == (-2)) $ fail 
      "Factorial was requested for a number that is too big" 
     return r 
    return factorialFunction 

factorial :: CInt -> CInt 
factorial = unsafePerformIO . unsafePerformIO factorialIO 

首先,请注意Factorial_table实例是如何存储的。另请注意,所有函数绑定都会返回IO。

所有相关的代码都在factorialIO中。它首先mallocs一个指针(并且 这里是使用来自Storable的大小和对齐信息,I 写了这个调用的类型,但这不是必需的)。然后它添加了终结器,它将在指针内存被释放之前运行。我们 将该指针封装在一个整数(factorialFunction)的函数内,始终使用 withForeignPtr并返回它。

由于我们知道我们的函数没有重要的副作用,所以最后2行只是将我们刚刚创建的东西变成纯函数。 让我们来测试一下吧:

ghci 
    Prelude> :m + Factorial 
Prelude Factorial> factorial 5 
    A factorial table was just allocated. 
    120 
Prelude Factorial> factorial 10 
    3628800 
Prelude Factorial> factorial 13 
    *** Exception: user error (Factorial was requested for a number that is too big) 
Prelude Factorial> factorial 12 
    479001600 
Prelude Factorial> :q 
    Leaving GHCi. 
    A factorial table was just freed. 

我希望这很有用。当然,这是一个完全人为的计算阶乘因子的方法,但这正是上帝创造阶乘因子的原因。