Haskell FFI: вызов FunPtrs

Вот моя ситуация:

Я хотел бы вызвать функцию av_free_packet ffmpeg:

// 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 (просто вызывая оригинал), но мне кажется, что вызов указателей функций должен быть каким-то образом возможен.


person yairchu    schedule 15.06.2009    source источник


Ответы (2)


Из Интерфейса внешних функций Haskell 98 1.0,

Динамический импорт.

Тип динамической заглушки должен иметь форму (FunPtr ft) -> 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
person ephemient    schedule 15.06.2009
comment
Благодарность! Я думаю вы хотели сделать funPktToNil::FunPtr(Ptr AVPacket -> IO()) -> Ptr AVPacket -> IO() - person yairchu; 16.06.2009
comment
Я действительно это имел в виду. Как неловко. В любом случае рад быть полезным. - person ephemient; 16.06.2009
comment
У вас есть тестовая программа, демонстрирующая ваше решение? Я попробовал тот же подход, но получил только segfaults. - person Greg Bacon; 16.06.2009

Небольшой пример, демонстрирующий, что это (ответ эфемиента) действительно работает (после беспокойства 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

person yairchu    schedule 15.06.2009
comment
Хороший! Я попытался импортировать чужой указатель на функцию и вызвать через него, но это не удалось. - person Greg Bacon; 16.06.2009