Around 1 o'clock on Dec 2, MenTaLguY wrote:
The only general solution I've found so far is to create synthetic "anchor" objects which are attached to a root and contain a refcount and a pointer to the managed memory. Usually the interfaces provided by the closures or whatever are sufficient to manage a refcount.
That's what I tried to suggest, but your wording is quite a bit clearer...
Hmm. One of the reasons I adopted a garbage collector was to facilitate writing in a functional style which would unfortunately be susceptible to this sort of problem.
Except for loops (and recursion), the overhead is all constant. For example, Nickle does lots of stuff like:
av = Reduce (NewInteger (sign, NaturalRsl (IntegerMag(IntegerRep.promote (av,0)), b)));
which does leave four extra values referenced temporarily, but those are dereferenced on exit from this function. When necessary, you can also just explicitly bracket sequences of code with ENTER()/EXIT() macros; obviously a pain, but at least you can manage recursion and iteration without running out of memory.
In this specific example, though, I don't believe the macros are necessary. I would expect this to work:
void bar() { MemStackFrame frame; operate(a(), b(), c()); }
Yes, that's clearly the way to manage this in C++.
At least it's more portable. As you probably know, at least on paper memset() isn't a portable way to initialize pointers.
When I find a modern machine for which memset doesn't work, I'll alert the media, but I agree it's nicer to just not assume things like this. The real reason for the change was to avoid ripping across large chunks of ram for no good reason.
In Inkscape I actually fill memory allocated for some classes with garbage before their constructor is called. It's helped shake out some subtle bugs that were previously flying below the radar because zero was close to a sensible value (and pages claimed from the OS are often initally zero-filled).
This was my basic reason -- it provided a clean invarient (all allocated memory is zero) which prevents surprises when you suddenly start re-using previously allocated memory instead of new memory.
Of course, I'd rather have the Nickle behaviour where using uninitialized values cause an exception to be raised, but C isn't that helpful.
Er, forgive my curmudgeonly ranting. ^^; I still wonder whether there might be another good conservative solution to the initialization/allocation ordering problem...
Yes, that would be nice. The real problem was across the transition where the code implicitly assumed that data were initialized to zero (and, of course, where NULL was represented by zero bits). New code is written in a more stylized fashion:
foo = ALLOCATE (&type, sizeof (type)); foo->value1 = 0; foo->pointer1 = NULL;
... code using foo
Which, as you say, provides some level of self-documentation.
-keith