Sven Panne <[EMAIL PROTECTED]> wrote,
> Simon Marlow wrote:
> > Manuel M. T. Chakravarty wrote:
> > [...] we should really enforce a stack discipline and use a wrapper
> > > function:
> > > alloca :: Int -> (Addr -> IO a) -> IO a
> >
> > Nice idea. I'm not sure about the implementation though: did you
> > have in mind using C's malloc/free?
>
> Perhaps I'm totally missing the point here, but alloca seems to be
> dead easy to me:
[delete version using malloc/free]
> It's getting tough if you want to avoid malloc/free though.
Which is exactly the reason, I didn't mention malloc and
free. First of all, my thinking is more along the lines of
how can we make this already promising FFI design into
something that can eventually get into the Haskell
standard. So, all GHC-specific hacks don't help us
anything. Second, we need a design that is sufficiently
straight-forward to implement and still allows an efficient
implementation of the FFI across different systems.
So, the question is, given the general problem of having to
deal with (in)out parameters in C, how can we handle them
nicely in the FFI? The byte array stuff doesn't cut it for
reasons already discussed.
The idea behind the `alloca' proposal is that it (1) allows
the compiler (or interpreter) to allocate a chunk of stable
memory in whatever way is most efficient in that
implementation and (2) it makes it clear when the memory
area can be freed again. Any better ideas?
> But IMHO the main problem is the handling of the correct offsets for
> e.g. inout parameters in the buffer. Alloca doesn't help here, and if
> you do it by hand, you end up with Haskell code which look suspiciously
> like the one H/Direct generates...
Hmmm, that's truly a problem if coding on the FFI manually.
So, my thinking was too tool-centric. So, another problem
is that we want to allow tools to use their knowlege of the
size of primitives datatypes etc to optimise the marshaling
process, but still, we want to allow a sufficiently
convenient manual use of the FFI.
So, how about providing functions that advance the `mem'
pointer for you or even have `writeXXXOffAddr' return the
next address after the datum just written? I.e, something
like
foo = alloca N $ \mem1 -> do
mem1 <- writeWord32OffAddr mem1 0 ...
mem2 <- writeInt16OffAddr mem2 0 ...
...
bar mem1 mem2
<optionally take stuff out of `memX' again>
foreign import ... bar :: Addr -> Addr ... -> IO ()
This is, however, maybe not so helpful because of alignment
constraints that are harder to satisfy manually than in
generated code. So how about
foo = alloca N $ \mem1 -> do
writeWord16OffAddr mem 0 ...
let mem2 = mem `plusAddr` 4
mem2 <- writeInt32OffAddr mem2 0 ...
...
bar mem1 mem2
<optionally take stuff out of `memX' again>
foreign import ... bar :: Addr -> Addr ... -> IO ()
A tool can pack everything as dense as the architecture-
dependent alignment constraints permit and a human can play
save and, e.g., use multiples of 4 on a 32-bit architecture
(the exact number should of course be hidden in a config
file to allow easy migration to other architectures). I
think, this is not more difficult than having some form of
`malloc' call for every in(out) parameter passed (which is
how I do it currently in Gtk+HS).
BTW, I recently noticed that `writeInt32OffAddr a k' and
friends access `a + k * 4' and not `a + k', as I always
thought. I think that this is rather unfortunate, as for
marshaling, I usually want to give a byte offset and not
multiples of the value currently accessed (as the marshaled
data is usually not uniform). Furthermore, I can always get
the current behviour by explicitly multiplying the index
with the number of 4 byte elements, but it is not possible
the other way around.
Finally, it would be nice to have floating point numbers
with explicit sizes - in the above `alloca' example, how
would a human programmer know, how much memory to allocate
for a float? I guess, they are not provided, because it
would be a pain to implement the float operations for sizes
that are not `natively' supported on the platform. However,
the current situation, where you have to hope that `Float'
in Haskell and `float' in C match is also not very nice and
definitely not appropriate for standardisation - unless
Haskell guarantees that `Float' and `float' as well as
`Double' and `double' always use the same representation
(and `long double' is completely missing so far). So, what
is less pain? Guarantee the use of the standard float sizes
in Haskell or `Float's of explicit size? Any other ideas?
In summary, WishList requests:
* `alloca' or something better :-)
* writeXXXOffAddr indexed with bytes offsets
* floats with explicit sizes or a any other form of
guarantee about their size & support for `long double'
Cheers,
Manuel