guile-devel
[Top][All Lists]
Advanced

[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




reply via email to

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