【问题标题】:Haskell FFI: Calling FunPtrsHaskell FFI:调用 FunPtrs
【发布时间】:2026-01-03 23:20:03
【问题描述】:

这是我的情况:

我想调用ffmpeg的av_free_packet函数:

// avformat.h
static inline void av_free_packet(AVPacket *pkt)
{
  if (pkt && pkt->destruct)
    pkt->destruct(pkt);
}

但不幸的是,这个函数是static inline,因此并没有真正出现在链接库中。

但是,它是一个非常简单的函数,我可以在 Haskell 中重新实现它。这就是我不知道该怎么做。这是部分尝试(.hsc):

av_free_packet :: Ptr AVPacket -> IO ()
av_free_packet pkt =
  when (nullPtr /= pkt) $ do
    destruct <- (#peek AVPacket, destruct) pkt :: IO (FunPtr (Ptr AVPacket -> IO ()))
    when (nullFunPtr /= destruct) $ funPtrToFun destruct pkt

funPtrToFun :: FunPtr a -> a
funPtrToFun = ?

现在我可以求助于在 C 中实现这个函数(通过调用原始函数),但在我看来,调用函数指针应该是可能的......

【问题讨论】:

    标签: c haskell ffi


    【解决方案1】:

    来自The Haskell 98 Foreign Function Interface 1.0

    动态导入。

    dynamic 存根的类型必须是(FunPtr ft) -&gt; ft 的形式,其中ft 可以是任何外来类型。

    举个例子,考虑

    foreign import ccall "dynamic"  
      mkFun :: FunPtr (CInt -> IO ()) -> (CInt -> IO ())
    

    存根工厂mkFun 将任何指向以整数值作为其唯一参数且没有返回值的 C 函数的指针转换为相应的 Haskell 函数。

    在您的情况下,用法如下所示。

    foreign import ccall "dynamic"
      funPktToNil:: FunPtr (Ptr AVPacket -> IO ()) -> Ptr AVPacket -> IO ()
    
    av_free_packet :: Ptr AVPacket -> IO ()
    av_free_packet pkt =
      when (nullPtr /= pkt) $ do
        destruct <- (#peek AVPacket, destruct) pkt
        when (nullFunPtr /= destruct) $ funPktToNil destruct pkt
    

    【讨论】:

    • 谢谢!我想你的意思是让 funPktToNil :: FunPtr (Ptr AVPacket -> IO ()) -> Ptr AVPacket -> IO ()
    • 我确实是这个意思。多么尴尬。无论如何,很高兴能提供帮助。
    • 您有演示您的解决方案的测试程序吗?我尝试了相同的方法,但只得到了段错误。
    【解决方案2】:

    一个小例子来证明这个(ehemient's answer)确实有效(遵循 gbacon 的关注):

    C:

    #include <stdio.h>
    
    typedef void funcType(int, int);
    typedef funcType * pFuncType;
    
    void printer(int a, int b) {
      printf("%d %% %d\n", a, b);
    }
    
    pFuncType gimmeFunc(int dummy) {
      return printer;
    }
    

    哈斯克尔:

    {-# LANGUAGE ForeignFunctionInterface #-}
    
    import Foreign.Ptr
    
    foreign import ccall unsafe "gimmeFunc"
      c_gimmeFunc :: Int -> IO (FunPtr (Int -> Int -> IO ()))
    foreign import ccall "dynamic"
      mkFunIntIntNil :: FunPtr (Int -> Int -> IO ()) -> Int -> Int -> IO ()
    
    main :: IO ()
    main = do
      fun <- c_gimmeFunc 1
      mkFunIntIntNil fun 3 5
    

    这对我有用 - 打印 3 % 5

    【讨论】:

    • 不错!我尝试导入一个指向函数的外部指针并通过它调用,但是出现了段错误。