By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
424,680 Members | 1,475 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 424,680 IT Pros & Developers. It's quick & easy.

Metacode (was "Custom error numbers/messages tip")

P: n/a
Here is some code to generate code for raising and getting information about
custom errors in an application. Executing the GenerateXyzErrDefs procedure
generates the code.

<tblXyzError>
ErrorCodeOffset (Long Int) ErrorObjBaseName (Text(50))
ErrorDescription (Text(255))

1 DontDoThat
Hey! Don't do that!

2 NotThatEither Woah!
Don't do that either!

3 AttemptedInstanceReinit
Attempted to reinitialize an instance of an object type that does not allow
multiple initializations per instance.

</tblXyzError>
<clsErrorDefinition>
Option Compare Database
Option Explicit

Private Const mycVBErrMethodNotApplicableInContxt = 444&

Private mlngErrNumber As Long
Private mstrErrDescrip As String

Public Property Get Number() As Long
Number = mlngErrNumber
End Property

Public Property Get Description() As String
Description = mstrErrDescrip
End Property

Public Sub Setup(ByVal lngErrNumber As Long, strDescription As String)
If mlngErrNumber <> 0 Then XyzErrAttemptedInstanceReinit.Raise
mlngErrNumber = lngErrNumber
mstrErrDescrip = strDescription
End Sub

Public Sub Raise()
Err.Raise Number, , Description
End Sub
</clsErrorDefinition>
<basXyzCodeGeneration>
Option Compare Database
Option Explicit

Private Const mycXyzErrDefModuleName = "basXyzErrorDefinitions"
Private Const mycXyzErrorTableName = "tblXyzError"
Private Const mycXyzObjPrefix = "xyz"
Public Const xyzcErrorCodeBase = 1000&

Public Sub GenerateXyzErrDefs()
Dim vbcsProject As VBIDE.VBComponents
Dim vbcModule As VBIDE.VBComponent
Dim db As DAO.Database
Dim rstXyzErrors As DAO.Recordset
Dim strNewFuncName As String
Dim lngErrorNum As Long

Set vbcsProject = VBE.VBProjects(1).VBComponents
RemoveVBComponentIfExists vbcsProject, mycXyzErrDefModuleName
Set vbcModule = CreateNewVBModule(vbcsProject, mycXyzErrDefModuleName)
vbcModule.Name = mycXyzErrDefModuleName

Set db = CurrentDb
Set rstXyzErrors = db.OpenRecordset( _
"SELECT * FROM [" & mycXyzErrorTableName & "] " & _
"ORDER BY ErrorCodeOffset", _
dbOpenDynaset, dbReadOnly)
If rstXyzErrors.RecordCount > 0 Then
While Not rstXyzErrors.EOF
strNewFuncName = _
UpperCaseFirstStringChar(mycXyzObjPrefix) & _
"Err" & rstXyzErrors!ErrorObjBaseName
lngErrorNum = xyzcErrorCodeBase + rstXyzErrors!ErrorCodeOffset
vbcModule.CodeModule.InsertLines _
vbcModule.CodeModule.CountOfLines + 1, _
"Public Function " & strNewFuncName & "() As clsErrorDefinition" & vbCrLf & _
" Static sobjErrDefinition As clsErrorDefinition" & vbCrLf & _
" If sobjErrDefinition Is Nothing Then" & vbCrLf & _
" Set sobjErrDefinition = New clsErrorDefinition" & vbCrLf & _
" sobjErrDefinition.Setup _" & vbCrLf & _
" " & lngErrorNum & ", _" & vbCrLf & _
" """ & rstXyzErrors!ErrorDescription & """" & vbCrLf & _
" End If" & vbCrLf & _
" Set " & strNewFuncName & " = sobjErrDefinition" & vbCrLf & _
"End Function"
rstXyzErrors.MoveNext
Wend
End If
rstXyzErrors.Close: Set rstXyzErrors = Nothing
Set db = Nothing

End Sub

Private Sub RemoveVBComponentIfExists( _
vbcs As VBIDE.VBComponents, _
strModuleName As String _
)
Dim vbcModule As VBIDE.VBComponent
On Error Resume Next
Set vbcModule = vbcs(strModuleName)
On Error GoTo 0
If Not (vbcModule Is Nothing) Then
vbcs.Remove vbcModule
Set vbcModule = Nothing
End If
End Sub

Private Function CreateNewVBModule( _
vbcs As VBIDE.VBComponents, _
strModuleName As String _
) As VBIDE.VBComponent
Dim vbcResultModule As VBIDE.VBComponent
Set vbcResultModule = vbcs.Add(vbext_ct_StdModule)
vbcResultModule.Name = strModuleName
Set CreateNewVBModule = vbcResultModule
End Function
</basXyzCodeGeneration>
Running GenerateXyzErrDefs generates the basXyzErrorDefinitions module as
follows...

<basXyzErrorDefinitions>
Option Compare Database
Option Explicit

Public Function XyzErrDontDoThat() As clsErrorDefinition
Static sobjErrDefinition As clsErrorDefinition
If sobjErrDefinition Is Nothing Then
Set sobjErrDefinition = New clsErrorDefinition
sobjErrDefinition.Setup _
1001, _
"Hey! Don't do that!"
End If
Set XyzErrDontDoThat = sobjErrDefinition
End Function

Public Function XyzErrNotThatEither() As clsErrorDefinition
Static sobjErrDefinition As clsErrorDefinition
If sobjErrDefinition Is Nothing Then
Set sobjErrDefinition = New clsErrorDefinition
sobjErrDefinition.Setup _
1002, _
"Woah! Don't do that either!"
End If
Set XyzErrNotThatEither = sobjErrDefinition
End Function

Public Function XyzErrAttemptedInstanceReinit() As clsErrorDefinition
Static sobjErrDefinition As clsErrorDefinition
If sobjErrDefinition Is Nothing Then
Set sobjErrDefinition = New clsErrorDefinition
sobjErrDefinition.Setup _
1003, _
"Attempted to reinitialize an instance of an object type that does
not allow multiple initializations per instance."
End If
Set XyzErrAttemptedInstanceReinit = sobjErrDefinition
End Function
</basXyzErrorDefinitions>
Nov 12 '05 #1
Share this Question
Share on Google+
7 Replies


P: n/a
rkc

"Steve Jorgensen" <no****@nospam.nospam> wrote in message
news:06********************************@4ax.com...
Here is some code to generate code for raising and getting information about custom errors in an application. Executing the GenerateXyzErrDefs procedure generates the code.


I am completely baffled by this.

Why generate a basXyzErrorDefinitions module that contains nothing but
duplicate functions?

Why not generate a class that not only raises and reports on the error, but
also initializes the pairs?
Nov 12 '05 #2

P: n/a
On Mon, 03 Nov 2003 12:33:47 GMT, "rkc" <rk*@yabba.dabba.do.rochester.rr.bomb>
wrote:

"Steve Jorgensen" <no****@nospam.nospam> wrote in message
news:06********************************@4ax.com.. .
Here is some code to generate code for raising and getting information

about
custom errors in an application. Executing the GenerateXyzErrDefs

procedure
generates the code.


I am completely baffled by this.

Why generate a basXyzErrorDefinitions module that contains nothing but
duplicate functions?

Why not generate a class that not only raises and reports on the error, but
also initializes the pairs?


I'm not sure I understand the question, so I'n not sure if this addresses it,
but...

Instances of clsErrorDefinition represent individual error types. Each of the
functions in basXyzErrorDefinitions accesses an instance of clsErrorDefinition
for a particular type.

I don't want to generate separate class modules for each case because
A. That many class modules would be a big clutter in the VBE.
B. Calling code would have the extra code and extra overhead of creating an
instance of whichever class before using it.
C. It is nice for the calling code to be able to implement functions that
accept or return a value of type clsErrorDefinition, and use early binding
rather than having to use Object type because multiple class types are
possible, and resolve everything else at run-time. An Interface could solve
that, but wouldn't be compatible with Access 97 - which is nice to be.

If I don't generate separate class -modules- for each case, then something has
to initialize them and provide access to the correct one by name. I could
have passed just the ID, and let the class look up the rest from the table,
but the generator is already reading the table, and the resulting code is nto
to be directly maintained anyway, so why not just dump all the info into the
code during code generation?

We're not worried about duplication in the generated code because it's not
manually maintained. The function pattern need only be maintained in one
place in the code generator, and the error details need be maintained in only
one place, the table. I guess it's kind of like doing a mail merge in MS
Word. I probably should have a comment automatically inserted at the top of
the basXyzErrorDefinitions module saying how to maintain and regenerate it, so
it's obvious to anyone maintaining the code.

Note - in my original code, the following line should be removed because it
was part of an experiment that failed, and nothing now uses it...
Private Const mycVBErrMethodNotApplicableInContxt = 444&

Nov 12 '05 #3

P: n/a
rkc

"Steve Jorgensen" <no****@nospam.nospam> wrote in message
news:6v********************************@4ax.com...
On Mon, 03 Nov 2003 12:33:47 GMT, "rkc" <rk*@yabba.dabba.do.rochester.rr.bomb> wrote:

"Steve Jorgensen" <no****@nospam.nospam> wrote in message
news:06********************************@4ax.com.. .
Here is some code to generate code for raising and getting informationabout
custom errors in an application. Executing the GenerateXyzErrDefs

procedure
generates the code.


I am completely baffled by this.

Why generate a basXyzErrorDefinitions module that contains nothing but
duplicate functions?

Why not generate a class that not only raises and reports on the error, butalso initializes the pairs?


I'm not sure I understand the question, so I'n not sure if this addresses

it, but...
We've been through this before and I was completely rejected, but here goes.

Now that you have decided that using a class is going to be part of the
process, why not make it THE process.
Instances of clsErrorDefinition represent individual error types. Each of the functions in basXyzErrorDefinitions accesses an instance of clsErrorDefinition for a particular type.
You're using the class as just another function in the process instead of
encapsualting the process in the class.
I don't want to generate separate class modules for each case because
A. That many class modules would be a big clutter in the VBE.
There would only be one class.
B. Calling code would have the extra code and extra overhead of creating an instance of whichever class before using it.
Your calling code has to go through the process of calling a function which
has to go through the process of creating an instance of the class. Where's
the extra overhead if the calling code simply creates an instance instead?
C. It is nice for the calling code to be able to implement functions that
accept or return a value of type clsErrorDefinition, and use early binding
rather than having to use Object type because multiple class types are
possible, and resolve everything else at run-time. An Interface could solve that, but wouldn't be compatible with Access 97 - which is nice to be.
Once again, there only needs to be one class. One class that contains all
the error numbers and descriptions. One class that exposes the error
numbers as property get routines. One class that raises the correct error
through a public method that takes one of the error numbers defined
by itself as a parameter.
If I don't generate separate class -modules- for each case, then something has to initialize them and provide access to the correct one by name. I could
have passed just the ID, and let the class look up the rest from the table, but the generator is already reading the table, and the resulting code is nto to be directly maintained anyway, so why not just dump all the info into the code during code generation?
Dump all the info into the class during code generation.
We're not worried about duplication in the generated code because it's not
manually maintained.


It's still duplicate code and it's still ugly and completely un-necessary.
In my view, of course.

The code generation stuff you posted looks interesting and I will be taking
a further look at it.



Nov 12 '05 #4

P: n/a
rk*@yabba.dabba.do.rochester.rr.bomb (rkc) wrote in
<3d*******************@twister.nyroc.rr.com>:
Once again, there only needs to be one class. One class that
contains all the error numbers and descriptions. One class that
exposes the error numbers as property get routines. One class that
raises the correct error through a public method that takes one of
the error numbers defined by itself as a parameter.


Steve's gone in a very different direction than what I foresaw.

He's using a table to store his error definitions, as I suggested,
and his reason is to make it possible to keep multiple attributes
of a single error correlated.

I had suggested that once you do this, you really need only two
things:

1. a module with the constants for your error numbers (generated in
code).

2. a module or class with the functions necessary to raise the
error.

You'd pass the class the error constant and the class would then
raise the error with the appropriate error description and do
whatever else you wanted it to do. The class itself could have as
many methods and properties as you wanted, but I would make them
all generic, rather than generating a class with properties
specific to all your errors.

Steve's objection to the table was that you couldn't enforce it
with compiling. His solution to that was to autogenerate the
constants. My thought is that all you really need are the constants
for the error numbers themselves (which are actually the PK of your
table) as your way of getting to all the other attributes of your
errror. And your class or module for raising your error and
examining its attributes would simply be a lookup of a particular
error code in the error table.

With this solution you get:

1. simple, generic code.

2. as many custom attributes of your errors as you need.

3. with the auto-generated module of error number constants, you
get compile-time enforcement of error codes without needing to
worry about correlating a set of multiple error attributes for each
error, since the error number is itself a pathway into the lookup
for all the other attributes.

You'd only need one instance of your error class (if you went that
route), since you can only raise one error at a time.

--
David W. Fenton http://www.bway.net/~dfenton
dfenton at bway dot net http://www.bway.net/~dfassoc
Nov 12 '05 #5

P: n/a
On Mon, 03 Nov 2003 20:26:03 GMT, dX********@bway.net.invalid (David W.
Fenton) wrote:
rk*@yabba.dabba.do.rochester.rr.bomb (rkc) wrote in
<3d*******************@twister.nyroc.rr.com>:
Once again, there only needs to be one class. One class that
contains all the error numbers and descriptions. One class that
exposes the error numbers as property get routines. One class that
raises the correct error through a public method that takes one of
the error numbers defined by itself as a parameter.


Steve's gone in a very different direction than what I foresaw.

He's using a table to store his error definitions, as I suggested,
and his reason is to make it possible to keep multiple attributes
of a single error correlated.

I had suggested that once you do this, you really need only two
things:

1. a module with the constants for your error numbers (generated in
code).

2. a module or class with the functions necessary to raise the
error.

You'd pass the class the error constant and the class would then
raise the error with the appropriate error description and do
whatever else you wanted it to do. The class itself could have as
many methods and properties as you wanted, but I would make them
all generic, rather than generating a class with properties
specific to all your errors.

Steve's objection to the table was that you couldn't enforce it
with compiling. His solution to that was to autogenerate the
constants. My thought is that all you really need are the constants
for the error numbers themselves (which are actually the PK of your
table) as your way of getting to all the other attributes of your
errror. And your class or module for raising your error and
examining its attributes would simply be a lookup of a particular
error code in the error table.

With this solution you get:

1. simple, generic code.

2. as many custom attributes of your errors as you need.

3. with the auto-generated module of error number constants, you
get compile-time enforcement of error codes without needing to
worry about correlating a set of multiple error attributes for each
error, since the error number is itself a pathway into the lookup
for all the other attributes.

You'd only need one instance of your error class (if you went that
route), since you can only raise one error at a time.


My thinking was that if I'm generating the code anyway, why not take this a
step farther than simply generating a list of constants and a way to look up
the other information about the constants. Why not generate named functions
that directly return class instances that encapsulate all the properties and
methods of the errors.

To raise a "DontDoThat" error, just say XyzErrDontDoThat().Raise. You don't
have to think in terms of looking things up at all, just tell the error to
raise itself. Would you agree or disagree this is an improvement over
generating constants and passing them to one or more lookup functions.
Nov 12 '05 #6

P: n/a
no****@nospam.nospam (Steve Jorgensen) wrote in
<j9********************************@4ax.com>:
On Mon, 03 Nov 2003 20:26:03 GMT, dX********@bway.net.invalid
(David W. Fenton) wrote:
rk*@yabba.dabba.do.rochester.rr.bomb (rkc) wrote in
<3d*******************@twister.nyroc.rr.com>:
Once again, there only needs to be one class. One class that
contains all the error numbers and descriptions. One class that
exposes the error numbers as property get routines. One class
that raises the correct error through a public method that takes
one of the error numbers defined by itself as a parameter.
Steve's gone in a very different direction than what I foresaw.

He's using a table to store his error definitions, as I
suggested, and his reason is to make it possible to keep multiple
attributes of a single error correlated.

I had suggested that once you do this, you really need only two
things:

1. a module with the constants for your error numbers (generated
in code).

2. a module or class with the functions necessary to raise the
error.

You'd pass the class the error constant and the class would then
raise the error with the appropriate error description and do
whatever else you wanted it to do. The class itself could have as
many methods and properties as you wanted, but I would make them
all generic, rather than generating a class with properties
specific to all your errors.

Steve's objection to the table was that you couldn't enforce it
with compiling. His solution to that was to autogenerate the
constants. My thought is that all you really need are the
constants for the error numbers themselves (which are actually
the PK of your table) as your way of getting to all the other
attributes of your errror. And your class or module for raising
your error and examining its attributes would simply be a lookup
of a particular error code in the error table.

With this solution you get:

1. simple, generic code.

2. as many custom attributes of your errors as you need.

3. with the auto-generated module of error number constants, you
get compile-time enforcement of error codes without needing to
worry about correlating a set of multiple error attributes for
each error, since the error number is itself a pathway into the
lookup for all the other attributes.

You'd only need one instance of your error class (if you went
that route), since you can only raise one error at a time.


My thinking was that if I'm generating the code anyway, why not
take this a step farther than simply generating a list of
constants and a way to look up the other information about the
constants. Why not generate named functions that directly return
class instances that encapsulate all the properties and methods of
the errors.


As long as a human being is going to be progamming in the database,
seems to me you want the amount of generated code to be as small
as possible.
To raise a "DontDoThat" error, just say XyzErrDontDoThat().Raise.
You don't have to think in terms of looking things up at all, just
tell the error to raise itself. Would you agree or disagree this
is an improvement over generating constants and passing them to
one or more lookup functions.


No, because it proliferates a huge amount of duplicative code.

--
David W. Fenton http://www.bway.net/~dfenton
dfenton at bway dot net http://www.bway.net/~dfassoc
Nov 12 '05 #7

P: n/a
On Tue, 04 Nov 2003 16:50:30 GMT, dX********@bway.net.invalid (David W.
Fenton) wrote:

....
My thinking was that if I'm generating the code anyway, why not
take this a step farther than simply generating a list of
constants and a way to look up the other information about the
constants. Why not generate named functions that directly return
class instances that encapsulate all the properties and methods of
the errors.
As long as a human being is going to be progamming in the database,
seems to me you want the amount of generated code to be as small
as possible.


I don't know if that should be a concern. Generated code should be treated as
a black box. Only the metacode and code generator need be dealt with by the
programmer.
To raise a "DontDoThat" error, just say XyzErrDontDoThat().Raise.
You don't have to think in terms of looking things up at all, just
tell the error to raise itself. Would you agree or disagree this
is an improvement over generating constants and passing them to
one or more lookup functions.


No, because it proliferates a huge amount of duplicative code.


I disagree on this. I do have a problem with my own code, though. Having
part of the code split between the error definition class and the code
generator is not good. Next time I have time, I'll take another stab. I may
decide to do it the way you were suggesting - we'll see what I come up with.
Nov 12 '05 #8

This discussion thread is closed

Replies have been disabled for this discussion.