Hello,
I'm working on a CFFI interface to the Perl API (I thought it would be fun - ha!) and I need to be able to do C-style pointer arithmetic. That is, I have a foreign pointer declared with DEFCVAR and I need to alter the address stored there. I couldn't see a way to do this with CFFI, so I created a new foreign type called :ADDRESS that would treat the foreign pointer as an integer value that I could modify.
Here's my code:
(defvar *pointer-size* (foreign-type-size :pointer) "The size of a pointer on the current platform, in bytes.")
(ecase *pointer-size* (1 (defctype :address :uint8)) ; unlikely (2 (defctype :address :uint16)) ; possible (4 (defctype :address :uint32)) ; most common (8 (defctype :address :uint64))) ; possible
(defmacro address-incf (address &optional (n 1)) "Increment ADDRESS by N pointers, like ++i on a C pointer." `(incf ,address (* ,n *pointer-size*)))
(defmacro address-decf (address &optional (n 1)) "Decrement ADDRESS by N pointers, like --i on a C pointer." `(decf ,address (* ,n *pointer-size*)))
(defmacro address-ref (address type) "Dereference ADDRESS to get the TYPE it points to, like MEM-REF." `(mem-ref (make-pointer ,address) ,type))
This does work for my purposes, but is there a better way using CFFI internals?
Thanks, -Stuart
Stuart Sierra wrote:
Hello,
I'm working on a CFFI interface to the Perl API (I thought it would be fun - ha!) and I need to be able to do C-style pointer arithmetic. That is, I have a foreign pointer declared with DEFCVAR and I need to alter the address stored there. I couldn't see a way to do this with CFFI, so I
You've got one half of the answer already -- `make-pointer'. Now see `pointer-address' (http://common-lisp.net/project/cffi/manual/html_node/pointer_002daddress.htm...).
Stephen Compall s11@member.fsf.org writes:
Stuart Sierra wrote:
I'm working on a CFFI interface to the Perl API (I thought it would be fun - ha!) and I need to be able to do C-style pointer arithmetic. That is, I have a foreign pointer declared with DEFCVAR and I need to alter the address stored there. I couldn't see a way to do this with CFFI, so I
You've got one half of the answer already -- `make-pointer'. Now see `pointer-address' (http://common-lisp.net/project/cffi/manual/html_node/pointer_002daddress.htm...).
But POINTER-ADDRESS is not setf-able, is it? I want to do this:
(defcvar "stack_pointer" :pointer)
(incf (pointer-address *stack-pointer*))
where "stack_pointer" is a global variable in an external library. But...
The function (SETF POINTER-ADDRESS) is undefined. [Condition of type UNDEFINED-FUNCTION]
-Stuart
On 2006-jul-14, at 02:38, Stuart Sierra wrote:
But POINTER-ADDRESS is not setf-able, is it?
Right. AFAICT, some of the Lisps supported by CFFI don't have mutable pointers.
I want to do this: (defcvar "stack_pointer" :pointer)
(incf (pointer-address *stack-pointer*))
See inc-pointer. <http://common-lisp.net/project/cffi/manual/html_node/ inc_002dpointer.html>
(setf *stack-pointer* (inc-pointer *stack-pointer* 1)) should work. You can abstract that with a simple macro:
(defmacro incf-pointer (place &optional (offset 1)) `(setf ,place (inc-pointer ,place ,offset)))
Any objections to adding this macro to CFFI?
Luís Oliveira wrote:
On 2006-jul-14, at 02:38, Stuart Sierra wrote:
But POINTER-ADDRESS is not setf-able, is it?
Right. AFAICT, some of the Lisps supported by CFFI don't have mutable pointers.
I suspected as much. I couldn't find it in SBCL's FFI, at least.
(setf *stack-pointer* (inc-pointer *stack-pointer* 1)) should work. You can abstract that with a simple macro:
(defmacro incf-pointer (place &optional (offset 1)) `(setf ,place (inc-pointer ,place ,offset)))
But won't that set the value that *STACK-POINTER* points to? In my example, *STACK-POINTER* is not a Lisp object but a foreign global variable accessed by name with DEFCVAR. As I understand CFFI's :POINTER type, the above macro will modify the memory location that PLACE points to but will not modify PLACE itself.
-Stuart
Stuart Sierra mail@stuartsierra.com writes:
(setf *stack-pointer* (inc-pointer *stack-pointer* 1)) should work. You can abstract that with a simple macro:
(defmacro incf-pointer (place &optional (offset 1)) `(setf ,place (inc-pointer ,place ,offset)))
But won't that set the value that *STACK-POINTER* points to? In my example, *STACK-POINTER* is not a Lisp object but a foreign global variable accessed by name with DEFCVAR. As I understand CFFI's :POINTER type, the above macro will modify the memory location that PLACE points to but will not modify PLACE itself.
No because your *STACK-POINTER* is a symbol-macro that expands into (%var-accessor-*stack-pointer*) or something like that. Here's an example in cffi-tests (var_pointer is a foreign variable of type void*):
CFFI-TESTS> (get-var-pointer '*var-pointer*) #.(SB-SYS:INT-SAP #X00396030) CFFI-TESTS> *var-pointer* #.(SB-SYS:INT-SAP #X00000000) CFFI-TESTS> (setf *var-pointer* (inc-pointer *var-pointer* 1)) #.(SB-SYS:INT-SAP #X00000001) CFFI-TESTS> (get-var-pointer '*var-pointer*) #.(SB-SYS:INT-SAP #X00396030) CFFI-TESTS> *var-pointer* #.(SB-SYS:INT-SAP #X00000001)
*var-pointer* == (mem-ref (get-var-pointer '*var-pointer*) :pointer)
HTH
On Fri, 2006-07-14 at 13:30 +0100, Luís Oliveira wrote:
See inc-pointer. <http://common-lisp.net/project/cffi/manual/html_node/ inc_002dpointer.html>
(setf *stack-pointer* (inc-pointer *stack-pointer* 1)) should work. You can abstract that with a simple macro:
(defmacro incf-pointer (place &optional (offset 1)) `(setf ,place (inc-pointer ,place ,offset)))
Any objections to adding this macro to CFFI?
This evaluates the PLACE forms twice. If I follow correctly, this can be more generally solved with a cross-platform setf-expander for POINTER-ADDRESS that requires its PTR arg to be a place, whereupon things like (incf (pointer-address *ptr*)) and any other place-modifier should work.
On Fri, 14 Jul 2006 11:01:27 -0500, Stephen Compall said:
On Fri, 2006-07-14 at 13:30 +0100, Luís Oliveira wrote:
See inc-pointer. http://common-lisp.net/project/cffi/manual/html_node/ inc_002dpointer.html
(setf *stack-pointer* (inc-pointer *stack-pointer* 1)) should work. You can abstract that with a simple macro:
(defmacro incf-pointer (place &optional (offset 1)) `(setf ,place (inc-pointer ,place ,offset)))
Any objections to adding this macro to CFFI?
This evaluates the PLACE forms twice. If I follow correctly, this can be more generally solved with a cross-platform setf-expander for POINTER-ADDRESS that requires its PTR arg to be a place, whereupon things like (incf (pointer-address *ptr*)) and any other place-modifier should work.
Looks like a job for DEFINE-MODIFY-MACRO to me.
Stephen Compall s11@member.fsf.org writes:
On Fri, 2006-07-14 at 13:30 +0100, Luís Oliveira wrote:
(defmacro incf-pointer (place &optional (offset 1)) `(setf ,place (inc-pointer ,place ,offset)))
This evaluates the PLACE forms twice.
Doh!
If I follow correctly, this can be more generally solved with a cross-platform setf-expander for POINTER-ADDRESS that requires its PTR arg to be a place, whereupon things like (incf (pointer-address *ptr*)) and any other place-modifier should work.
Hmm. I find that a bit confusing. (incf (pointer-address foo)) really makes it look look that foo's address is mutable.
So, I propose incf-pointer again, this time without the double evaluation bug:
(define-modify-macro incf-pointer (&optional (offset 1)) inc-pointer)
Luís Oliveira wrote:
If I follow correctly, this can be more generally solved with a cross-platform setf-expander for POINTER-ADDRESS that requires its PTR arg to be a place, whereupon things like (incf (pointer-address *ptr*)) and any other place-modifier should work.
Hmm. I find that a bit confusing. (incf (pointer-address foo)) really makes it look look that foo's address is mutable.
No more than (incf (ldb (byte 2 1) foo)) makes it look as if the integer itself at FOO is mutable, unless you must take into account the prevalence of 1-arg slot accessors.
So, I propose incf-pointer again, this time without the double evaluation bug:
(define-modify-macro incf-pointer (&optional (offset 1)) inc-pointer)
Simple enough for anyone to duplicate for future modifiers, too. I should spend more time expanding my symbology.
For posterity, here is a setf-expander:
(define-setf-expander pointer-address (ptr &environment env) (multiple-value-bind (temp-vars value-forms store-vars store-form access-form) (get-setf-expansion ptr env) (destructuring-bind (ptr-store-var) store-vars (with-unique-names (store-var) (values temp-vars value-forms (list store-var) `(let ((,ptr-store-var (make-pointer ,store-var))) ,store-form ,store-var) `(pointer-address ,access-form))))))