This is the mail archive of the guile@cygnus.com mailing list for the guile project.
Index Nav: | [Date Index] [Subject Index] [Author Index] [Thread Index] | |
---|---|---|
Message Nav: | [Date Prev] [Date Next] | [Thread Prev] [Thread Next] |
> ** The semantics of smob marking have changed slightly. > > The smob marking function (the `mark' member of the scm_smobfuns > structure) is no longer responsible for setting the mark bit on the > smob. [cut] I have never written a smob type or (for that matter) used Guile much at all. (I read this list mainly out of an interest in languages and translators.) But I will claim some expertise, because I once implemented the equivalent of smobs for Emacs Lisp. I was therefore curious to see whether smobs had a similar interface to the one I devised. Here is what I found in smob.h (19980419): typedef struct scm_smobfuns { SCM (*mark) SCM_P ((SCM)); scm_sizet (*free) SCM_P ((SCM)); int (*print) SCM_P ((SCM exp, SCM port, scm_print_state *pstate)); SCM (*equalp) SCM_P ((SCM, SCM)); } scm_smobfuns; I assume this defines an "abstract base class" for deriving new Scheme pseudo-types whose methods are implemented in C. My ELisp smobs (which I called "foreign objects") had a similar virtual table structure, but with seven methods instead of four. I'll describe them below; perhaps you would comment on the desirability or feasibility of adding some of them to the smob interface. >From my lisp.h: #ifdef LISP_FOREIGN_OBJECT_TYPE /* struct Lisp_Foreign_Object can hold arbitrary binary data not directly managed by Lisp. The VPTR points to a table of object methods that are called at various times. The TYPE-OF method is called by Ftype_of() and must return the same (`equal') object each time it is called with a given argument. DESTROY is called after the sweep phase of garbage collection, when the object is to be freed. The MARK and SWEEP methods may be called during the mark and sweep phases, repectively. MARK must recursively call mark_object() on all contained Lisp objects. SWEEP is called when our object is not referenced by other data, but before it is to be destroyed. If SWEEP returns `nil', the object is not subsequently destroyed. TO_STRING is used by the printer and must return a string. All foreign objects are printed in hash notation. EQUAL is used in the recursive equality tester. It should pass its DEPTH argument to recursive_equal() when comparing subobjects, to guard against circularity. EQUAL may assume that the `type-of' both args is the same (`equal', not `eq'). (Under the current implementation, the VPTRs will be the same, too.) EQUAL must return a boolean value indicating whether its first two arguments are `equal'. The CALL method is called when our object appears in functional position. Any of the function pointers may be 0, in which case a default behavior will be used. Each of the methods corresponds to and is called only from within one enumeration of complex Lisp data types. (Typically a switch statement in C code.) The "foreign" object type effectively extends these enumerations, and thus the typespace, ad infinitum. */ struct Lisp_Foreign_Object_VTable { Lisp_Object (*type_of) P_ ((Lisp_Object self)); void (*destroy) P_ ((Lisp_Object self)); void (*mark) P_ ((Lisp_Object self)); Lisp_Object (*sweep) P_ ((Lisp_Object self)); Lisp_Object (*to_string) P_ ((Lisp_Object self)); int (*equal) P_ ((Lisp_Object self, Lisp_Object other, int depth)); Lisp_Object (*call) P_ ((Lisp_Object self, int nargs, Lisp_Object *args)); }; struct Lisp_Foreign_Object { int type : 16; /* = Lisp_Misc_Foreign_Object */ int spacer : 16; struct Lisp_Foreign_Object_VTable *vptr; void *data; Lisp_Object lisp_data; }; #endif /* LISP_FOREIGN_OBJECT_TYPE */ I'll assume that your `mark' corresponds to my `mark', your `equalp' corresponds to my `equal', and `print' and `to_string' are roughly equivalent. At first, I used a single `destroy' the way I assume the smob `free' function works. Later, I realized that the destructor might want to call Lisp code, which shouldn't happen while any mark bits are set. So I split object destruction into `sweep', which is called during gc_sweep(), and `destroy', called when it is safe to invoke user code. It was also useful to allow the sweep method to return a value indicating that the object was still referenced implicitly and should not be freed yet. (I see the smob `free' returns a value; can that be used to prevent destruction?) The `type_of' method exists because there are conceptually different types of foreign object, which may share the same virtual table. I called it from the Lisp builtin `type-of' function (not sure if there's a Scheme counterpart) and when printing. (If type_of was a null pointer, you got #<foreign-object (output of to_string)>, similar to the way I imagine smobs are printed.) Finally, I added `call' to simplify the conversion of foreign code objects. Originally, when wrapping code objects, I constructed a lambda expression that passed the foreign object and its arguments to a special subr designed for the purpose. This was inelegant, so I allowed objects to define a `call' method, putting them on a par with the `subr' type. Of course, this required a change to the evaluator. Am I right in my assumptions about the smob interface? Would you like me to take a stab at documenting smobs (if it hasn't been done)? Cheers -John