haskellffistorable

Passing mixed storable vectors to a C function


I have a list of vectors - the set of types is known and fixed - let us say, CInt and CChar. The list is unknown at compile time - the composition will be determined from a configuration file at run-time. For example, we might decide we need to pass two vectors to C function: One CInt vector of length 10, one CChar vector of length 50. As to how C function interprets them, I can handle that logic by passing a vector encoding types of each vector (say, 0 => CInt, 1 => CChar), and a vector encoding length of each vector passed (10,50).

What I am trying to figure out is how to generate a vector of mixed vectors (just for passing to C). I tried a toy solution like the one below (it models the same idea - generating a storable vector of Ptr of mixed types - in actual code, each Ptr will point to another Storable vector). It failed because of type error - I suspect it is related to existentially qualified types that ehird pointed out before in another question I asked earlier. Since I am using Storable instance for passing to C FFI, I guess I can't wrap the types (without defining another storable instance).

{-#  LANGUAGE BangPatterns, GADTs #-}
import Data.Vector.Storable as SV
import Foreign.C.Types (CChar, CInt)
import GHC.Int (Int32)
import Foreign.Marshal.Alloc
import Foreign.Ptr (Ptr)

mallocInt :: IO (Ptr CInt)
mallocInt = malloc

mallocChar :: IO (Ptr CChar)
mallocChar = malloc

main = do
  a <- mallocInt
  b <- mallocChar
  let c = SV.fromList [a,b]
  return ()

Error in ghci 7.4.1:

test.hs:17:26:
    Couldn't match expected type `CInt' with actual type `CChar'
    Expected type: Ptr CInt
      Actual type: Ptr CChar
    In the expression: b
    In the first argument of `fromList', namely `[a, b]'
Failed, modules loaded: none.

I will appreciate pointers on how to solve the above problem. I could write a custom vector filling function using Data.Vector.Storable.Mutable.new and unsafeWrite, but still I will need to fit the mixed types.


Solution

  • Like in C, you need to cast a char * and int * to a void * before being able to store it in a generic array. So, cast your Ptr CChar and Ptr CInt to a Ptr () before inserting it into a vector.

    You can cast a pointer by using the Foreign.Ptr.castPtr function like so:

    intPtr :: Ptr CInt
    intPtr = undefined -- dummy value
    
    voidPtr :: Ptr ()
    voidPtr = castPtr intPtr