[ann] go-ffi

721 views
Skip to first unread message

Sebastien Binet

unread,
May 6, 2012, 2:07:52 PM5/6/12
to golang-nuts
hi there,

just a quick mail to announce a little (experimental) side project I
have been hacking on during this extended week-end:
go-ffi, the cgo-based bindings to libffi (and dlopen)

it is here:
https://bitbucket.org/binet/go-ffi

with some documentation:


The ``ffi`` package wraps the ``libffi`` ``C`` library (and
``dlopen/dlclose``) to provide an easy way to call arbitrary functions
from ``Go``.

Installation
------------

``ffi`` is go-get-able::

$ go get bitbucket.org/binet/go-ffi/pkg/ffi


Example
-------

::

// dl-open a library: here, libm on macosx
lib, err := ffi.NewLibrary("libm.dylib")
handle_err(err)

// get a handle to 'cos', with the correct signature
cos, err := lib.Fct("cos", ffi.Double, []Type{ffi.Double})
handle_err(err)

// call it
out := cos(0.).Float()
println("cos(0.)=", out)


Limitations/TODO
-----------------

- no check is performed b/w what the user provides as a signature and
the "real" signature

- it would be handy to just provide the name of the library (ie: "m")
instead of its filename (ie: "libm.dylib")

- it would be handy to use some tool to automatically infer the "real"
function signature

- it would be handy to also handle structs


Documentation
-------------

http://go.pkgdoc.org/bitbucket.org/binet/go-ffi/pkg/ffi


hth,
-s

Carlos Castillo

unread,
May 7, 2012, 11:12:34 AM5/7/12
to golan...@googlegroups.com
I had developed my own FFI wrapper for go, but never published it since I found that although wrapping dlopen and ffi were actually for the most part straightforward, to actually get useful functionality like what we currently get from CGO is quite a different matter entirely.

I have posted my code online at https://github.com/cookieo9/goffi not to be in competition with your package, but to provide inspiration in case there are certain decisions / designs you might find useful. I have no intention (at the moment) to develop it as I found no cases where it would be an advantage to use it over CGO, especially to counter the drawbacks.

Some things you might notice about my code (if you decide to look):
  • I try to avoid having the user need to deal with unsafe.Pointers (to keep them out of trouble) and thus use uintptrs in the API
  • I check the parameter-types on the call versus the ones passed when the function was created
  • I separated the lower-level packages (ffi & dlopen) from a higher level wrapper (fcall) so that quick easy to use calls with default options are simple (only need fcall), but power users can mix and match using the exact-same api that fcall uses

Some things I liked about your code:

  • Your functions returning reflect.Value (vs interface{}) do have the benefit that the values are slightly easier to use (no type assertions)
  • You automatically handle strings as pointers, which makes it easier to use them (I provide equivalents to the C.*String functions)

Before I gave up, I was thinking about making arguments/return types optionally satisfy some get/set interface so that a users own types/structs could be passed directly to FFI functions without needing to modify the FFI wrapper.

I am looking forward to see how you wrap ffi_closure, because without access to a C-parser, that is probably the most useful feature of FFI vs. CGO and if done well would make programming callback code much easier than the current export functionality, especially since you can't export Go closures/anonymous functions:

func fn (i int) { log.Println(i) }

ptr := ffi.Callback(fn)

C.set_callback(ptr)

- Carlos

Sebastien Binet

unread,
May 7, 2012, 1:14:51 PM5/7/12
to Carlos Castillo, golan...@googlegroups.com
Carlos,

On Mon, May 7, 2012 at 5:12 PM, Carlos Castillo <cook...@gmail.com> wrote:
> I had developed my own FFI wrapper for go, but never published it since I
> found that although wrapping dlopen and ffi were actually for the most part
> straightforward, to actually get useful functionality like what we currently
> get from CGO is quite a different matter entirely.
>
> I have posted my code online at https://github.com/cookieo9/goffi not to be
> in competition with your package, but to provide inspiration in case there
> are certain decisions / designs you might find useful. I have no intention
> (at the moment) to develop it as I found no cases where it would be an
> advantage to use it over CGO, especially to counter the drawbacks.
>
> Some things you might notice about my code (if you decide to look):
>
> I try to avoid having the user need to deal with unsafe.Pointers (to keep
> them out of trouble) and thus use uintptrs in the API
> I check the parameter-types on the call versus the ones passed when the
> function was created
> I separated the lower-level packages (ffi & dlopen) from a higher level
> wrapper (fcall) so that quick easy to use calls with default options are
> simple (only need fcall), but power users can mix and match using the
> exact-same api that fcall uses

yeah, I've thought about having a "low-level" and a "convenience" API
for ffi too.
yours seem nice enough, I guess I'll just reap them off :}

> Some things I liked about your code:
>
> Your functions returning reflect.Value (vs interface{}) do have the benefit
> that the values are slightly easier to use (no type assertions)
> You automatically handle strings as pointers, which makes it easier to use
> them (I provide equivalents to the C.*String functions)
>
> Before I gave up, I was thinking about making arguments/return types
> optionally satisfy some get/set interface so that a users own types/structs
> could be passed directly to FFI functions without needing to modify the FFI
> wrapper.

that's also something that I tinkered with but left it for some other
week-end, leaving some more time to sink in.
I actually thought that would be more or less what the ffi_arg and
ffi_sarg were for...

>
> I am looking forward to see how you wrap ffi_closure, because without access
> to a C-parser, that is probably the most useful feature of FFI vs. CGO and
> if done well would make programming callback code much easier than the
> current export functionality, especially since you can't export Go
> closures/anonymous functions:
>
> func fn (i int) { log.Println(i) }
>
> ptr := ffi.Callback(fn)
>
> C.set_callback(ptr)

for the C-parser, I thought about plugging in go-clang (or some sort
of interface a-la sql/driver in case requiring clang to be too much of
a dependency, of course, if somebody already has a go-based
c-parser... speak-up.)
getting the proper signature or rather: the AST from the string
representation of that signature, is rather easy using go-clang.
the next step would be to just provide the header+library and get an
automatically generated ffi-binding code, leveraging clang on the way.

I came to libffi for the callbacks, but I haven't had time to crack
them open yet.

(btw, nice package!)

-s

Andrea Fazzi

unread,
May 7, 2012, 1:20:24 PM5/7/12
to Sebastien Binet, golan...@googlegroups.com
2012/5/7 Sebastien Binet <seb....@gmail.com>:
Not Go related but perhaps it can be of some interest for you:

https://github.com/ffi/ffi-swig-generator

Cheers,
Andrea

Sebastien Binet

unread,
May 7, 2012, 1:30:13 PM5/7/12
to Andrea Fazzi, golan...@googlegroups.com
indeed. thanks.

-s

James McKaskill

unread,
May 7, 2012, 1:31:34 PM5/7/12
to Sebastien Binet, Carlos Castillo, golan...@googlegroups.com
On Mon, May 7, 2012 at 1:14 PM, Sebastien Binet <seb....@gmail.com> wrote:
for the C-parser, I thought about plugging in go-clang (or some sort
of interface a-la sql/driver in case requiring clang to be too much of
a dependency, of course, if somebody already has a go-based
c-parser... speak-up.)
getting the proper signature or rather: the AST from the string
representation of that signature, is rather easy using go-clang.
the next step would be to just provide the header+library and get an
automatically generated ffi-binding code, leveraging clang on the way.

I was thinking of porting my luaffi library [1] at some point over to go. This has a mini C parser + all the ffi stuff built in.

EG in lua

ffi.cdef('int printf(const char* fmt, ...)')
ffi.C.printf("hello world %d\n", 3)

I'm not sure on how useful it would really be as CGO works quite well. The advantage with lua is that writing stuff with the lua api is nice, but its excessively verbose.


-- James
Reply all
Reply to author
Forward
0 new messages