An alternative to .init

downs default_357-line at yahoo.de
Tue Sep 9 08:56:01 PDT 2008


Andrei Alexandrescu wrote:
> downs wrote:
>> Andrei Alexandrescu wrote:
>>> What's the deal with destroyed but not deleted? Consider:
>>>
>>> int[] a; if (condition) { Array!(int) b; a = b.all; } writeln(a);
>>>
>>> This is a misuse of the array in that a range crawling on its back
>>> has survived the array itself. What should happen now? Looking at
>>> other languages:
>>>
>>> 1) All Java objects are unowned, meaning the issue does not appear
>>> in the first place, which is an advantage. The disadvantage is that
>>> scarce resources must be carefully managed by hand in client code.
>>>
>>> 2) C++ makes the behavior undefined because it destroys data AND
>>> recycles memory as soon as the array goes out of scope. Mayhem
>>> ensues.
>>>
>>> We'd like:
>>>
>>> 1.5) Allow object ownership but also make the behavior of incorrect
>>> code well-defined so it can be reasoned about, reproduced, and
>>> debugged.
>>>
>>> That's why I think an Array going out of scope should invoke
>>> destructors for its data, and then obliterate contents with
>>> ElementType.init. That way, an Array!(File) will properly close all
>>> files AND put them in a "closed" state. At the same time, the
>>> memory associated with the array will NOT be deallocated, so a
>>> range surviving the array will never crash unrelated code, but
>>> instead will see closed files all over.
>>>
>>
>> I don't think this is a good thing, for reasons similar to the
>> Error/Exception flaw - specifically, code that works in debug mode
>> might end up failing in release mode.
>>
>> To explain what I mean by Error/Exception flaw, consider the case of
>> an array out of bounds error, wrapped carelessly in a try .. catch
>> (Exception) block.
>>
>> This would work fine in debug mode, and presumably retry the
>> operation until it succeeded.
>>
>> In release mode, however, the above would crash.
>>
>> This is clearly undesirable, and arises directly from the fact that
>> Error is derived from Exception, not the other way around or
>> completely separate (as it clearly should be).
>>
>> After all, an Error ![is] an Exception, since Exceptions are clearly
>> defined as recoverable errors, and the set of unrecoverable errors is
>> obviously not a subset of the recoverable ones.
>>
>> This leads to my actual point: I suggest an extension of .init: the
>> .fail state, indicating data that should not be accessed.
>>
>> Any standard library function that encounters data that is
>> intentionally in the .fail state should throw an Error.
>>
>> For instance, the .fail state for strings could be a deliberately
>> invalid UTF8 sequence.
>>
>> When this state could reasonably come up in normal operations, it is
>> recommended to use values that will readily be visible in a debugger,
>> such as the classical 0xDEADBEEF.
>>
>> This is imnsho superior to using .init to fill this memory, which
>> doesn't tell the debugging programmer much about what exactly
>> happened, and furthermore, might cause the program to treat "invalid"
>> memory the same as "fresh" memory, if only by accident.
> 
> I hear you. I brought up the same exact design briefly with Bartosz last
> week. We called it T.invalid. He argued in favor of it. I thought it
> brings more complication than it's worth and was willing to go with
> T.init for simplicity's sake. Why deal with two empty states instead of
> one.
> 
> One nagging question is, what is T.fail for integral types? For pointers
> fine, one could be found. For chars, fine too. But for integrals I'm not
> sure that e.g. T.min or T.max is a credible fail value.
> 
> 
> Andrei

For numbers, it should probably be "the same as .init". Not every error condition can be detected, sadly.

It would also be nice if a .fail value could be provided as an extension to typedef somehow .. user defined types will probably have their own possible failure indicators.


More information about the Digitalmars-d-announce mailing list