lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]


Am 20.06.2019 um 15:33 schrieb nobody:
> On 20/06/2019 13.19, David Heiko Kolf wrote:
>> In C# the last error is reported and I actually think that is the
>> right decision.
>>
>> […]
>>
>> If the first error is reported I will never even know that there was a
>> second error. And once I fixed the first error, I probably won't see
>> that there is problem with my __close function.
> 
> The situation that caused the last error is potentially caused by an
> arbitrarily large number of (mis-/un-)handled errors that happened
> before that, and the data that your __close has to work with is
> arbitrarily insane.  If you want to have a chance to work your way back
> to the earlier errors, you'll have to write paranoid (long, unreadable)
> code.
> 
> Only the first error in the chain is certainly caused by an actual
> problem.  All later errors are potentially just interface/invariant
> violations caused by the first error, and it's not your(/__close's) job
> to navigate the mess caused by interface-/invariant-violating code.

Well, the way I learned it was that a cleanup routine just has one
well-defined job and that is to release all resources still used by the
object. So whatever happens in code outside of this object should have
no influence.

Quite the contrary, the cleanup function is supposed to get me out of
the error situation. If the situation is so messed up that I cannot
clean up resources anymore, can I still continue executing my program?
In fact, C++ seems to take this approach: if a destructor throws an
exception while another exception is already processed, it terminates
the entire application.

> How would you "fix" this "error" in your example?  I don't see anything
> wrong there.
> 
>> __close = function( )  z.closed = true  end -- error, z might be nil
> 
> If anything, I might add a loud check that z is a table (or
> __index-able), but Lua's error message ("attempt to index a nil value")
> already says that (and even includes the type of the value, and
> potentially its name!) – so a manual check would just add more code to
> produce a less informative error message… so actually there is nothing
> to fix?  (What am I missing? O.o)

So in my case, which was probably too artificial, the cleanup code
should first check that the objects it tries to "free" are actually
existing in the first place. Maybe this example would be more realistic:

  function mt:__close ()
    self.file_x:close()
    self.file_y:close()
  end

  function obj:open_x (name)
    self.file_x = io.open(name, "w")
  end

  function obj:open_y (name)
    self.file_y = io.open(name, "w")
  end

The object using this close function might have had file_y already
opened but there was an error before opening file_x. Now thanks to the
error raised by file_x being nil file_y will actually not be closed --
and you won't notice it and assume it is closed.

So in this case I have to test whether each of those variables had
already been assigned a value:

  function mt:__close ()
    if self.file_x then self.file_x:close() end
    if self.file_y then self.file_y:close() end
  end

Sure, you are right that someone could assign a random value that is not
a file to one of those variables. But that would be a further usage
error. First assigning one file and then assigning the next is not in
itself an error.

Best regards

David