[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: FF-example.c



>This question concerns the C code in MCL 2.0:examples:FF
>examples:ff-example.c. I'd like to know what the following define
>statements are trying to do:
>define CDR(x) (* ((long *) (x)))
>
>define CAR(x) (* (((Ptr *) (x)) -1))
>
>define MACPTR_PTR(x) (* (Ptr *) (((long) (x)) +7)))
>
>The definitions are the refered by
>CDR(* x), CAR(* x) and (MACPTR_PTR(* x)  where x is
>type defined as             long *x;
>
>The exact code is as follows:
>growptr(x)
>long *x;
>{
>Ptr newp;
>if (MACPTR_PTR(CAR(*x))) DisposPtr(MACPTR_PTR(CAR(*x)));
>newp = NewPtr((CDR(*x))>>FIXNUM_SHIFT);
>MACPTR_PTR(CAR(*x))=newp;
>}
>
>The lisp FFI defines the above function as
>
>(deffcfun (grow-ptr "growptr") ((cons :lisp-ref) :novalue))
>
>The lisp call is as follows:
>(grow-ptr (cons (#_NewPtr 5) 10))
>_NewPtr 5) 10))
>
>I understand the overall function of the code; my proble lies in the
>particular define stamements.

In order to understand this code, you need to understand how MCL
represents conses and macptrs. You can get a start on this by reading
the "Lisp Object Representation" section of the file "ccl:library;lap.doc"
and looking at the constants in "ccl:library;lispequ.lisp".

A CONS is a pointer to the second of two longwords. The first longword
is the CAR of the cons. The second longword (the one at which the CONS
points) is the CDR.

It seems to me that CDR would be more consistently (with the
CAR & MACPTR-PTR definitions) stated as:

#define CDR(x) (* ((Ptr *) (x)))

This would have necessitated casting the result to long in its
use in grow_ptr.

The definitions do the standard "I don't know what type the argument
I'm getting is so I'll cast it to where it needs to be, nor do I know
whether I need to put parens around it so I will just to be careful"
that is always necessary when writing C macros.

A MACPTR is a lisp uvector. A uvector has 4 bytes of GC overhead,
1 byte of subtype, 3 bytes of length, then length bytes of data.
A MACPTR is tagged as 1 in the low three bits, meaning that it
points one byte into the 8 byte header. Hence MACPTR_PTR is accessing the
first data word in the MACPTR, 1+7 bytes from the start of the header.
This is where the memory address that the MACPTR boxes is stored.

A lisp fixnum (passed in the CDR of the argument) is tagged as 0 in the
low three bits. Hence you need to right shift it 3 (FIXNUM_SHIFT) bits
to get a C long.

grow_ptr could have been written without using the macros as follows
(though I may have made some mistakes due to my infrequent use of C):

growptr (consptr)
  Ptr *consptr, macptr, macptr_ptr;
  long car, cdr;  
{
  Ptr newp;
  car = (long) *(*consptr - 1);
  cdr = (long) **consptr;
  macptr = (Ptr) (car + 7)
  macptr_ptr = (Ptr) (* macptr)
  if (macptr_ptr) DisposPtr(macptr_ptr);
  newp = NewPtr(cdr >> FIXNUM_SHIFT);
  *macptr = newp;
}

The reason for doing this with macros is that if the representation
changes in the future (and Gary has already swapped CAR & CDR and
eliminated 4 of the 8 bytes of overhead for a uvector), you can simply
change the macros and recompile.

Clear as mud?