Subject: [clisp-list] Changing variables in a C dynamic library

From: "Hoehle, Joerg-Cyril" <[email-masked]>
Date: Thu Feb 12 02:48:06 2004

Hi,

Nico Swart is confused about some aspects of what the CLISP FFI calls places. He writes:

>Is this because of 'fresh' variables or what am I missing ?
I'm not sure what you mean by fresh, but indeed, your C code is passed a new (stack-allocated) *struct bar, not the distinguished address of my_struct.

>(def-c-var p_my_struct (:LIBRARY "libtest2.so.0") (:type (c-ptr bar)))
Please enter
	p_my_struct
at the Lisp prompt. You'll obtain a Lisp-level copy of the struct contents, as a Lisp structure (or class). Each time p_my_stuct is evaluated, this copying occurs.

When you then write
> (get-own-c-float-1 p_my_struct)
you pass this Lisp instance to the call-out function, which stack-allocates a corresponding C struct and fills it from the Lisp values.
I.e. For each call, you pass in a new (pointer to a) struct.

Since you don't use :out parameter mode, the bar.out slot that you fill is never read back by Lisp.

What you may want is working with references. These materialize in CLISP as #<FOREIGN-VARIABLE...> objects.

Please try out, at the Lisp prompt:
(ffi:c-var-object p_my_struct)  -> #<FOREIGN-VARIABLE...>
(ffi:c-var-address p_my_struct) -> #<FOREIGN-ADDRESS ...>

If you want to work with references, you must define your foreign function as taking a c-pointer instead of (c-ptr bar) and pass it a reference or address. E.g.
(def-call-out get-own-c-float-1
   (:arguments (ptr c-pointer))

You cannot have a single call-out function accept either Lisp-level structures/classes or foreign references interchangeably.

You then call the function using either:
(get-own-c-float-1 (ffi:c-var-object p_my_struct))
(get-own-c-float-1 (ffi:c-var-address p_my_struct))

The #<FOREIGN-...> are normal Lisp objects. You may store them away like anything else.
(defparameter *my-stuct-ref* (ffi:c-var-object p_my_struct))
(get-own-c-float-1 *my-stuct-ref*)

You could interface like this (untested):
(with-c-var (data 'bar)
  (setf (slot data 'x) 100.0d0)
  (setf (slot data 'y)  20.0d0)
  (values
   (get-own-c-float-1m (c-var-address data))
   ;;notabene: I consider this part bad code:
   ;;normally I'd only dereference :out slots when the
   ;; function returns a success indicator. Otherwise the content
   ;; may be undefined, and reading it may result in a conversion
   ;; error (not all random bit patterns represent readable data),
   ;; certainly not one's intent.
   (slot data 'out)))

You could also try to take advantage of the CLISP FFI's :in and :out modes. These do not feature struct-level granularity. Instead, you must split them at the function parameter level. I.e. use a struct for :in, and another for :out values.

Try to not create a mixed structure with some :in and some :out values, as you currently have. Distinguished in and out modes IMHO provide for a better design, in the sense that it is more amenable to cross-language interfacing, performance tuning, database interaction and distributed computing.

It also does not require you to initialize :out slots.
(with-foreign-object (p 'bar (make-bar :x 10.0d0 :y 20.0d0))
  (foreign-value p))
*** - NIL cannot be converted to the foreign type DOUBLE-FLOAT
you'd have to add :out 0.0d0 to make-bar.

Of course, I myself wrote low-level C code with mixed in and out slots -- but at least, it was a conscious and weighted decision, instead of "I always did it this way and never thought about any other one".

Regards,
	Jörg Höhle.
For reference:
>struct bar {
>    double x, y;
>    double out;
>};
>(def-call-out get-own-c-float-1
>   (:library "libtest2.so.0")
>   (:language :stdc)
>   (:name "test_dll2")
>   (:arguments (ptr (c-ptr bar)))
>   (:return-type double-float))

---

From: Nico <[email-masked]>
Date: Thu Feb 12 16:51:01 2004

Hi,

>I'm not sure what you mean by fresh, but indeed, your C code is passed a new (stack-allocated) *struct bar, not the distinguished address of my_struct.
The 'fresh' term is used in the implementation notes. I understood that 
to mean a stack created object.

>Of course, I myself wrote low-level C code with mixed in and out slots -- but at least, it was a conscious and weighted decision, instead of "I always did it this way and never thought about any oth…

I resolved my wrong view as this by using 'c-pointer' in the declaration 
and 'c-var-object' in the call exactly as you pointed out. Although I 
resolved it, I appreciate
your explanation and suggestions as it added to my understanding, thanks.

Regards,
Nico Swart