From: taylanbayirli@gmail.com (Taylan Ulrich Bayırlı/Kammer)
To: guile-user@gnu.org
Subject: Bytestructures, FFI
Date: Tue, 31 May 2016 22:29:58 +0300 [thread overview]
Message-ID: <87oa7mauvd.fsf@T420.taylan> (raw)
Hi there,
I had announced this project a while ago, here:
https://lists.gnu.org/archive/html/guile-user/2015-08/msg00034.html
I just added support for converting a bytestructure descriptor into a
type descriptor as per Guile's FFI module.
E.g. the uint8 descriptor in (bytestructures guile numeric) converts to
the value of the uint8 variable in the (system foreign) module, and a
struct descriptor such as (bs:struct `((x ,uint8) (y ,uint16))) converts
to what one would get from evaluating (list uint8 uint16) after
importing (system foreign). (Such lists are accepted by Guile's FFI to
denote structs; it's yet undocumented in the manual.)
For convenience, a bs:pointer->proc procedure is offered, that wraps
pointer->procedure and adds bytestructure related functionality. Here's
a minimal example of how to use it:
--- start transcript ---
taylan@T420:~/src/scheme/bytestructures$ cat test.c
typedef struct {
struct {
int x;
float y;
} s;
int z;
float t[3];
} foo_t;
foo_t testfunc(foo_t arg)
{
++arg.s.x;
++arg.s.y;
++arg.z;
++arg.t[0];
++arg.t[1];
++arg.t[2];
return arg;
}
taylan@T420:~/src/scheme/bytestructures$ gcc -shared -o libtest.so test.c
taylan@T420:~/src/scheme/bytestructures$ sudo mv libtest.so /usr/lib/
taylan@T420:~/src/scheme/bytestructures$ guile
GNU Guile 2.0.11
Copyright (C) 1995-2014 Free Software Foundation, Inc.
Guile comes with ABSOLUTELY NO WARRANTY; for details type `,show w'.
This program is free software, and you are welcome to redistribute it
under certain conditions; type `,show c' for details.
Enter `,help' for help.
scheme@(guile-user)> (import (bytestructures guile))
scheme@(guile-user)> (import (bytestructures guile ffi))
scheme@(guile-user)> (define foo_t (bs:struct `((s ,(bs:struct `((x ,int)
(y ,float32))))
(z ,int)
(t ,(bs:vector 3 float32)))))
scheme@(guile-user)> (define foo (bytestructure foo_t '((s ((x 1)
(y 2.2)))
(z 3)
(t #(0 1 2)))))
scheme@(guile-user)> (bytestructure-ref foo 's 'x)
$1 = 1
scheme@(guile-user)> (bytestructure-ref foo 't 1)
$2 = 1.0
scheme@(guile-user)> (define testfunc
(bs:pointer->proc
foo_t ;return value type
(dynamic-func "testfunc" (dynamic-link "libtest"))
(list foo_t))) ;list of argument types
scheme@(guile-user)> (define foo2 (testfunc foo))
scheme@(guile-user)> (bytestructure-ref foo2 's 'x)
$3 = 2
scheme@(guile-user)> (bytestructure-ref foo2 't 1)
$4 = 2.0
scheme@(guile-user)>
--- end transcript ---
To recap:
We have a C file which defines a struct, and a function that
takes such a struct and returns a new one with some of the fields'
values modified. We make this a shared library.
In Guile, we can express the same struct layout via a bytestructure
descriptor object, which we put in the variable foo_t. We can also
instantiate such a struct in memory, reified in Scheme as a
bytestructure object, which we put into the variable foo.
We use bs:pointer->proc to create a Scheme procedure that wraps the
function in our shared library; bs:pointer->proc allows us to use the
foo_t descriptor in the return value and argument list declarations.
We can now call this procedure with the struct we had created from
Scheme and put in the variable foo as a bytestructure object. The new
struct returned by the C function is automatically turned into a
bytestructure object that can be used from Scheme with the
bytestructures API.
Neat, isn't it?
If test.c instead contained:
void testfunc(foo_t *arg)
{
++arg->s.x;
++arg->s.y;
++arg->z;
++arg->t[0];
++arg->t[1];
++arg->t[2];
}
(i.e. testfunc now takes a struct pointer and modifies it in-place)
then we could create our procedure like:
scheme@(guile-user)> (import (prefix (system foreign) ffi:))
scheme@(guile-user)> (define testfunc
(bs:pointer->proc
ffi:void
(dynamic-func "testfunc" (dynamic-link "libtest"))
(list '*)))
and things would work like this:
scheme@(guile-user)> (testfunc foo)
scheme@(guile-user)> (bytestructure-ref foo 's 'x)
$3 = 2
scheme@(guile-user)> (bytestructure-ref foo 't 1)
$4 = 2.0
Magic!
Note that while bytestructures supports unions and bit-fields, libffi
and therefore (system foreign) does not, so the above won't work yet for
types in which unions or bitfields are involved. Unions can possibly be
supported with a little hack, which I'll look into later.
I have not yet made the semantics of bs:pointer->proc concrete, and it's
yet undocumented. Feel free to ask me questions here or on Freenode
under the nick 'taylan'. Comments are welcome too!
Taylan
next reply other threads:[~2016-05-31 19:29 UTC|newest]
Thread overview: 6+ messages / expand[flat|nested] mbox.gz Atom feed top
2016-05-31 19:29 Taylan Ulrich Bayırlı/Kammer [this message]
2016-05-31 20:13 ` Bytestructures, FFI Amirouche Boubekki
2016-05-31 21:07 ` Taylan Ulrich Bayırlı/Kammer
2016-06-01 21:40 ` Ludovic Courtès
2016-06-02 0:07 ` Taylan Ulrich Bayırlı/Kammer
2016-06-02 7:04 ` Ludovic Courtès
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
List information: https://www.gnu.org/software/guile/
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=87oa7mauvd.fsf@T420.taylan \
--to=taylanbayirli@gmail.com \
--cc=guile-user@gnu.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).