[Openmcl-devel] best practice for C++ class access from CCL?

Jason E. Aten j.e.aten at gmail.com
Sat Mar 26 14:21:29 UTC 2011

It seems I've answered my own question on this one.

I'll attach a file showing how to access C++ via SWIG from CCL, in case
anyone else has the same question.


Jason E. Aten, Ph.D.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.clozure.com/pipermail/openmcl-devel/attachments/20110326/baf5fab6/attachment.html>
-------------- next part --------------

;; sample CCL interactive lisp session that interacts with C++
;; tested on linux x86_64 ; CCL banner says:
;; Welcome to Clozure Common Lisp Version 1.7-dev-r14684M-trunk  (Linuxx8664)!

(:cd "/home/jaten/dj/test_ccl_ffi")
(require 'cffi)
(load (compile-file "testlib.lisp"))
(load (compile-file "testlib-clos.lisp"))

;(close-shared-library "/home/jaten/dj/test_ccl_ffi/testlib.so")
(open-shared-library "/home/jaten/dj/test_ccl_ffi/testlib.so")

;(cffi:foreign-funcall "forward" :double 11.0D0 :double 55.0D0 :double)
;(backward 11 66)
;in backward, returning first(11) - second(66)

(setf myd (make-instance 'd))
; calls the constructor

; redudant now, but if you wanted to call the ctor again, here's how:
(initialize-instance myd) 
; calls the constructor, again.

; call a method on instance, equivalent to the C++ myd->D::my-d-func();
(my-d-func myd)
; D::my_D_func() called!
; constructor in class D running!D::my_D_func() called! with _dint=-383373672   <- flush of io stream was can be slow. might see ctor message show up now. Figure out how to turn on flushes if need be -- refer to the mailing list posting by Gary Byers below.
;; from    Gary Byers <gb at clozure.com>
;; to      Sol Swords <sswords at centtech.com>
;; cc      openmcl-devel at clozure.com
;; date    Wed, Mar 23, 2011 at 2:49 PM
;; subject Re: [Openmcl-devel] timely output from an external-process-output-stream
;; mailing listopenmcl-devel.clozure.com
;; See also the C library functions 'setvbuf' and 'setbuf', either or both
;; of which may be available (and some OSes may offer other variants.)
;; Doing something like:
;;  setvbuf(stdout, NULL, _IONBF, 0);
;; before writing to stdout has the effect of making stdout "unbuffered"
;; (so all data written to it is immediately written to the underlying device.)

;; Allocating foreign objects.
;; Now make-heap-ivector is the Clozure CCL specific way of doing things, but it turns
;; out not to be necessary if you use "swig -c++ -cffi testlib.i".
;; This is because SWIG generates CLOS wrappers,
;; and they in turn invoke (via extern "C" wrappers) C++ functions 
;; that allocate with C++'s new on the C++ heap.
;; So it's nice to avoid make-heap-ivector here and use the CFFI, because it's more portable.
;;  But for reference, in case we need to interact with other objects that don't have SWIG bindings:
(multiple-value-bind (la lap)
    (make-heap-ivector 3 '(unsigned-byte 32))
  (setq a la) ;; the first one is the one to free...later when done with it.
  (setq ap lap)) ;; the second foreign pointer is to pass to foreign functions. DON'T free it!!!
;;; see the Clozure manual for full details.

(setf myc (make-instance 'cdemo))
;#<cdemo #x3020011C8CCD>

(type-of myc)
; cdemo

; you cannot pass the CLOS object to a C/C++ function directly...
(print_C_b myc)
;Error: value #<c #x30200100693D> is not of the expected type macptr.

; So, how do we get the foreign pointer out of the myc object... hmm... there was a way!

;; Here is how: this works to call a function that takes an object.
(print_C_b (slot-value myc 'ff-pointer))

; the result:
; in print_C_b where C.b = 0.000000
; nil
;;; YESSSS!! Success.

; stack-based automatic allocation of foreign sturct, and auto cleanup
(cffi:with-foreign-object (ptr 'S) ;; allocate an S struct and get a pointer ptr to it.
			  (format t "1st: ~A~%" (cffi:foreign-slot-value ptr 'S 'a)) ;; print the before value of ptr->a
			  (setf (cffi:foreign-slot-value ptr 'S 'a) 4242)          ;;
			  (format t "2nd: ~A~%" (cffi:foreign-slot-value ptr 'S 'a))
			  ; now use the struct in a call over in C/C++ land.
			  (print_struct_of_type_S_ ptr))

; this works!  printing comes back out-of-order, but no matter. We get back:
; in print_struct_of_type_S where s.a = 4242.  Then we add 10 to it and return it.
; 1st: 21480256  ; garbage still in memory from the fresh allocation of the struct
; 2nd: 4242      ; we set the value
; 4252           ; evidence that the return value from print_struct_of_type_S(s) actually succeeded.

;in print_C_b where C.b = 0.000000
;;; yeah! it works!
;; what doesn't work, is to tray and call the C method print_C_b and pass it the Lisp object directly:
;;; (print_C_b myc)
;;; Error: value #<cdemo #x30200105D7AD> is not of the expected type macptr.
(print_C_b (slot-value myc 'ff-pointer)) ; instead; this works

(cffi:with-foreign-object (ptr 'cdemo) ;; allocate an CDemo objecdt and get a pointer ptr to it.
			  (format t "type-of ptr: ~A~%" (type-of ptr))

; notes on how we figured out to use  cffi:foreign-slot-value  - by looking at the auto-cleanup macros:
;(macroexpand '(cffi:with-foreign-object (ptr 'S)
;			  (setf (cffi:foreign-slot-value ptr 'S 'a) 42)))
; (let ((#:size474 4)) (%stack-block ((ptr #:size474)) (setf (cffi:foreign-slot-value ptr 's 'a) 42)))
;; notice the (cffi:foreign-slot-value ptr 's 'a) call--- a key one!
;;  which is key to accessing  
; struct S {
;   int a;
; };

;;; Manual memory management:

;; how to allocate a struct on the heap... use cffi:foreign-alloc
(setf f-ptr (cffi:foreign-alloc 'S))
; this worked!
; #<A Foreign Pointer #x1767CC0>

; query the value of a struct field, here S.a :
(cffi:foreign-slot-value f-ptr 's 'a)
; 1165078168

; set the value:
(setf (cffi:foreign-slot-value f-ptr 'S 'a) 42)
;;Compiler warnings :
;;   In an anonymous lambda form at position 31: Undeclared free variable f-ptr

;; confirm that it has been set
(cffi:foreign-slot-value f-ptr 's 'a)
; 42
;; horray!

; call a function using the value
(print_struct_of_type_S_  f-ptr)
; worked!!!
; in print_struct_of_type_S where s.a = 42.  Then we add 10 to it and return it.
; 52

;; cleanup
;; this appears to work to deallocate the struct
(cffi:foreign-free f-ptr)

;; check the slot, it seems zeroed out. I guess we were lucky this didn't crash. Behavior probably undefined (here there be dragons).
(cffi:foreign-slot-value f-ptr 's 'a)
; 0

;;;;;;;;;;;;;;;;; end working examples of common lisp interaction with C++

More information about the Openmcl-devel mailing list