[Ecls-list] How to wrap C++ classes using ECLs foreign function interface?

Matthew Mondor mm_lists at pulsar-zone.net
Wed Jul 17 16:28:19 UTC 2013

On Wed, 17 Jul 2013 21:43:00 +0900
Dietrich Bollmann <dietrich at formgames.org> wrote:

> - Functions might be faster than methods
> Something like this?:
> === definitions ===
> (defun make-cube (&key (size 1.0))
>   (ffi:c-inline (size) (:double) :pointer-void "new Cube(#0)" :one-liner t))
> (defun cube-get-size (cube)
>   (ffi:c-inline (cube) (:pointer-void) :double "((Cube*) #0)->getSize()"
> :one-liner t))
> [...]


> > (although there also is the concept of sealed classes, which could also
> > help)
> >
> [...]
> Is something like this implemented for ECL?  If so I would prefer this
> approach as encoding the "class" in the name of the wrapper functions makes
> the code unnecessarily verbose.

Unfortunately I have no personal experience with sealed classes, but I
remember reading on this list about it at least once, and by grepping
the ECL src/clos/ code, it seems that it optimizes slot accessors if it
can for sealed classes.  These classes are most probably no longer
expansible after they're sealed.  I'm not sure of the proper interface
to use to seal them though (there exists
http://ecls.sourceforge.net/new-manual/ch29s05.html but it's not yet
documented :)

What I see that might be relevant:

Used in boot.lsp for the method-combination class):
(setf (slot-value (find-class 'method-combination) 'sealedp) t)

Seen in hierarchy.lsp:
(sealedp :initarg :sealedp :initform nil :accessor class-sealedp)

Then in std-accessors.lsp, there is the std-class-sealed-accessors
function which is used instead of std-class-accessors for sealed
classes in std-class-generate-accessors (at least for fixnum).

> > - MAKE-CUBE (or a variant) could potentially instanciate the object
> >   without adding an implicit finalizer, with a corresponding WITH-CUBE
> >   or WITH-OBJECT macro responsible for finalization in UNWIND-PROTECT.
> >   If I remember the implicit finalization system may be slower
> >   (probably a good idea to test instead of trusting my word on this).
> >   WITH-* style macros are also a very common lispy interface, require
> >   very few indentation and permit explicit, immediate finalization as
> >   an option.
> >
> Like this?:
> [...]
> (defmacro with-cube ((var &rest args) &body body)
>   `(let ((,var (make-cube , at args)))
>      (unwind-protect
>  (progn , at body)
>        (cube-delete ,var))))
> [...]

The idea is there, it's however generally better to also store an
internal link to the instance for finalization to always succeed no
matter what the caller does.  Here's an example extracted from some of
my code (notice the s-sdl-surface):

(defmacro with-sdl-surface ((sdl-surface &optional bind-var) &body body)
  "Ensures to free the supplied SDL-SURFACE object after evaluating BODY,
which is useful as image buffers may be quite large.
If BIND-VAR is supplied, also binds BIND-VAR to SDL-SURFACE within BODY,
which allows to give a name to the binding when supplying a constructor
function as SDL-SURFACE."
  (let ((s-sdl-surface (gensym)))
    `(let* ((,s-sdl-surface ,sdl-surface)
            ,@(if bind-var `((,bind-var ,s-sdl-surface)) nil))
              , at body)
         (sdl-free-surface ,s-sdl-surface)))))

> In my case, I actually found out that Rhino doesn't like me to finalize any
> objects anyway and wants to manage them itself:  A lisp variable might get
> out of scope but still exist in the Rhino scene.  If I add a finalizer it
> forces the destruction of the Rhino object as well - causing Rhino to
> crash...

Hmm that seems odd and inconvenient... I'm not sure what to suggest
about this, does Rhino permit to send a notification, or to be told
that storage is managed by the caller?  Alternatively, if it uses its
own GC, does it provide facilities for rooting and references?  If it
has no such GC, a possibility might be that objects were not always
finalized/freed in the right order, causing some dependencies to still
have pointers to invalid objects or such...  But I have no experience
with Rhino whatsoever, and am not sure if with boehm-gc all should be
fine with it as long as all no-longer-wanted references are cleaned by
setting them to NULL/nil...

More information about the ecl-devel mailing list