Question
· Aug 7, 2017

%Status vs Other Return Values in Caché ObjectScript Methods

Hi, folks!

There is an interesting discussion in the neighboring topic which raises a question for me: is there any reason to have %Status as a return value for COS method?

How are you coding? Do you prefer %Status or other types of return values and why?

Discussion (16)2
Log in or sign up to continue

%Status or %Library.Status provides a number of useful capabilities.

Following is documentation from the %Library.Status Class.

The %Status data type class is used to represent an error status code.

Many of the methods provided by the Caché Class Library return error status information using the %Status data type. The include file, %occStatus.INC, contains several macro definitions that are useful in testing the value of an error code in %Status format.

These macros include:

  • $$$ISOK(status:%Status) returns true (1) if the status code status does not represent an error condition.
  • $$$ISERR(status:%Status) returns true (1) if the status code status represents an error condition.

You can get a more detailed description of an error by using the system-provided DecomposeStatus^%apiOBJ(status:%Status,&err,flag) routine.

This routine takes a status code and returns an array of error strings.

For example: 

Set status=obj.%Save() If $$$ISERR(status) Do DecomposeStatus^%apiOBJ(status,.err,"-d") For i=1:1:err Write err(i),! 

A method returning a %Status code representing an error will throw an exception in a client Visual Basic (ActiveX) or Java application.

When you receive a <ZSOAP> or <ZGTW> error, throw it away and take whatever comes into %objlasterror as your final %Status code. For <ZSOAP>, %objlasterror will have the real error description such as the timeout, XML parsing errors, authentication errors, etc. For <ZGTW> errors, %objlasterror will have the complete Java stack trace of the error.

Kind regards,

AS

Since booleans can be only true or false, you usually don't need to know about anything else, so let the caller handle what it should do.

 

ClassMethod AssertClassExists(ClassName As %String) As %Status
{
   if '..ClassExists(ClassName) return $$$ERRROR($$$ClassDoesNotExist, ClassName)
   return $$$OK
}

ClassMethod ClassExists(ClassName as %String) as %Boolean {

return ##class(%Dictionary.CompiledClass).%ExistsId(ClassName)

}

I believe this recommendation you linked on our documentation is outdated and wrong. One must use %objlasterror on several instances. Examples:

Java Gateway: http://docs.intersystems.com/latest/csp/docbook/DocBook.UI.Page.cls?KEY=...

.NET Gatewayhttp://docs.intersystems.com/latest/csp/docbook/DocBook.UI.Page.cls?KEY=...

SOAP Error Handlinghttp://docs.intersystems.com/latest/csp/docbook/DocBook.UI.Page.cls?KEY=...

Caché ActiveX Gatewayhttp://docs.intersystems.com/latest/csp/docbook/DocBook.UI.Page.cls?KEY=...

%New() constructorhttp://docs.intersystems.com/latest/csp/docbook/DocBook.UI.Page.cls?KEY=...

Using %Dictionary Classeshttp://docs.intersystems.com/latest/csp/docbook/DocBook.UI.Page.cls?KEY=...

There are many instances where %objlasterror is the ONLY place where you can find out what REALLY happened. Not using this information on production is, IMHO, unwise.

Kind regards,

Amir Samary

Yes, like... %DynamicObject and %DynamicArray.

I still don't quite get why to use %objlasterror instead of returning the error.

Method %ToJSON(outstrm As %Stream.Object) As %String
{
if $D(outstrm) {
if $isobject(outstrm) {
if '(outstrm.%IsA("%Stream.Object")) {
throw ##class(%Exception.General).%New("%ToJSON - argument passed is not an instance of %Stream.Object")
}
try {
set ans = $zu(210,27,outstrm)
catch {
do  $$$APPERROR1($$$LASTERROR)  <- This appends the error to %objlasterror and that's it.
}

ISC advises against using %objlasterror in production code.

citation from http://docs.intersystems.com/latest/csp/docbook/DocBook.UI.Page.cls?KEY=...

Using the %objlasterror error status variable
The Test class includes references to %objlasterror, which should be used as a debug resource only (for example, in development code that does not yet report errors properly), so that the underlying problem can be diagnosed and the offending code's error reporting can be corrected. It is appropriate for such code to kill %objlasterror whenever it uses an error status that is an expected condition and not a reportable error.

My personal preference is %Status: 1 = OK,
0 = something went wrong + standard or handmade Error Code
+ ability of $system.Status.Append(Status) to get a chain of error messages
which gives you the chance to drill down to the source. Which is especially important when
you get it from some embedded action.

The opposite to me is
<ZSOAP>  that leave you alone with no hint what went wrong
or the famous <ZSG> homed in %Save() Method

to pass return value I prefer objects or variables passed byRef od Output
 
The other opposite is SQLCODE (would be better named SQLerror)  0 is OK. Anything else needs action
But that's tradition in SQL world since more than half a century

Many guides to "good programming" (in any language) would advise that the return from a function/method should be used for "real" data only, and any "exception" situations should be flagged as an error. While I'm not convinced this is always the best way, I can see the advantages. Code with repeated tests of returned status values can be messy and hard to read, and if the only thing it can do when the status is a fail is to quit out again with a status of "failed", then there is not a lot to be gained.

Mike

Errors can be encountered during the normal execution of any code and the cause of those errors is not always known or predictable. Caché has three primary error reporting mechanisms two are passive, meaning the user code is responsible for checking some error indicator and one is active, meaning the error triggers a change in the code path. The passive error reporting, %Status and SQLCODE, both require the user code to check for error conditions after executing some code. The code author must know before hand that these error indicators can be changed by the code that is being executed.

The third mechanism is exceptions. ($ZE with ZTRAP, etc. exist of course). With exceptions, the user code only needs to know that an exception could be thrown and to properly structure the code to deal with that possibility. If Caché user code is not already properly structured then $ZTRAP errors can cause problems so structuring code to deal with exceptions should not be considered a hardship.

The advantage of exceptions over the two passive types is that all three types of errors - %Status, SQLCODE, and exception - can be integrated into a single error handler. The passive checks can be used to construct exceptions using exception class constructors - CreateFromStatus and CreateFromSQLCODE - which can then be thrown. The error handling code lives in the CATCH block and can use the exception instance without further need to parser of otherwise process the exception in order to identify the type of exception, the exact error code and name, error context values, and so on. There is no need to litter your code with a lot of error handling code, detecting different types of errors, formatting, parsing, and so on. Just do it all in one place.

There are also exception methods to cast an exception as %Status or SQLCODE error values should there be a contractual obligation to report errors using one of the passive methods.

Another advantage to using exceptions is that there is virtually no runtime overhead. Also, unless you must return a %Status value (it happens), your code is free to return a useful value from a function, improving code readability and efficiency.