[Openmcl-devel] CCL::%GET-PTR --- When to use?
Gary Byers
gb at clozure.com
Thu Jan 6 23:51:25 PST 2005
On Fri, 7 Jan 2005, David Steuber wrote:
> I seem to be having endless confusion on when I need to use
> CCL::%GET-PTR when using the Carbon API for OS X. I think the problem
> lies in me not always being able to tell when a MACPTR is like a
> pointer or a pointer to a pointer. Has anyone come up with a good
> heuristic so as not to have to guess so often?
>
It general helps a lot to use things like RLET and PREF; they're
generally just syntactic sugar for lower-level constructs (including
%GET-PTR and friends), but they often make otherwise confusing
distinctions clearer. (If it's any consolation, I think that C
programmers - even experienced ones - often find some of the
same things to be confusing ...)
One thing to bear in mind is that the values of variables bound
by RLET are always of type "pointer to ...", and it sometimes
makes things clearer to name the variables with that in mind:
(rlet ((x :int)) ; X is a pointer to an :INT-sized
...) ; chunk of memory, it is uninitialized here
may be less clear than something like:
(rlet ((x-ptr :int)) ; X-PTR is a pointer ...
...)
Whatever naming convention one uses,
(rlet ((VAR0 FOREIGN-TYPE0)
...
(VARn FORIEGN-TYPEn))
...)
always binds each VARi to a pointer to an object to type FOREIGN-TYPEi,
and (unless there's a FOREIGN-VALUEi involved) the "value" of that
object is undefined (whatever random bits were in that block of
memory.)
That's as true for the "pointer-to-pointer" case as for other cases.
As a slightly contrived example, consider a C function like:
void get_nth_string(int n, char **stringbuf, int *maxlen);
A common C mistake (which is, unfortunately, very easy to replicate)
is to do something like:
void
bad_example()
{
int maxlen = 512;
char *string; /* so far, this is a pointer to nowhere */
get_nth_string(0, &string, &maxlen); /* This will probably segfault */
}
A better C example would look more like:
void
better_example()
{
int maxlen = 512;
char string[512]; /* or use malloc here */
get_nth_string(0, &string, &maxlen);
}
Translating into OpenMCL's FFI:
(defun bad-example ()
(rlet ((maxlen :int 512)
(string (:* :char)))
(#_get_nth_string 0 string maxlen)))
(That'll still probably segfault, becuse the (* :CHAR) pointer that
STRING points to doesn't point anywhere meaningful.)
(defun better-example ()
(rlet ((maxlen-ptr :int 512)
(string (:array :char 512)) ; allocate a string
(string-ptr (:* :char) string)) ; point to the string
(#_get_nth_string 0 string-ptr maxlen-ptr)))
Some of this advice starts to break down, because RLET and PREF don't
handle foreign arrays well (when they handle them at all.) I think
that unless you have to break down and use primitives (STACK-BLOCK.
%GET-PTR, etc.), it's often clearer to let the macros deal with the
details (to the extent that they do.)
I think that the fact that you can screw yourself "just like a C
programmer can" is ultimately a good thing (or at least more of a
good thing than not.) A C compiler might catch some errors at
compile-time (by the time any of this gets to the OpenMCL compiler,
it's just open-coding some memory-access function), but neither
environment is likely to catch the bad_example above.
More information about the Openmcl-devel
mailing list