Haskell FFI:调用FunPtrs

10

这是我的情况:

我想调用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语言中实现该函数(只需调用原始函数)来解决问题,但我觉得以某种方式调用函数指针应该也是可行的。

2个回答

8

Haskell 98外部函数接口1.0中得知:

Dynamic import.

The type of a dynamic stub has to be of the form (FunPtr ft) -> ft, where ft may be any foreign type.

As an example, consider

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

The stub factory mkFun converts any pointer to a C function that gets an integer value as its only argument and does not have a return value into a corresponding Haskell function.

在您的情况下,使用方法应该类似于以下内容。
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 ()开玩笑。 - yairchu
确实我是这个意思。真尴尬。无论如何,很高兴能够帮助。 - ephemient
你有一个演示你解决方案的测试程序吗?我尝试了相同的方法,但只得到了段错误。 - Greg Bacon

7
一个小示例来证明这个(ephemient 的回答)实际上是有效的(遵循 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;
}

Haskell:

{-# 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

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接