Passing Errors from Functions to Containing Sub

whdyck

Registered User.
Local time
Today, 09:00
Joined
Aug 8, 2011
Messages
169
I'm using MS Access 2003.

This is probably a bonehead question, but I've been googling for a while with little success.

I have some validation code that I'd like to make into a function because the code will be used in multiple places. I'm thinking that if an error happens in the validation function, I want the calling sub to know about that.

So what is the best way to let the container sub (that called the function) know about the error that happened in the function? And should the error message be generated in the function or the container sub?

I cannot seem to get the container sub to see the error generated by the function.

Or is there a better way?

Thanks.

Wayne
 
Trap the error in the sub, like on error goto SubErr in that way if the function will get an error it will jump to the sub on error action
 
A function will return one value. That said, sometimes if my function returns a positive value, I will trap an error and return a -999 back to the caller and set a trap (recovery gracefully condition) for the return value of -999.
Just be sure to include comments about any special conditions.
 
Further to Rx's suggestion. (I call that an "out of scope" return value as an error code.)

It is possible to indicate many independent conditions through an out of scope return value.

For example with positive values as normal returns, errors can be returned using the values of -1, -2, -4, -8, -16 etc. Any or all of these values can be combined by simply adding them.

They can be separated again by using bitwise And and Or operations.

Another alternative is to return a Variant which allows Null or NullString as potential independent error returns. Alternatively return the text of the error message. Of course the calling procedure must be able to manage this intelligently.

I try to avoid Variants but will use them for this if there is no better option.

The error can be returned as a ByRef function argument. This allows a function to output more than one variable.

Another way is to set up the function as a Method of Class. The current error is held as Property of the Class object allowing it to be interrogated after the function is run. The errors can even be held in a Collection and interrogated by a Method, providing access to the whole error history.
 
What I commonly do is edit the Err object's .Source property and re-raise it, so it can be handled by calling code. Consider...

Code:
private sub Consumer()
on error goto handler
[COLOR="Green"]  'calls producer[/COLOR]
  debug.print Producer
  exit sub
handler:
[COLOR="Green"]  'runs if producer raises an error[/COLOR]
  msgbox err & " " & err.description & " in " & err.source
End sub

private function Producer() as single
on error goto handler
[COLOR="Green"]  'causes error[/COLOR]
  Producer = 1/0
  exit function
handler:
  if err = 70 then [COLOR="Green"]'permission denied[/COLOR]
[COLOR="Green"]    'ignore this error[/COLOR]
  else
[COLOR="Green"]    'append my err.Source and re-raise for consumer to handle[/COLOR]
    err.raise err, err.Source & " in Producer()"
  end if
end function
So Producer() is free to succeed, fail silently, or raise errors that it can't handle and pass them up the stack. And if each error handler that re-raises this error appends it's sub/function name to err.Source, then you can return pretty detailed info about the origin of errors, even if they are handled AND nested a few layers deep.
 
Hi Wayne.

This is just personal opinion…

A lot of people ask for the best way to do something but there is no best way without knowing precisely what that something is.

In general:-
1. Errors should be fixed not handled. That may sound rather trite but it is quite correct. By the time it becomes necessary to show an error message to the end user the game is over.

By error message I mean the internal Access error message not what action the program might take in order to keep running. When it becomes necessary to show the end user the internal Access error message the programmer is basically saying “Sorry about that chief; I stuffed up!”

Now because it’s a programmer stuff up, if it wasn’t then the programmer would have fixed it, the error becomes unknown. It then becomes impossible for the code to correct for an unknown error.

If we add into the equation that the most important thing in a database is the data then there is little choice to be made. Reporting an internal Access error to the end user means it’s an unknown error. An unknown error can’t be corrected. The data is the most important thing. The data must be protected at all cost. No stuffing around with trying to save the error to a table; for all we know the network might be down. No stuffing around with trying to e-mail an error; the e-mail server may be down. Be careful even trying to display the error; it might be a reference problem.

The error is unknown. The application must cease operation under an unknown condition. The application must close to protect the data. The game over.


2. Errors should be handled by the caller not the called. It would be nice to think that the called procedure could protect itself but, in general in Access, it can’t. There is a concept, in other languages, that the called procedure should protect itself at all cost. It kind of makes some sense since that the protection is done in one place, it is better to write code in one place than in many places.

But it’s the data types available which make it almost impossible for a VBA called procedure to protect itself. You will see it written many times on the web that we should use the smallest data type available for a particular situation. For example; if you only need a Byte then use a Byte, if you only need an Integer use an Integer, blah, blah, blah. That type of posting on the web should be questioned. It is either being posted by someone from another language, or, it is being posted by someone who is simply reposting something they have read from another person who has worked in another language. The propagation of rubbish is rampant on the web.

VBA is a different bucket of bolts and it’s the Variant data type which makes it different. No other data type can handle the possibility that a passed argument is Null, only a Variant can.

So unless all data types, received by a procedure, are of type Variant then the procedure can not protect itself. If they are not Variants then the failure occurs in the caller. Such a failure will not even get through the pass to the called and therefore the called can not possibly handle it.

Hence, unless all received arguments are of type Variant then the calling procedure must do the protection because the called procedure can’t when passed a Null. And who writes all received arguments as type Variant? (Maybe we should???)

So there is no best way, only a precise definition of what is required under a particular circumstance.

Chris.
 
What I commonly do is edit the Err object's .Source property and re-raise it, so it can be handled by calling code.

After I posted my question, I discovered the Raise.Err method, and I found that the following works at passing the error to the calling sub:
Code:
ErrorHandler:
    Call Err.Raise(Err.Number)
    Exit Function

But you have added a helpful suggestion that helps identify the code in which the error occurred.

Thanks for the help.

P.S. What's the difference between Err.Raise ... and Err.Raise(...)?

Wayne
 
P.S. What's the difference between Err.Raise ... and Err.Raise(...)?
There are two ways to call a Sub, by using it's name only...
Code:
NameOfSub Parameter1, Parameter2
...and...
Code:
Call NameOfSub(Parameter1, Parameter2)
...and if you use the latter, then you enclose the parameter list in brackets.
 

Users who are viewing this thread

Back
Top Bottom