New QOF type, QOF_TYPE_COLLECT and recursive entity copying

Derek Atkins warlord at MIT.EDU
Tue May 10 08:18:20 EDT 2005

MOved to gnucash-devel....

Quoting Neil Williams <linux at>:

> On Monday 09 May 2005 9:46 pm, Derek Atkins wrote:
> > Neil,
> >
> > A couple of comments on this patch.  I'll note that I haven't looked very
> > closely at it, but in my cursory examination I see a number of issues:
> I committed this too early - there were bits that were unfinished and bits 
> that should have been cleaned up. My bad.
> However, I simply don't understand your objection to GncEntry being a 
> QOF_TYPE_COLLECT from GncInvoice - see later.
> > 1) You introduce a number of memory leaks.  For example, when you just
> > reset the address pointer to a passed-in object you potentially leak
> > memory.
> Is this the correct solution?
> void
> qofCustomerSetAddr (GncCustomer *cust, QofEntity *addr_ent)
> {
> 	GncAddress *addr;
> 	if(!cust || !addr_ent) { return; }
> 	addr = (GncAddress*)addr_ent;
> 	if(addr == cust->addr) { return; }
> 	if(cust->addr != NULL) { gncAddressDestroy(cust->addr); }
> 	cust->addr = addr;
> }

Yes, this would be more correct. :)

> > You have similar issues in  places like qofOwnerGetType() where I
> > really don't think you should be returning g_strdup'd strings when the
> data
> > is constant.
> Fixed. Thanks.
> > 2) You add a number of APIs that really don't belong.  There is absolutely
> > no reason to export the invoices list of entries as a QofCollection (or a
> > KVP frame).
> The KVP frame was redundant, it wasn't meant to be committed. The 
> QofCollection, however, I need.
> GList* gncInvoiceGetEntries(invoice) already existed - it was already public.
> I didn't change that. QOF cannot deal with the GList directly so I use a 
> QofCollection instead. Then all I've done is a set routine to complement the
> get routine that already existed.

Why can't QOF be changed to deal with the GList directly?  Why does the "convert
the GList into a collection" code have to be duplicated in every place where it
has a list of objects, rather than putting that code in a central place in

> QOF_TYPE_COLLECT (QofCollection) is the new QOF type, it's not a regular 
> collection, it's a secondary collection that is used to pass references to 
> many entities of the same type in one parameter. It doesn't contain all 
> entities from the book, it contains all entities associated with the 
> parameter. In this case, it's every (QofEntity*)GncEntry in the GList* of the
> specific GncInvoice*.  The collection is written out as a series of tags and
> passed back to the invoice as one. The collection is also compared as one - 
> using a deep compare routine. During a merge, the collection is updated as a
> single block.

I don't agree that it should work this way.  When you merge accounts do you
merge the whole list of transactions as a single block?

> QOF_TYPE_COLLECT is being used, in this example, as a "typed list". It's a 
> block of QofEntity pointers with a guarantee that all entities are of the 
> same e_type. In the backend, this is converted to a block of pointers to 
> QofEntityReference that contains the GUID and the type of each. This is then
> written to QSF. I cannot simply write out the GUID using the list, QOF needs
> the type, hence a collection.
> > I'd be less concerned if this were a private API -- but it 
> > certainly does not belong in the .h file and exported from the module.
> ? This is the only problem that remains.

I don't know.  (sorry, I'm on the road so I only have limited time to actually
ponder these issues).

> ?? How else am I to obtain and set the entries for the invoice? GncEntry is 
> exported in the current v2 XML so it isn't transparent like Owner. QOF has to
> be able to create the entries for the invoice and this means accessing the 
> list. Doesn't it?

In the v2 gnc-xml the GncEntry is a top-level object, just like a Transaction. 
It's exported separately (just like transactions) and loaded and linked via
GUID references.  It's not exported as a subtree.

Note that it IS possible for a single GncEntry to be referenced by TWO invoices!
 In particular it can be referenced by a Vendor Invoice and then again by a
Customer Invoice.

> What have I missed? QOF is working from the invoice down - it locates the 
> Customer etc. via the GncOwner Collection, it locates the billterms using a 
> one-to-one reference. The invoice owns the entries (yes?), so whether the 
> invoice has been posted or not, QOF should be able to access the entries from
> the invoice object. How else can I create a new invoice - with entries - in 
> from QOF?

I don't know offhand.  I'm just pointing out that there are issues with the
current approach.  That doesn't mean I know how to solve those issues, I'm

> The business objects are written to v2 XML via Scheme and it's complete 
> mumbo-jumbo to me (even the generated C code). I can't tell how the Invoice 
> and the Entry are currently processed from the v2 XML and there's absolutely
> *no* documentation.
> :-(

HUH???  There's no scheme at all involved in writing the business objects into
v2 gnc-xml.  All the C code was hand written (although it was mostly a
copy-and-paste job by yours truly)..  Look in src/business/business-core/file
for the source code.  No, there's not much documentation in how the objects are
stored.  Hint: each first-class object is stored by itself.

> I don't understand why I cannot use a secondary collection typed list for 
> pointers to the entries within the invoice. OK, I know it's a hash table 
> internally, but that is why it is so useful - it's so easy to lookup and 
> ensure you aren't referencing the same entity twice.

See above about how you handle the fact that the same entry could be in multiple
invoices.  Also, I don't think that code belongs in the invoice module, and it
certainly doesn't belong in the public api.

> It's quite separate from qof_book_get_collection - at no point is the entity
> itself changed, it remains within it's primary collection. Adding an entity 
> to a secondary collection does not change the primary. It's about references
> and recursion.

Fair enough..  Except that in the case of a GncOwner how do you make sure that
you have a collection with one and only one entity?  (I've already mentioned
the multiple-reference thing ;)

> > 3) I don't see why you need a "qof_temp" storage in the GncOwner object. 
> > It seems superfluous.
> I changed qofOwnerSetEntity and didn't twig that qofOwnerSetType and 
> qofOwnerSetOwner were left hanging without much to do. Removed.
> > 4) You change a lot of style structure which makes it harder to read your
> > patches.  :)
> Now there's two things going on there, first the majority of all those
> changes 
> are Doxygen fixes. Anywhere that I've changed:
> -/** @name Create/Destroy Functions */
> -/** @{ */
> +/** @name Create/Destroy Functions 
> + @{ */
> Those were generating horrible artefacts in the Doxygen output - prefixing 
> function declarations in the named block with * - confusion was inevitable.
> const char* myfunc(); // OK
> *const char* myfunc(); // NOT OK
> Only in GncInvoice did I inadvertently change the spacing in the declarations
> - that was only 5 lines.
> :-(

There were a number of places where you changed:

   foo *      fooobj;


   foo       *fooobj;

Is doxygen stupid enough not to understand the former?  I also noticed a place
where you changed:

   foo *fooobj = g_new0(foo, 1);


   foo *fooobj;
   foo = g_new0(foo,1);

There's no reason to do that...   IMHO...  It's just a style thing.  Unless, of
course, doxygen is so stupid that it can't recognize that..  :(


       Derek Atkins, SB '93 MIT EE, SM '95 MIT Media Laboratory
       Member, MIT Student Information Processing Board  (SIPB)
       URL:    PP-ASEL-IA     N1NWH
       warlord at MIT.EDU                        PGP key available

More information about the gnucash-devel mailing list