[plt-scheme] Manipulating Scheme vectors with FFI
On Oct 28, Benjamin Seppke wrote:
>
> So my question is: How can I manipulate Scheme vectors inside a c-
> function and see the results of that in Scheme..?
Generally speaking, you don't want to manipulate *Scheme* vectors --
they hold data that your C library will not know how to handle. The
right solution is to translate a Scheme vector to a C vector and
back. Here's a few examples:
The C "library" I have is this:
int foo(double arr[], int length) {
int i;
for (i=0; i<length; i++) arr[i] *= 2.0;
return length;
}
A simple interface, similar to yours:
(define foo
(get-ffi-obj 'foo "xx.so"
(_fun (_vector i _double) _int -> _int)))
(foo #(1.0 2.0 3) 3)
The problem here is that you never see the result (but the return
value is fine). This is the semantics of `(_vector i <type>)' -- the
value is an input to the foreign function, and nothing more. First of
all, I'll make it easier: the `_double*' type is similar to `_double'
except that it will coerce integers too:
(define foo
(get-ffi-obj 'foo "xx.so"
(_fun (_vector i _double*) _int -> _int)))
(foo #(1 2 3) 3)
The next thing to do is to use a "custom type" to specify the second
input, which will result in a simpler interface at the scheme side.
We want to send the length of the vector, but this requires having a
name for the vector so we can refer to it -- [name : type] is used to
get such a name for an argument, and [type = expr] is used to specify
the value (so it's gone from the interface of the resulting function):
(define foo
(get-ffi-obj 'foo "xx.so"
(_fun [v : (_vector i _double*)]
[_int = (vector-length v)]
-> _int)))
(foo #(1 2 3))
Now we get to your problem. The thing is that we want to specify a
`_vector' type in `io' mode, so it will be translated on its way to C
and back -- but that requires specifying the length of the returned
vector. To get this following how you started the code, you specify
the arguments explicitly:
(define foo
(get-ffi-obj 'foo "xx.so"
(_fun (v) ::
[p : (_vector io _double* (vector-length v)) = v]
[_int = (vector-length v)]
-> _int)))
(foo #(1 2 3))
The problem, as you've noticed, is that you never get to hold the
return value. You can do that by specifying a second `->' and the
result expression, for example:
(define foo
(get-ffi-obj 'foo "xx.so"
(_fun (v) ::
[p : (_vector io _double* (vector-length v)) = v]
[_int = (vector-length v)]
-> _int
-> p)))
(foo #(1 2 3))
and you can also name the return value, and use it in the "real
result" expression:
(define foo
(get-ffi-obj 'foo "xx.so"
(_fun (v) ::
[p : (_vector io _double* (vector-length v)) = v]
[_int = (vector-length v)]
-> [r : _int]
-> (if (zero? r) (error "boom") p))))
(foo #(1 2 3))
(I made it throw an error if r=0, since my "library" returns 3...)
But it's probably better to just use a cvector, which corresponds to a
C vector, and since it is mutable, you can just reference the results
with `cvector-ref':
(define foo
(get-ffi-obj 'foo "xx.so"
(_fun [v : _cvector]
[_int = (cvector-length v)]
-> [r : _int]
-> (if (zero? r) (error "boom") v))))
(define v (list->cvector '(1 2 3) _double*))
(cvector->list (foo v))
Here I actually used `cvector->list' to see the whole list. In this
case there is really no need to return the vector, you can just use
the actual value which is now different:
(define foo
(get-ffi-obj 'foo "xx.so"
(_fun [v : _cvector]
[_int = (cvector-length v)]
-> [r : _int]
-> (when (zero? r) (error "boom")))))
(define v (list->cvector '(1 2 3) _double*))
(foo v)
(cvector->list v)
but the previous setup is probably better.
--
((lambda (x) (x x)) (lambda (x) (x x))) Eli Barzilay:
http://www.barzilay.org/ Maze is Life!