我想通过FFI接口从Haskell提供PETSc库(的一个子集),以便隐藏用户的内存和错误管理;
make test1
使用加载的模块传递并激活GHCi。由于库通过MPI和完全分布式数据结构实现并行操作,因此在大多数操作期间不应期望Haskell有大量数据流量(所有数据组装,计算和释放都应由库原语完成)但仅在"数据就绪"。与PETSc相关的Haskell函数将主要在IO monad中具有值,因为我们无法保证纯度(例如,返回的C错误代码可能因程序外部的原因而变化)
unsafePerformIO
和错误管理需要alloca
。这种思路是否正确?mpirun
执行吗? 我对所有建议和评论持开放态度。提前谢谢
- 注意:
我们希望GHC生成mpirun
可以执行的二进制文件:由于可以使用-optl
标记(参考here)将选项从GHC命令行传递给链接器,因此我可以使用ghc -optl-static -lmpich
标记。已被建议使用$ ./configure --with-cc=gcc --with-cxx=g++ --with-fc=gfortran --with-shared-libraries=1 --download-mpich --download-fblaslapack
之类的组合。我可以在尝试后立即添加更多内容。
1)config命令:
{-# 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
2)PETSC.hsc
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
3)Makefile
{{1}}
答案 0 :(得分:2)
宏大!我很想使用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相关的一个非常艰难的例子,因为将char ***
参数的编组管理到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 -wise实际上将在IO
monad中,因为你将通过调用API函数来设置内部PETSc状态的位,并且你需要在Haskell函数的类型中捕获这种有效性。
使用mpirun
运行GHC生成的二进制文件应该不是问题。
我也不会写makefile。您应该可以使用Cabal完成所有操作而不会有太多麻烦!