Macro functions or Try/Catch for error handling

Got an idea for enhancing PureBasic? New command(s) you'd like to see?
Mistrel
Addict
Addict
Posts: 3415
Joined: Sat Jun 30, 2007 8:04 pm

Macro functions or Try/Catch for error handling

Post by Mistrel »

PureBasic does not have an equivalent construct for the Try/Catch method of error handling. It does not support object-oriented paradigms and more closely resembles the procedural "C programming language".

C handles errors through return values or errorno. Macro functions are the ideal solution for handling these types of error values because they can also return the original value of a function in case of special circumstances or if no real error has occurred.

Here is a simple procedural error handler in PureBasic, for example:

Code: Select all

Macro ErrorHandler(Result)
  CompilerIf Defined(LastError,#PB_Variable)
  CompilerElse
    Protected LastError
  CompilerEndIf
  If Result < 0
    ReportError(LastError)
    ProcedureReturn LastError ;/ Exit procedure/thread
  EndIf
EndMacro
PureBasic's macros are perfectly fine if the function being called has no return value:

Code: Select all

ErrorHandler(SomeFunction())
But if it does then this or some similar form of it is required:

Code: Select all

Result = SomeFunction()
ErrorHandler(Result)
But it makes large and complex code more difficult to read with the repetition of these essential functions.

Certainly, here is an option to keep things on "one line":

Code: Select all

Result = SomeFunction(): ErrorHandler(Result)
But this can be error prone, impossible to read, and difficult to maintain as now every return value can have multiple representations and also duplicate variable names. Considering lines cannot be split in PureBasic, it's possible for the "ErrorHandler" portion to be cut off past the edge of the screen and require horizontal scrolling to adjust.

There will also now be to unique cases to be handled where the function returns a value and if it does not. The calling convention will be completely different and also error-prone and confusing:

Code: Select all

ErrorHandler(SomeFunction1())
Result = SomeFunction2(): ErrorHandler(Result)
Additionally, if the second method is used and the function goes off the screen then there is no way to identify that the error is even being handled without scrolling horizontally.

Macros for error handling are essential here but can be impossible to read and are difficult to maintain. Because PureBasic follows the procedural paradigm I hope that the PureBasic team will strongly consider supporting macro functions. It's the only way to elegantly handle these errors without adding additional code to handle each return value and unless there is a future for Try/Catch exception handling with adequate, architecture-independent supporting functions (register/call stack dump with debugger support), it is the obvious solution.
Fred
Administrator
Administrator
Posts: 18162
Joined: Fri May 17, 2002 4:39 pm
Location: France
Contact:

Re: Macro functions or Try/Catch for error handling

Post by Fred »

You could also simply do a macro which takes the result variable as parameter:

Code: Select all

ErrorHandlerResult(Result, SomeFunction())
It is not that difficult to read IMHO. For the sake of readability, i would choose a smaller label like CheckResult() or something similar. You can also put the linenumber/filename/procedurename in the macro so you get precise inforamtion about were the error happened.
Mistrel
Addict
Addict
Posts: 3415
Joined: Sat Jun 30, 2007 8:04 pm

Re: Macro functions or Try/Catch for error handling

Post by Mistrel »

It still requires multiple lines to handle a single error result and it cannot be used in an expression.

I use a hybrid approach where the macro can in certain cases be used in an expression to limit the number of lines for each error check. It works but it's not a pleasant solution.

Here is an example of this I have in one of my projects:

Code: Select all

Procedure _CopyResultInteger(Input, *Output.Integer)
  If *Output
    *Output\i=Input
  EndIf
  ProcedureReturn Input
EndProcedure

Macro Exit()
  ErrorString.s=ErrorLib_GetLastError($434241,@LastError.LastError)
  SetStatus("*** Error: "+ErrorString.s)
  SetStatus("*** "+GetFilePart(LastError\SourceFile.s)+" Line "+Str(LastError\ErrorLine))
  ProcedureReturn 1 ;/ Exit thread
EndMacro

Macro Assert(Expression, Result=0)
  _CopyResultInteger(Expression, Result)
  If Result=0
    CompilerIf Defined(Assert_Result_,#PB_Variable)
      Assert_Result_=0
    CompilerElse
      Define Assert_Result_=0
    CompilerEndIf
    _CopyResultInteger(Expression, @Assert_Result_)
  EndIf
  If Result<0 Or Assert_Result_<0
    Exit()
  EndIf
EndMacro

Macro ErrorHandler(Value)
  _CopyResultInteger(Value, @ErrorHandler_Result)
  If ErrorHandler_Result<0
    ProcedureReturn ErrorHandler_Result
  EndIf
EndMacro
You can also put the linenumber/filename/procedurename in the macro so you get precise inforamtion about were the error happened.
Yes, of course. I already do this.

Code: Select all

Macro (libname)Macro_SetError(ErrorID)
  ErrorLib_SetLastError(Private_(libname)_GetErrorLibID(),ErrorID,#PB_Compiler_File,#PB_Compiler_Line)
EndMacro
Post Reply