[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: ffi for glutInit
From: |
Mark H Weaver |
Subject: |
Re: ffi for glutInit |
Date: |
Thu, 26 Jul 2012 12:33:56 -0400 |
User-agent: |
Mozilla/5.0 (X11; Linux x86_64; rv:14.0) Gecko/20120714 Thunderbird/14.0 |
On 07/25/2012 06:19 PM, Aleix Conchillo Flaqué wrote:
I have started working on bindings for OpenGL and GLUT.
https://github.com/aconchillo/guile-gl
Excellent! :-)
Now, I'm stuck with glutInit. The signature of the function is like this:
glutInit(int *argc, char **argv);
See: http://www.opengl.org/resources/libraries/glut/spec3/node10.html
I don't really know how to do the char** part, so I need some help here.
For the user, the guile procedure will simply look like (glutInit) and
internally (command-line) would be ideally used to construct the
char**, etc.
First of all, I should mention that although the dynamic FFI is very
convenient, it is not necessarily the best way to write a robust and
efficient library wrapper. Most notably, things like preprocessor
macros are not handled by the FFI, and there is currently a non-trivial
overhead for each call through the FFI. This overhead arises because
the FFI does not generate native code wrappers, but instead each wrapper
has a loop that iterates over the list of arguments and argument types,
and constructs the stack frame one argument at a time before calling the
C function. This is probably not significant in most cases, but for
small and fast C functions that might be used within an inner loop with
a high iteration count, you'd probably want to write the wrapper in C.
In this case, we have the problem that libffi, which is the basis for
Guile's dynamic FFI, does not support array types. However, in practice
we can pretend that arrays and structs have the same layout, and I
suspect that this works on most architectures. Here's one way to do it:
(use-modules (system foreign))
(define libglut-obj (dynamic-link "libglut"))
;; (glut-init args), where args is the complete list of command
;; arguments (starting with the program name), calls glutInit and
;; returns the (possibly) modified list of arguments.
(define glut-init
(let ((foo-init-raw (pointer->procedure
void
(dynamic-func "glutInit" libglut-obj)
(list '* '*)))
(saved-c-strings '()))
(lambda (args)
(let* ((num-args (length args))
(c-strings (map string->pointer args))
(argcp (make-c-struct (list int)
(list num-args)))
(argv (make-c-struct (make-list (+ 1 num-args) '*)
(append c-strings
(list %null-pointer)))))
(set! saved-c-strings (append c-strings saved-c-strings))
(foo-init-raw argcp argv)
(let ((argc (car (parse-c-struct argcp (list int)))))
(map pointer->string
(parse-c-struct argv
(make-list argc '*))))))))
;; Example usage
(set-program-arguments (glut-init (program-arguments)))
Note the use of 'saved-c-strings' to keep a reference to all of the C
string buffers that we ever pass to 'glutInit'. This is important
because the glut docs specify that 'glutInit' wants the original
unmodified 'argv' passed to 'main', which means that it can assume that
the strings will never be freed. 'string->pointer' returns a C string
buffer managed by the garbage collector, which means that the string may
be freed unless the GC can see a pointer to the _beginning_ of the string.
Happy hacking!
Mark