Haskell中带双环境的ForeignPtr终结器实现方案问询
封装依赖双环境变量的C类型的ForeignPtr方案
方案一:用元组打包环境,复用现有API
Haskell的addForeignPtrFinalizerEnv仅支持单个环境参数,我们可以把两个环境指针打包成Haskell元组,在finalizer中拆包使用,无需修改底层实现:
首先定义C函数的FFI绑定:
foreign import ccall "gr_mpoly_init" gr_mpoly_init :: Ptr GRMPoly -> Ptr MPolyCtx -> Ptr GrCtx -> IO () foreign import ccall "gr_mpoly_clear" gr_mpoly_clear :: Ptr GRMPoly -> Ptr MPolyCtx -> Ptr GrCtx -> IO ()
然后编写包装后的finalizer,接收打包的环境元组:
grMPolyFinalizer :: Ptr GRMPoly -> (Ptr MPolyCtx, Ptr GrCtx) -> IO () grMPolyFinalizer polyPtr (mctx, cctx) = gr_mpoly_clear polyPtr mctx cctx
最后创建带环境的ForeignPtr:
newGRMPoly :: Ptr MPolyCtx -> Ptr GrCtx -> IO (ForeignPtr GRMPoly) newGRMPoly mctx cctx = do polyPtr <- mallocBytes sizeOfGRMPoly -- 需提前定义GRMPoly的内存大小 gr_mpoly_init polyPtr mctx cctx newForeignPtrEnv polyPtr (mctx, cctx) grMPolyFinalizer
方案二:自定义环境类型,提升类型安全性
如果觉得元组不够直观,可自定义专用环境类型,避免类型歧义:
-- 封装双环境的自定义类型 data GRMPolyEnv = GRMPolyEnv (Ptr MPolyCtx) (Ptr GrCtx) -- 对应环境类型的finalizer grMPolyFinalizer' :: Ptr GRMPoly -> GRMPolyEnv -> IO () grMPolyFinalizer' polyPtr (GRMPolyEnv mctx cctx) = gr_mpoly_clear polyPtr mctx cctx -- 创建GRMPoly的ForeignPtr newGRMPoly' :: Ptr MPolyCtx -> Ptr GrCtx -> IO (ForeignPtr GRMPoly) newGRMPoly' mctx cctx = do polyPtr <- mallocBytes sizeOfGRMPoly gr_mpoly_init polyPtr mctx cctx newForeignPtrEnv polyPtr (GRMPolyEnv mctx cctx) grMPolyFinalizer'
方案三:用StablePtr兼容旧版GHC(需额外C代码)
若使用的GHC版本不支持newForeignPtrEnv,可借助StablePtr打包环境,需要编写少量C包装代码:
Haskell侧代码
import Foreign.StablePtr foreign import ccall "&gr_mpoly_clear_stable" gr_mpoly_clear_stable :: FunPtr (Ptr GRMPoly -> StablePtr (Ptr MPolyCtx, Ptr GrCtx) -> IO ()) newGRMPolyStable :: Ptr MPolyCtx -> Ptr GrCtx -> IO (ForeignPtr GRMPoly) newGRMPolyStable mctx cctx = do polyPtr <- mallocBytes sizeOfGRMPoly gr_mpoly_init polyPtr mctx cctx -- 打包环境到堆内存,再转为StablePtr envTuple <- mallocBytes (sizeOf (undefined :: (Ptr MPolyCtx, Ptr GrCtx))) poke envTuple (mctx, cctx) stableEnv <- newStablePtr envTuple newForeignPtr gr_mpoly_clear_stable polyPtr
C侧包装代码
#include <HsFFI.h> #include "gr_mpoly.h" // 引入原C库头文件 void gr_mpoly_clear_stable(gr_mpoly_t A, StablePtr env) { // 解包StablePtr中的环境元组 typedef struct { mpoly_ctx_t mctx; gr_ctx_t cctx; } EnvTuple; EnvTuple *tuple = (EnvTuple *)deRefStablePtr(env); // 调用原清理函数 gr_mpoly_clear(A, tuple->mctx, tuple->cctx); // 释放临时内存和StablePtr free(tuple); freeStablePtr(env); }
内容的提问来源于stack exchange,提问作者Hartmut Monien
相关产品推荐
相关产品推荐

