[Openmcl-devel] best practice for C++ class access from CCL?
Jason E. Aten
j.e.aten at gmail.com
Sat Mar 26 07:21:29 PDT 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.
Best,
Jason
--
Jason E. Aten, Ph.D.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.clozure.com/pipermail/openmcl-devel/attachments/20110326/baf5fab6/attachment.htm>
-------------- 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)
;-55.0D0
(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
;nil
;;; 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
;;42
;; 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)
;nil
;; 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