Tuesday, June 12, 2012

Universal References

References have always been a problem in Qore.  The main reason was that there was no unified lvalue handling, and also having local variables thread-local meant that there would either have to be no references to thread-local variables, or nonintuitive restrictions (for example by disallowing a reference to an lvalue expression anchored by a thread-local variable to be assigned to a variable with greater scope).  Up unti now, Qore took the wimpy way out and only allowed references to be passed to function and method calls.

Today I just committed support for universal references, addressing (another) long-standing deficiency in the language.  I was motivated by writing some Qore code that needed to do some reformatting to a complex data structure (that was parsed from XML data - BTW did I ever mention that I prefer YAML?).  Basically I needed to add the number of order items to the last order in a list contained which was an attribute of the last record of a list.

The code looked as follows:

# finalize last order - set numberofitems
recs[recs.size() - 1].eventdata.ufulfildespatchconfirm.order[ol.size() - 1].numberofitems = oh.items.size();
recs[recs.size() - 1].eventdata.ufulfildespatchconfirm.order += get_order(e, h);

I was frustrated by Qore's lack of references to simplify the above code.  Then I realized that I had the unified lvalue infrastructure (implemented in Qore 0.8.4) and had also solved the thread-local multi-threaded access problem when I implemented closures; when a local variable is bound into a closure, the local variable is not only thread-local anymore; it has a mutual-exclusion lock on it and its lifetime is reference counted - it lasts only as long as it's bound in a runtime closure or until its local scope expires.  Because a runtime closure could be used in multiple threads, all local variables bound in the closure when it's created at runtime are protected by the mutual-exclusion lock to ensure consistency and atomicity.

Therefore I simply applied this same approach to local variables that are referenced and removed all restrictions on the use of the lvalue reference operator ('\') in Qore.

The resulting code is cleaner and more consistent, and I actually found a segfault-inducing memory error with the old cludgy implementation at the same time.

The above code now reads:
# finalize last order - set numberofitems
reference orders = \recs[recs.size() - 1].eventdata.ufulfildespatchconfirm.order;
reference lord = \orders[orders.size() - 1];
lord.numerofitems = lord.items.size();
orders += get_order(e, h);

That's two lines longer than the first one without references, but much easier to read, understand, and maintain.

Qore 0.8.5 should be out before too long; I mainly want to get it out so I can update all the binary modules; I discovered a bug in a new library API that is only used by modules built with qpp, so I want to get Qore 0.8.5 out relatively quickly and then update all the binary modules in common use as well.

This feature is already in svn and looks to be stable; the only other feature I plan on adding to Qore for 0.8.5 is support for abstract class methods - this way java-style interfaces can be implemented by defining a class with abstract methods; I've been wanting this for a while, and it doesn't look too hard to do, so I hope to get that done in the next few days.