如何包装不安全的FFI? (Haskell的)

时间:2014-08-22 05:22:08

标签: haskell ffi unsafe-perform-io

这是Is there ever a good reason to use unsafePerformIO?

的后续问题

所以我们知道

p_sin(double *p) { return sin(*p); }

不安全,不能与unsafePerformIO一起使用。

但是p_sin函数仍然是一个数学函数,它以不安全的方式实现的事实是一个实现细节。我们并不完全希望矩阵乘法在IO中,因为它涉及分配临时内存。

我们如何以安全的方式包装此功能?我们需要锁定,自己分配内存等吗?是否有处理此问题的指南/教程?

1 个答案:

答案 0 :(得分:6)

实际上,如果你采用p_sin对该答案不安全的方式,它取决于p_sin 而不是是一个数学函数,至少不是一个从数字到数字 - 当相同指针指向的内存不同时,它取决于给出不同的答案。所以,从数学上讲,两个调用之间存在某种的不同;使用正式的指针模型,我们可能会告诉你。 E.g。

type Ptr = Int
type Heap = [Double]

p_sin :: Heap -> Ptr -> Double

然后C函数等同于

p_sin h p = sin (h !! p)

结果不同的原因是因为一个不同的Heap参数,它在C定义中是未命名但隐含的。

如果p_sin在内部使用临时内存,但不依赖于通过其接口的内存状态,例如

double p_sin(double x) {
    double* y = (double*)malloc(sizeof(double));
    *y = sin(x);
    x = *y;
    free(y);
    return x;
}

然后我们确实有一个实际的数学函数Double -> Double,我们可以

foreign import ccall safe "p_sin" 
    p_sin :: Double -> Double

我们没事。界面中的指针在这里杀死纯度,而不是C函数。

更实际的是,我们假设你有一个用指针实现的C矩阵乘法函数,因为那是你在C中对数组建模的方法。在这种情况下,你可能会扩展抽象边界,所以在你的程序中会发生一些不安全的事情,但它们都会被模块用户隐藏起来。在这种情况下,我建议在实现中使用IO注释所有不安全的内容,然后在将unsafePerformIO提供给模块用户之前将其module Matrix -- only export things guaranteed to interact together purely (Matrix, makeMatrix, multMatrix) where newtype Matrix = Matrix (Ptr Double) makeMatrix :: [[Double]] -> Matrix makeMatrix = unsafePerformIO $ ... foreign import ccall safe "multMatrix" multMatrix_ :: Ptr Double -> IO (Ptr Double) multMatrix :: Matrix -> Matrix multMatrix (Matrix p) = unsafePerformIO $ multMatrix_ p 注释掉。这样可以最大限度地减少杂质的表面积。

{{1}}