[Top][All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Chicken-users] patch for chicken-bind

From: Kristian Lein-Mathisen
Subject: Re: [Chicken-users] patch for chicken-bind
Date: Tue, 26 Jun 2012 17:18:34 -0700

Hi Jim,

I really appreciate you looking over this! 

However, I think that it may be more practical to return locatives because they are interchangeable with c-pointers. This makes them easier to pass around with other parts of the foreign-code.

If we remove (locative ...) and use scheme-pointer, nested struct getters and struct return-types in scheme would return blobs instead of locatives. Let's check out this (untested) example of what I think would be typical usage:

(bind "
struct point  { float x, y; };
struct circle { struct point origin; float radius; };
float distance(struct point, struct point);")

(define c1 (make-circle (make-point 10 10) 0.5))
(define c2 (make-circle (make-point 1 1) 0.2))

;; circle-origin is a nested-struct getter.
;; if it returns blobs, we must do:
(distance (make-locative (circle-origin c1)) (make-locative (circle-origin c2)))
;; if it returns locative:
(distance (circle-origin c1) (circle-origin c2))

I was not aware of the performance penalty introduced by (locative <blob>). Would it be beneficial to return the locative of the blob as a last step, and use scheme-pointer in the intermediate, destination-operand, step?

Thank you,

On Mon, Jun 25, 2012 at 10:18 PM, Jim Ursetto <address@hidden> wrote:
Tip: if you use scheme-pointer instead of c-pointer, you can omit the locative).  E.g. (make-blob size) instead of (location (make-blob size)).  This will be faster.

On Jun 25, 2012, at 5:27 PM, Kristian Lein-Mathisen wrote:

Hi guys!

It's me again, still going on about struct-by-value in chicken-bind. This time I think I may have code 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. 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.

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
address@hidden chicken-bind]$ echo "float length(struct point*)" | chicken-bind - -o -
    (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:

address@hidden chicken-bind]$ echo "float length(struct point)" | chicken-bind - -o -
    (define length
        (((c-pointer (struct "point")) 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:

address@hidden chicken-bind]$ echo "struct point intersection(struct line*, struct line)" | chicken-bind  - -o -
      (define intersection/overwrite!
          (((c-pointer (struct "point")) dest)
           ((c-pointer (struct "line")) a0)
           ((c-pointer (struct "line")) a1))
      (define (intersection a0 a1)
        (let ((dest (location
                      (make-blob (foreign-value "sizeof(struct point)" int)))))
          (intersection/overwrite! dest a0 a1)

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:

address@hidden chicken-bind]$ echo "struct circle { struct point origin; float radius ; }" | chicken-bind - -o -
  (define circle-origin
    (lambda (s)
      (let ((blob (location
                    (make-blob (foreign-value "sizeof(struct point)" int))))
                (((c-pointer (struct "point")) _dest)
                 ((c-pointer (struct "circle")) s))
                "*_dest = s->origin;")))
        (copy-struct! blob s)
  (define circle-radius
      (((c-pointer (struct "circle")) s))
  (define make-circle
      (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_);")))

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 for these features.

Cheers fellow Chickeners,
- Kris

Chicken-users mailing list

reply via email to

[Prev in Thread] Current Thread [Next in Thread]