tags:

views:

380

answers:

2

Here's my situation:

I would like to call ffmpeg's av_free_packet function:

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

But unfortunately this function is static inline, and so doesn't really appear in the linked library.

However, it is a very simple function, which I could reimplement in Haskell. And that's what I can't figure out how to do. Here's a partial attempt (.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 = ?

For now I could resort to implementing this function in C (by just calling the original), but it seems to me that calling function pointers should be possible somehow..

+4  A: 

From The Haskell 98 Foreign Function Interface 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.

In your case, the usage would look something like the following.

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
ephemient
thanks! I think you meant to make funPktToNil :: FunPtr (Ptr AVPacket -> IO ()) -> Ptr AVPacket -> IO ()
yairchu
Indeed I did mean that. How embarrassing. In any case, glad to be of assistance.
ephemient
Do you have a test program that demonstrates your solution? I tried the same approach but got only segfaults.
Greg Bacon
@gbacon: I tested it and it works. see example below.
yairchu
+5  A: 

A small example to demonstrate that this (ephemient's answer) actually works (following gbacon's concern):

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

This works for me - prints 3 % 5

yairchu
Nice! I tried importing a foreign pointer-to-function and calling through it, but that segfaulted.
Greg Bacon