Haskell中PETSc FFI的库设计

我想通过FFI接口从
Haskell获得
PETSc库(的一个子集),以隐藏用户的内存和错误管理;

>使用下面显示的命令构建带有共享库的PETSc 3.5.3,测试套件成功运行
>使用标头导入,类型和两个示例外部函数接口准备了一个.hsc文件#2
>准备了一个Makefile#3来自动化构建; make test1传递并使用加载的模块激活GHCi.

由于库通过MPI和完全分布式数据结构实现并行操作,因此在大多数操作期间不应期望Haskell有大量数据流量(所有数据组装,计算和释放都应由库原语完成)但仅限于“数据就绪“.与PETSc相关的Haskell函数将主要在IO monad中具有值,因为我们无法保证纯度(例如,返回的C错误代码可能因程序外部的原因而变化)

>需要unsafePerformIO来包装内存分配和错误管理.这种思路是否正确?馊主意
>用GHC编译的二进制文件可以用mpirun执行吗?是

我对所有建议和评论持开放态度.先感谢您

– 注意:
我们希望GHC生成一个mpirun可以执行的二进制文件:由于可以使用-optl标志将参数从GHC命令行传递给链接器(参考号here),我建议使用ghc -optl-static等组合 – lmpich.我会尽快补充一下这个问题.

1)config命令:

$./configure --with-cc=gcc --with-cxx=g++ --with-fc=gfortran --with-shared-libraries=1 --download-mpich --download-fblaslapack

2)PETSC.hsc

{-# LANGUAGE CPP, ForeignFunctionInterface, EmptyDataDecls #-}
module PETSc where

import Foreign
import Foreign.Ptr
import Foreign.C.Types
import Foreign.C.String

#include <petscksp.h>
#include <petscsys.h>

newtype PetscErrCode = PetscErrCode {unPetscErrCode :: CInt} deriving (Eq, Show)
newtype PetscInt = PetscInt {unPetscInt :: CInt} deriving (Eq, Show)

data Petsc
-- PetscErrorCode PetscInitialize(int *argc,char ***args,const char file[],const char help[])
foreign import ccall unsafe "petscsys.h PetscInitialize"
  c_petscInitialize :: Ptr CInt -> Ptr (Ptr CString) -> CString -> CString -> IO PetscErrCode

-- PetscErrorCode PetscFinalize(void)
foreign import ccall unsafe "petscsys.h PetscFinalize"
  c_petscFinalize :: IO PetscErrCode

3)Makefile

PETSC_DIR_ARCH = ${PETSC_DIR}/arch-darwin-c-debug

PETSc.hs: 
    hsc2hs PETSc.hsc -I ${PETSC_DIR}/include -I ${PETSC_DIR_ARCH}/include

test1: PETSc.hs 
    ghci -dynamic PETSc.hs -L${PETSC_DIR_ARCH}/lib

最佳答案 雄心勃勃的!我很想使用C2HS而不是hsc2hs,因为它可以为你生产一些外国进口的样板. (我是C2HS的维护者,所以你可以用一粒盐来取我说的话!)

例如,您可以像这样绑定PetscInitialize和PetscFinalize:

-- This is in PETSc.chs
module PETSc (initialize, finalize) where

import Foreign
import Foreign.Ptr
import Foreign.C.Types
import Foreign.C.String
import System.Environment (getArgs)

#include <petscksp.h>
#include <petscsys.h>

-- Marshalling helpers for PETSc error codes.

newtype ErrCode = ErrCode { unErrCode :: Int }
                deriving (Eq, Show)

convErrCode :: CInt -> ErrCode
convErrCode = ErrCode . fromIntegral

{#typedef PetscErrorCode CInt#}
{#default out `ErrCode' [PetscErrorCode] convErrCode#}


-- Marshalling helpers for "char ***" argument to PetscInitialize.

withCStrings :: [String] -> ([CString] -> IO a) -> IO a
withCStrings ss f = case ss of
  [] -> f []
  (s:ss') -> withCString s $\cs -> do
    withCStrings ss' $\css -> f (cs:css)

withCStringArray :: [String] -> (Ptr CString -> IO a) -> IO a
withCStringArray ss f = withCStrings ss $\css -> withArray css f

withCStringArrayPtr :: [String] -> (Ptr (Ptr CString) -> IO a) -> IO a
withCStringArrayPtr ss f = withCStringArray ss $\css -> with css f


-- Low-level function hooks.

{#fun PetscInitialize as internal_initialize
    {`Int', withCStringArrayPtr* `[String]', `String', `String'}
    -> `ErrCode'#}
{#fun PetscFinalize as finalize {} -> `ErrCode'#}


-- Better API for initialization.

initialize :: String -> String -> IO ErrCode
initialize file help = do
  args <- getArgs
  internal_initialize (length args) args file help

这实际上是与C2HS相关的一个非常艰难的例子,因为管理charc参数到PetscInitialize的编组有点尴尬,但你明白了.大多数其他编组案例应该更直接 – 处理指针和数组非常容易,就像编组C字符串一样. (如果您决定使用它,我很乐意帮助解决C2HS问题.)

一旦你有了这个,你可以这样称呼它:

-- This is Tst.hs or something...
module Main where

import PETSc

main :: IO ()
main = do
  res1 <- initialize "" ""
  print res1
  res2 <- finalize
  print res2

还不是很有用,但这是一个开始!像这样编译它:

c2hs -C -I/opt/petsc/arch-linux2-cxx-opt/include PETSc.chs
ghc --make Tst.hs PETSc.hs -L/opt/petsc/arch-linux2-cxx-opt/lib/ -lpetsc

(根据需要调整路径,obvs).

回答你的其他问题:

>不要使用unsafePerformIO,除非你真的确定你所调用的函数是“有效的纯粹” – PetscInitialize肯定不符合这个条件.你可以写一个PETSc monad作为IO的一种限制包装,如果你不想直接在IO monad中拥有所有内容,但是你所做的PETSc方面的大部分真的将在IO monad中,因为你通过调用API函数来设置内部PETSc状态的位,你需要在Haskell函数的类型中捕获这种有效性.
>使用mpirun运行GHC生成的二进制文件应该不是问题.

我也不会写makefile.您应该可以使用Cabal完成所有操作而不会有太多麻烦!

点赞