Exception Handling

BlitzMax Forums/BlitzMax Programming/Exception Handling

peltazoid(Posted 2008) [#1]
Can anyone please tell me why the following does not throw an exception, it just keeps running well beyond the end of the file


'exception test

SuperStrict


Local infile : TStream = ReadFile("<replace with any short text file>") 
Local count : Long
Local tvar : String

If Not infile
	Print "not loaded"
	End
End If



Repeat
	
	Try
		tvar = ReadString(infile , 10)
		Print tvar
		count :+ 1
	
	Catch a : Object
			
		Print a.ToString()
		End
		
	EndTry
	
	Print count
	
Forever


As I understand it, As soon as any of the stream reading command get to the end of the file they throw a TStreamReadException which my try block should pickup, but it does not. What am I doing wrong. I plan to use the block to detect errors in case of a short file.

Cheers.


Retimer(Posted 2008) [#2]
Readstring doesn't have an error handle like that. It only checks if the length is greater than 0, if not, runtime error. Nothing is thrown.

in streams.bmx (streams.mod)
	Method ReadString$( length )
		Assert length>=0 Else "Illegal String length"
		Local buf:Byte[length]
		Readbytes buf,length
		Return String.FromBytes( buf,length ) 
	End Method


You can replace and recompile with:

	Method ReadString$( length )
		If length < 1 Or pos()+length > size() Then Throw "Stream read out of position"
		Local buf:Byte[length]
		Readbytes buf,length
		Return String.FromBytes( buf,length ) 
	End Method


highlighting the change of:

If length < 1 Or pos()+length > size() Then Throw "Stream read out of position"


Which will give you what you are looking for, but only with readstring. You could always add the same line to each 'read' function, but I would suggest doing that with ?debug around it.


peltazoid(Posted 2008) [#3]
Yes readstring does not have a throw in it, but readbytes which is called from readstring does have a throw.


from stream.bmx (module BRL.Stream)
Method ReadBytes( buf:Byte Ptr,count )
	Local n=count
	While count>0 And Not Eof()
		Local n=Read( buf,count )
	        If Not n Throw New TStreamReadException
		count:-n
		buf:+n
	Wend
	Return n
End Method


so your change should not be required as ReadBytes is called from readstring. Plus the documentation also states

Function ReadString$( stream:TStream,length ) 
Returns A String of length length  
Description Read a String from a stream 
Information A TStreamReadException is thrown if not all bytes could be read.  


Stepping through the code it seems like it ignores the asserts when the end of file occurs mid string an just keeps reading.

Is this a bug or something that could be addressed for the next release?

Cheers.


peltazoid(Posted 2008) [#4]
I've been stepping though all the function calls and I have discovered the following.

the readbytes (and writebytes) have a duplicate identifier in different scope.

Method ReadBytes( buf:Byte Ptr,count )
	Local n=count
	While count>0 And Not Eof()
		Local n=Read( buf,count )
		If Not n Throw New TStreamReadException
		count:-n
		buf:+n
	Wend
	Return n
End Method


In The above code taken from stream.bmx

there are 2 n variables. one is in scope the whole method the other just in the while loop, the n in the while gets updated, with the characters actually read from the file, this decrements count with those characters read and updates the pointer to the buf

there is only one check to see if the returned number of read characters is not zero, if it is zero then a error is thrown, other wise it will happily read from a empty stream.

So I add the following line

to the read function in TCStream

Method Read( buf:Byte Ptr,count )
		Assert _cstream Else "Attempt to read from closed stream"
		Assert _mode & MODE_READ Else "Attempt to read from write-only stream"

		Assert _pos <> _size Else "End of File Reached" ' new line

		count=fread_( buf,1,count,_cstream )	
		_pos:+count
		Return count
	End Method


the new line being
 
     Assert _pos <> _size Else "End of File Reached" ' new line


but this still does not throw an error. I am using assert correctly?

i.e Assert <comparison> Else <Message>

Help would be appricated.

Cheers.


Retimer(Posted 2008) [#5]
Oops, sorry. I didn't look deeper with readstring.

Try :

Assert pos() + count < size() Else "End of File Reached" ' new line


Assert <Situation that is safe> else "error"


peltazoid(Posted 2008) [#6]
So After some meatballs and a can of coke, my brain is refreshed and I spotted the error.

My modifying the Read Method, was pointless as it is called after an Eof Call.

So I have placed a throw before the while loop in ReadBytes and that now once the end of file is reached another read will throw a TStreamReadException. The read of the stream that reaches the EOF will return correctly.

so here is the working function

Method ReadBytes( buf:Byte Ptr,count )
	Local n = count

	'MARK: new line 		
	If Eof() Then Throw New TStreamReadException

	        While count>0 And Not Eof()
		        Local n = Read( buf , count )
			If Not n Throw New TStreamReadException
			count:-n
			buf:+n
		Wend
	Return n
End Method


most of solving this was getting my head around asserts, and rushing to get it done.

Could this be added to the official mods? I don't like having to mod BRLs functions.

Cheers.


ziggy(Posted 2008) [#7]
Isn't this a bit inconsistent? Assert is suposed to generate a 'RuntimeError' if the condition is false, surprisingly the error message generated by the BlitzMax compiler is 'unhandled exception', even when the assert is happening inside a exception-hanlded area. This is very confusing. Does anybody understand why assert doesn't really THROW a real exception when the condition is false, instead of fakeing a false unhandled exception? I'm not sure if I am missing something or there is a strange behaviour on the exceptions area.


peltazoid(Posted 2008) [#8]
Yeah looking deeper than readstring is what caused me some headache. Esp with the two b variables in different scopes in the same function, couple that with me trying to suss it out while doing other things and not spending the time to look it over!

Also thanks for the pointers with Assert.


ziggy(Posted 2008) [#9]
I got a very explanatory code example from a very very very advanced user of BlitzMax. regarding the exception issues here discussed and that's the always working' exception handling code:

Strict

Try
	'Test code:
	Assert False, "This is an error!"
	'end of test code

Catch ex:Object
	Notify TTypeId.ForObject(ex).Name() + ":" + ex.ToString()
End Try

Catching the exception as an object fires the catch statement for EVERY possible exception, then using a simple reflection operation we can get the exception information.
It seems it is possible to have several catch statements for a single Try block, something like:
Try
...
Catch ex:TStreamReadException
...
Catch ex:TStreamWriteException
...
Catch ....
End Try

It seems very obvious now, doesn't it?


peltazoid(Posted 2008) [#10]
Ziggy,

that reflection example seems quite useful, to find what object causes the exception, I have not looked at reflection at all and to be honest I have little Idea about what it can be used for.

Cheers


grable(Posted 2008) [#11]
Its not really reflection (as in BRL.Reflection), just normal casting...
Essentially the same as:
Try
	...
Catch e:Object
	If TStreamReadException(e) Then
		...
	ElseIf TStreamWriteException(e) Then
		...
	Else
		Throw e
	EndIf
EndTry


Also note that you have to follow the class hierarchy in reverse when using multiple catch statements.


ziggy(Posted 2008) [#12]
@grable: The Throw method accept ANY object, so the only safe way to get any exception is using the reflection based example.
You can even do:
Throw New TList


So the only way to handle ANY possible exception is:
Catch ex:Object
	Local ExceptionType:String = TTypeId.ForObject(ex).Name()
	Local ExceptionMessage:String = ex.ToString()
	

Your example will leave any exception different from TStreamReadException and TStreamWriteException unhandled.


grable(Posted 2008) [#13]

Your example will leave any exception different from TStreamReadException and TStreamWriteException unhandled.


Exactly, just like yours ;)

I was trying to show that multiple catch statements was like the code i supplied, hence the word "Essentially".

But I see now that you really did use BRL.Reflection, sorry about that. no harm intended.