FFI: Mapping anonymous structs

I’m trying to map a C library to Haskell using capi instead of ccall if possible. I’m having problems with these types in C though:

typedef struct {
    char _[24];
}* AsapoConsumerHandle;

If I map this as type AsapoConsumerHandle = Ptr () (because you’re not supposed to inspect the pointer anyways), as such:

type AsapoConsumerHandle = Ptr ()

foreign import capi "asapo/consumer_c.h asapo_create_consumer" asapo_create_consumer :: IO AsapoConsumerHandle

Then capi complains:

     note: expected ‘struct <anonymous> **’ but argument is of type ‘void **’
       67 |                                           AsapoErrorHandle* error);

But I’m not sure how to map this anonymous struct capi compatible.

1 Like

You should be able to use a CTYPE pragma to assign the name to the handle, something like

newtype {-# CTYPE "asapo/consumer_c.h" "AsapoConsumerHandle" #-}
        AsapoConsumerHandle = AsapoConsumerHandle (Ptr CChar)
3 Likes

Interesting! That works.

But I now realize that…the other thing also works. It outputs an error, but still compiles. Weird.

I am confused now. Let’s assume I have this:

newtype 
  {-# CTYPE "asapo/common/common_c.h" "AsapoErrorHandle" #-} AsapoErrorHandle = 
    AsapoErrorHandle (Ptr CChar)

foreign import capi "asapo/consumer_c.h asapo_new_handle" asapo_new_handle :: IO AsapoErrorHandle

So I can create a handle using asapo_new_handle, fine. Then there are functions that fill these handles:

foreign import capi "asapo/consumer_c.h asapo_consumer_generate_new_group_id" asapo_consumer_generate_new_group_id :: AsapoConsumerHandle -> Ptr AsapoErrorHandle -> IO ()

I thought I’d create a handle and then use with to get a pointer to use it in the second function:

consumer <- ...
errorHandle <- asapo_new_handle
with errorHandle $ \errorHandlePtr -> asapo_consumer_generate_new_group_id consumer errorHandlePtr

This doesn’t work, “of course”, because with assumes something Storable, but my newtype isn’t. Should I use GeneralizedNewtypeDeriving to derive Storable? Or extract/cast the pointer?

You can write a custom withErrorHandle function (since with is just alloca plus poke) or you can derive a newtype Storable (which will pollute documentation, but is way more convenient). Neither solution feels good, but either works well enough.