Hi guys!

It's me again, still going on about struct-by-value in
chicken-bind<http://wiki.call-cc.org/eggref/4/bind>.
This time I think I may have
code<https://github.com/kristianlm/chicken-bind> worthy
of entering the official repo. The patches add three new features:

   1. Struct-by-value in arguments
   2. Struct-by-value return types
   3. Nested structs (practically same as 2)

Functions on the Scheme-side interface all functions using pointers or
locatives, regardless of their original signature.

You can have a look at my 10 commits that make up the patch on
github<https://github.com/kristianlm/chicken-bind/commits/>. I
tried to be descriptive in my commit messages. Please let me know of your
thoughts and concerns. If nothing pops up, I'll pass it on Felix
(chicken-bind maintainer) for review.

*Motivation*
While most C libraries pass structs by reference, both physics engines I've
come across, Chipmunk and Box2D, pass small structs like 2d-vectors around
by value everywhere. This patch made my life easier.

*Code samples*
Let's walk through the new foreign-lambda snippets that it generates. I use
the point struct in my examples, pretend it's some 2d/3d vector of
floats. First, let's look at passing a struct by reference:

*1. Struct arguments*
[klm@kth chicken-bind]$ echo "float length(struct point*)" | chicken-bind -
-o -
(begin
  (begin
    (define length
      (foreign-lambda float "length" (c-pointer (struct "point"))))))
*
*
Nothing's changed there, my patch will kick in when you pass structs by
value. The patch checks if any arguments are non-pointer struct arguments,
and if there are any, it wraps the call in a foreign-lambda* with all
struct-by-val arguments to c-pointer variant which are dereferenced in C:

[klm@kth chicken-bind]$ echo "float length(struct point)" | chicken-bind -
-o -
(begin
  (begin
    (define length
      (foreign-lambda*
        float
        (((c-pointer (struct "point")) a0))
        "C_return(length(*a0));"))))

*2. Struct return-types*
Struct return-types are a little trickier and are split into two functions.
One will call the original function, storing the result in a additional
destination operand. The other will allocate memory to use as this
destination and calls the first:

[klm@kth chicken-bind]$ echo "struct point intersection(struct line*,
struct line)" | chicken-bind  - -o -
(begin
  (begin
    (begin
      (define intersection/overwrite!
        (foreign-lambda*
          void
          (((c-pointer (struct "point")) dest)
           ((c-pointer (struct "line")) a0)
           ((c-pointer (struct "line")) a1))
          "*dest=(intersection(a0,*a1));"))
      (define (intersection a0 a1)
        (let ((dest (location
                      (make-blob (foreign-value "sizeof(struct point)"
int)))))
          (intersection/overwrite! dest a0 a1)
          dest)))))

As shown above, you can mix and match struct value-passing and
pointer-passing in the arguments.

*3. Nested structs*
Nested structs face the same problem as struct return-types, but
unfortunately I haven't looked into uniting the codebase. However, it
follows the same destination-method as above:

[klm@kth chicken-bind]$ echo "struct circle { struct point origin; float
radius ; }" | chicken-bind - -o -
(begin
  (define circle-origin
    (lambda (s)
      (let ((blob (location
                    (make-blob (foreign-value "sizeof(struct point)" int))))
            (copy-struct!
              (foreign-lambda*
                void
                (((c-pointer (struct "point")) _dest)
                 ((c-pointer (struct "circle")) s))
                "*_dest = s->origin;")))
        (copy-struct! blob s)
        blob)))
  (define circle-radius
    (foreign-lambda*
      float
      (((c-pointer (struct "circle")) s))
      "return(s->radius);"))
  (define make-circle
    (foreign-lambda*
      (c-pointer (struct "circle"))
      (((c-pointer (struct "point")) origin) (float radius))
      "struct circle *tmp_ = (struct circle *)C_malloc(sizeof(struct
circle));\ntmp_->origin = *origin;\n\ntmp_->radius =
radius;\n\nC_return(tmp_);")))


*Caveats*
Struct-by-value return types and nested-struct getters return locatives.
This is nice because it will be like any other scheme-object and doesn't
need to be explicitly freed. Be careful though, locatives will be moved
around by the GC and thus pointers to it are not permanent.

A also added a small
test-suite<https://github.com/kristianlm/chicken-bind/blob/master/tests/struct-passing-tests.scm>for
these features.

Cheers fellow Chickeners,
- Kris
_______________________________________________
Chicken-users mailing list
Chicken-users@nongnu.org
https://lists.nongnu.org/mailman/listinfo/chicken-users

Reply via email to