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)


От The Haskell 98 Foreign Function Interface 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

Малък пример за демонстриране, че това (отговорът на 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

person yairchu    schedule 15.06.2009
comment
хубаво! Опитах се да импортирам чужд указател-към-функция и да се обадя през него, но това се случи с грешка. - person Greg Bacon; 16.06.2009