Hello List, I'm running into a problem with c2hs and how it parses the C typedef 'size_t'. On 32bit systems, this ends up being parsed as a CUInt. On 64bit systems, this ends up as a CULong. This gets especially sticky with function pointers. In order to make bindings with c2hs that work across the different word sizes, I have to write an indirection layer in C that defines wrappers for functions or re-defines typedef's for function pointers to include 'unsigned long' instead of 'size_t'. I see there is a ticket open for this: http://hackage.haskell.org/trac/c2hs/ticket/20 Has any one else run into this issue? Is there a good workaround that doesn't involve writing a C function/typedef for each collision? /jve
On Thu, 2009-10-01 at 10:20 -0400, John Van Enk wrote:
Hello List,
I'm running into a problem with c2hs and how it parses the C typedef 'size_t'. On 32bit systems, this ends up being parsed as a CUInt. On 64bit systems, this ends up as a CULong. This gets especially sticky with function pointers.
Right. Of course that's because on those different platforms size_t really is a typedef for unsigned int or unsigned long.
I see there is a ticket open for this: http://hackage.haskell.org/trac/c2hs/ticket/20
Has any one else run into this issue? Is there a good workaround that doesn't involve writing a C function/typedef for each collision?
So what you would want, presumably, is to map the "size_t" typedef to the Haskell type Foreign.C.Types.CSize, rather than what c2hs discovers as the actual raw type of "size_t". Then you're making the promise that unsigned long, or unsigned int really really does match Foreign.C.Types.CSize. Currently c2hs has no support for remapping basic types like that. As for a workaround, just use fromIntegral to convert to CSize. You know this is always possible because you know CSize matches CUInt or CULong on the appropriate platforms. $ cat foo.h #include <stddef.h> size_t foo(void); $ cat foo.c #include "foo.h" size_t foo(void) { return 42; } $ cat foo.chs import Foreign.C.Types foo :: IO CSize foo = fmap fromIntegral {# call foo as bar #} $ gcc -c foo.c $ c2hs foo.h foo.chs $ ghci foo.hs -fffi foo.o Main> foo 42 Duncan
Hi Duncan, Yes, I forgot to leave out that I'd like to see 'size_t' mapped to CSize. As a (dirty) workaround, one can use 'castPtr' as the marshaler when dealing with pointers to 'size_t'. I'm a little more concerned about FunPtr's (though 'castPtr' still makes the issue go away). Here's my specific case dealing with function pointers: In 64bit world, it looks like this: type ReadableCallback = Ptr () -> Ptr CUChar -> *Ptr CULong* -> IO CUInt {#fun gcry_ac_io_init_readable_callback { id `ACIO', id `FunPtr ReadableCallback', id `Ptr ()' } -> `()'#} In 32bit world, it looks like this: type ReadableCallback = Ptr () -> Ptr CUChar -> *Ptr CUInt* -> IO CUInt {#fun gcry_ac_io_init_readable_callback { id `ACIO', id `FunPtr ReadableCallback', id `Ptr ()' } -> `()'#} I'd really like it if I could use 'Ptr CSize' (which corresponds to the 'size_t * ptr' in the C code). My current workaround (which doesn't use a cast in Haskell) is to redefine the function pointer type in a C header file to use 'unsigned long' instead of 'size_t', but this gets cludgy in a hurry. If c2hs could translate to CSize directly, I could pass a pointer to CSize. Is it really such a problem to make the conversion? My assumption is that CSize would match 'size_t' for the specific architecture. Thanks for your comment. /jve On Thu, Oct 1, 2009 at 12:37 PM, Duncan Coutts <duncan.coutts@googlemail.com
wrote:
On Thu, 2009-10-01 at 10:20 -0400, John Van Enk wrote:
Hello List,
I'm running into a problem with c2hs and how it parses the C typedef 'size_t'. On 32bit systems, this ends up being parsed as a CUInt. On 64bit systems, this ends up as a CULong. This gets especially sticky with function pointers.
Right. Of course that's because on those different platforms size_t really is a typedef for unsigned int or unsigned long.
I see there is a ticket open for this: http://hackage.haskell.org/trac/c2hs/ticket/20
Has any one else run into this issue? Is there a good workaround that doesn't involve writing a C function/typedef for each collision?
So what you would want, presumably, is to map the "size_t" typedef to the Haskell type Foreign.C.Types.CSize, rather than what c2hs discovers as the actual raw type of "size_t". Then you're making the promise that unsigned long, or unsigned int really really does match Foreign.C.Types.CSize.
Currently c2hs has no support for remapping basic types like that.
As for a workaround, just use fromIntegral to convert to CSize. You know this is always possible because you know CSize matches CUInt or CULong on the appropriate platforms.
$ cat foo.h #include <stddef.h> size_t foo(void);
$ cat foo.c #include "foo.h" size_t foo(void) { return 42; }
$ cat foo.chs
import Foreign.C.Types foo :: IO CSize foo = fmap fromIntegral {# call foo as bar #}
$ gcc -c foo.c $ c2hs foo.h foo.chs $ ghci foo.hs -fffi foo.o Main> foo 42
Duncan
On Thu, 2009-10-01 at 13:00 -0400, John Van Enk wrote:
Hi Duncan,
Yes, I forgot to leave out that I'd like to see 'size_t' mapped to CSize.
As a (dirty) workaround, one can use 'castPtr' as the marshaler when dealing with pointers to 'size_t'. I'm a little more concerned about FunPtr's (though 'castPtr' still makes the issue go away).
Here's my specific case dealing with function pointers:
Ohhh, if you're dealing with pointers to CSize then it's easy, c2hs supports that directly. See the c2hs user guide section on the pointer hook. {# pointer *size_t as CSizePtr -> CSize #} This tells c2hs that when it sees a *size_t type in a function argument or result that it should be imported as Ptr CSize. It generates a type alias and uses that when it imports functions eg: {# pointer *size_t as CSizePtr -> CSize #} foo :: Ptr CSize -> IO () foo = {# call foo as raw_foo #} generates: type CSizePtr = Ptr (CSize) foo :: Ptr CSize -> IO () foo = raw_foo foreign import ccall safe "foo.chs.h foo" raw_foo :: CSizePtr -> IO () Duncan
Ohhh, if you're dealing with pointers to CSize then it's easy, c2hs supports that directly. See the c2hs user guide section on the pointer hook.
{# pointer *size_t as CSizePtr -> CSize #}
I seriously should have thought of that. I have #pointer's everywhere, but I didn't think to do it with that. Thanks. /jve
participants (2)
-
Duncan Coutts -
John Van Enk