Greets,

Instantiable Lucy class's will typically have two constructor-type functions:
new() and init().  Abstract classes will typically only have init().  

(The new/init bifurcation was mentioned briefly in a previous thread:
<http://mail-archives.apache.org/mod_mbox/lucene-lucy-dev/200903.mbox/%[email protected]%3e>)

When building objects from C, we usually call new();

    ANDQuery *and_query = ANDQuery_new(children);

All new() does is allocate the blank object and then invoke init():

    ANDQuery*
    ANDQuery_new(VArray *children)
    {
        ANDQuery *self = (ANDQuery*)VTable_Make_Obj(ANDQUERY);
        return ANDQuery_init(self, children);
    }

The limitation of new() is that it does not support subclassing, since the
VTable on which we invoke VTable_Make_Obj() is fixed -- in this case,
ANDQUERY.  

However, when invoking a constructor from Host space, we don't have to go
through new().  So long as we can find the right VTable, can call
VTable_Make_Obj() and init() ourselves.  Thus the constructor Perl bindings --
which typically create functions named "new" in Perl space -- do not actually
invoke the class's new() function.  Instead, they are wrappers around init().

A second subtlety: VTable_Make_Obj has to use calloc rather than malloc, in
order to ensure orderly cleanup in the event that an exception occurs midway
through object construction.

Some classes, e.g. Indexer, IndexReader, will have very elaborate constructors
which can fail for any number of reasons.  When this happens, the object must
be DECREF'd before the exception is thrown, or we leak memory -- which will
result in the destructor being invoked.

Destructors must clean up individual member variables.  However, if the
constructor fails before a particular member is assigned, the destructor may
mistake a random number at that member's address for a valid object, resulting
in a memory error.

To guard against this we use calloc to allocate object structs, and ensure
that destructors always NULL-check any member they are about to clean up, even
if the object could not possibly have functioned properly without that member
variable.

The DECREF macro is implemented with this in mind.

    /* If an object is not NULL, decrement its refcount, calling destroy on it
     * if the refcount drops to 0.
     */
    #define LUCY_DECREF(_self) lucy_Obj_decref((lucy_Obj*)_self)

    static CHY_INLINE chy_u32_t 
    lucy_Obj_decref(lucy_Obj *self)
    {
        if (self != NULL) { return Lucy_Obj_Dec_RefCount(self); }
        else { return 0; }
    }

DECREF should generally be favored over direct calls to Dec_RefCount() --
which is why it is has a nice, short name.

Marvin Humphrey

Reply via email to