Why the pointer transfered is not the expected value in FFI?

Viewed 76

I try to call a C function with the declaration from Haskell side:

int IupOpen(int *argc, char ***argv);

Then I do the following binding:

foreign import capi "iup.h IupOpen"
    c_IupOpen :: Ptr CInt -> Ptr (Ptr CString) -> IO CInt
iupOpen :: Maybe [String]   -- ^ Program's command line arguments
        -> IO Int      -- ^ The initialization status flag
iupOpen args = case args of
    Nothing -> fromIntegral <$> c_IupOpen nullPtr nullPtr
    Just argv -> alloca $ \argcPtr -> do
        poke argcPtr (fromIntegral . length $ argv)
        argvs <- mapM (`withCString` return) argv
        pCstr <- withArray argvs return
        argvPtr <- alloca $ \ppCstr -> poke ppCstr pCstr >> return ppCstr
        fromIntegral <$> c_IupOpen argcPtr argvPtr

After compiling, the following warning is given:

C:\...\AppData\Local\Temp\ghc6096_0\ghc_5.c: In function 'ghczuwrapperZC1ZChiupzm0zi1zi0zi0zm1ddNNYbMeka1JTSCkHxKK2ZCGraphicsziIUPziRawziIupZCIupOpen':
C:\...\AppData\Local\Temp\ghc6096_0\ghc_5.c:10:143: warning: passing argument 2 of 'IupOpen' from incompatible pointer type [-Wincompatible-pointer-types]
   10 | HsInt32 ghczuwrapperZC1ZChiupzm0zi1zi0zi0zm1ddNNYbMeka1JTSCkHxKK2ZCGraphicsziIUPziRawziIupZCIupOpen(void* a1, void*** a2) {return IupOpen(a1, a2);}    
      |                                                                                                                                               ^~       
      |                                                                                                                                               |        
      |                                                                                                                                               void *** 
In file included from C:\...\AppData\Local\Temp\ghc6096_0\ghc_5.c:7:
C:/.../hs/hs-iup/iup-3.30_Win64_mingw6_lib/include/iup.h:37:56: note: expected 'char ***' but argument is of type 'void ***'
   37 | IUP_API int       IupOpen          (int *argc, char ***argv);

But the parameter argvPtr I passed is really of type Ptr (Ptr CString). I don't understand why this warning happens.

Thans for any ideas!

=======Update 1===========

After some tries, I solved the issue by replacing capi with ccall. It seems that capi have a procedure to produce some wrapper functions, which don't have the proper type signature.

But still I don't know why capi can't work correctly? Is it a bug or due to anything that I haven't noticed?

======Update 2=============

New code without dangling pointers (I believe ^_^):

foreign import capi "iup.h IupOpen"
    c_IupOpen :: Ptr CInt -> Ptr (Ptr CString) -> IO CInt
open :: Maybe [String]     -- ^ Program's command line arguments
     -> IO Int             -- ^ The initialization status flag
open args = case args of
    Nothing -> fromIntegral <$> c_IupOpen nullPtr nullPtr
    Just argv -> alloca $ \ptrArgc -> do
        poke ptrArgc (fromIntegral . length $ argv)
        alloca $ \ptrCStr -> do
            cstrArr <- mapM newCString argv   -- to free an array of pointer
            pokeArray ptrCStr cstrArr
            alloca $ \pptrCStr -> do
                poke pptrCStr ptrCStr
                flag <- c_IupOpen ptrArgc pptrCStr
                mapM_ free cstrArr
                return . fromIntegral $ flag

The pointer type is still mismatched unless using ccall.

=============Update 3=====================

Well, finally I find that it's a bug according to the issue #15531

0 Answers
Related