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

Custom error numbers/messages tip

P: n/a
If you've ever employed custom error numbers and messages in you programs,
you've probably ended up with code similar to what I've ended up with in
the past something like...

<code>
public Const xyzcErrNumBase As Long = 500

Public Const xyzcErrNumHeyDontDoThat = xyzcErrNumBase + 1
Public Const xyzcErrTxtHeyDontDoThat = "Hey! Don't do that!"

<...>

Err.Raise xyzcErrNumHeyDontDoThat, , xyzcErrTxtHeyDontDoThat

<...>

</code>
There is a duplication problem here. Every time you want to raise the
error, you have to pass both constants, and there's nothing enforcing the
fact that the constants you pass are the ones that go together. It's easy
to make a mistake (I've made them), and the mistake is likely to go
unnoticed until after it's had you scratching your head for hours in a
debugging session at some point looking for the wrong error.

VB/VBA doesn't offer much by way of help for this such as array constants,
etc., but I figured out a clever way to clean things up a bit. Combine the
number and decription into one string, then write a function to parse out
the pieces, and raise the error.

<code>
public Const xyzcErrNumBase = 500

Public Const xyzcErrHeyDontDoThat = _
xyzcErrNumBase + 1 & ":Hey! Don't do that!"

Public Sub RaiseCustomErr(strErrDefinition As String)
Dim lngColonPos As Long
Dim lngErrNum As Long
Dim strErrMessage As String
lngColonPos = InStr(strErrorSpec, ":")
lngErrNum = CLng(Left$(strErrorSpec, lngColonPos - 1))
strErrMessage = Mid$(strErrorSpec, lngColonPos + 1)
Err.Raise lngErrNum, , strErrMessage
End Sub

<...>

RaiseCustomErr xyzcErrHeyDontDoThat

<...>

</code>

Nov 12 '05 #1
Share this Question
Share on Google+
17 Replies


P: n/a
no****@nospam.nospam (Steve Jorgensen) wrote in
<q1********************************@4ax.com>:
If you've ever employed custom error numbers and messages in you
programs, you've probably ended up with code similar to what I've
ended up with in the past something like...

<code>
public Const xyzcErrNumBase As Long = 500

Public Const xyzcErrNumHeyDontDoThat = xyzcErrNumBase + 1
Public Const xyzcErrTxtHeyDontDoThat = "Hey! Don't do that!"

<...>

Err.Raise xyzcErrNumHeyDontDoThat, , xyzcErrTxtHeyDontDoThat

<...>

</code>
There is a duplication problem here. Every time you want to raise
the error, you have to pass both constants, and there's nothing
enforcing the fact that the constants you pass are the ones that
go together. It's easy to make a mistake (I've made them), and
the mistake is likely to go unnoticed until after it's had you
scratching your head for hours in a debugging session at some
point looking for the wrong error.

VB/VBA doesn't offer much by way of help for this such as array
constants, etc., but I figured out a clever way to clean things up
a bit. Combine the number and decription into one string, then
write a function to parse out the pieces, and raise the error.

<code>
public Const xyzcErrNumBase = 500

Public Const xyzcErrHeyDontDoThat = _
xyzcErrNumBase + 1 & ":Hey! Don't do that!"

Public Sub RaiseCustomErr(strErrDefinition As String)
Dim lngColonPos As Long
Dim lngErrNum As Long
Dim strErrMessage As String
lngColonPos = InStr(strErrorSpec, ":")
lngErrNum = CLng(Left$(strErrorSpec, lngColonPos - 1))
strErrMessage = Mid$(strErrorSpec, lngColonPos + 1)
Err.Raise lngErrNum, , strErrMessage
End Sub

<...>

RaiseCustomErr xyzcErrHeyDontDoThat

<...>

</code>


See, Steve, to me, that's a wrongheaded way to do it, as it's a
kludge, to my way of thinking. Yes, it's fast to implement it, but
is it really that great? It also is susceptible to typos.

To me, since this is something you're going to re-use (right?), it
makes more sense to populate an array with the pairs and then wrap
that in a function or class that will raise the appropriate error.
It would require a bit of thought, yes, as you'd need something in
it that was similar to those nice easy-to-understand constant
names.

Another kludgy way to do it would be to pass the constant name to a
function and have your function Eval() the strings to get the
values. If you passed it "xyzcErrNumHeyDontDoThat", it would then
Eval("xyzcErrNumHeyDontDoThat") to get the number, and then
Eval(Left("xyzcErrNumHeyDontDoThat",7) & "Txt" &
Mid("xyzcErrNumHeyDontDoThat",11)).

Is that any less kludgy than your method? I think so, because the
result is that it gives you the same capability in a single
function without having to screw up your nice error constants.

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

P: n/a
On Mon, 27 Oct 2003 21:05:33 GMT, dX********@bway.net.invalid (David W.
Fenton) wrote:

....
See, Steve, to me, that's a wrongheaded way to do it, as it's a
kludge, to my way of thinking. Yes, it's fast to implement it, but
is it really that great? It also is susceptible to typos.

To me, since this is something you're going to re-use (right?), it
makes more sense to populate an array with the pairs and then wrap
that in a function or class that will raise the appropriate error.
It would require a bit of thought, yes, as you'd need something in
it that was similar to those nice easy-to-understand constant
names.
I thought of that idea, but then how do you name the values you pass to the
function? If you define constants, then you have duplication between the
constant declarations and the array offset positioning, and they're also
not in the same place in the code..
Another kludgy way to do it would be to pass the constant name to a
function and have your function Eval() the strings to get the
values. If you passed it "xyzcErrNumHeyDontDoThat", it would then
Eval("xyzcErrNumHeyDontDoThat") to get the number, and then
Eval(Left("xyzcErrNumHeyDontDoThat",7) & "Txt" &
Mid("xyzcErrNumHeyDontDoThat",11)).
Sure, but then you lose compile-time checking. You won't know if you
mis-typed the name until your code tries to raise the error. Also, if I
remember correctly, you can't evaluate global variables or constants in an
Eval, only global functions (I could be wrong, but that's my recollection).
Is that any less kludgy than your method? I think so, because the
result is that it gives you the same capability in a single
function without having to screw up your nice error constants.


I went for the approach I did because I was looking for something the
compiler would enforce. With my proposal, if you mis-type the error you
ahve to fix it before you can compile.
Nov 12 '05 #3

P: n/a
On Mon, 27 Oct 2003 21:05:33 GMT, dX********@bway.net.invalid (David W.
Fenton) wrote:

....
See, Steve, to me, that's a wrongheaded way to do it, as it's a
kludge, to my way of thinking. Yes, it's fast to implement it, but
is it really that great? It also is susceptible to typos.


Well, I still say it's not that kludgey, and less so than the staus quo,
but how about this improvement. Why am I adding the offset in each
constant declaration when that could be done in a function that can be
called directly as well as by the raise function?

This thinking leads to...

<code>
Public Const xyzcErrNumBase = 500

Public Const xyzcErrHeyDontDoThat = _
"1:Hey! Don't do that!"

<...>

Public Function DecodeXyzErrDef( _
strErrorSpec As String, _
Optional ByRef strErrMessage As String _
) As Long
Dim lngColonPos As Long
lngColonPos = InStr(strErrorSpec, ":")
strErrMessage = Mid$(strErrorSpec, lngColonPos + 1)
DecodeXyzErrDef = _
xyzcErrNumBase + _
CLng(Left$(strErrorSpec, lngColonPos - 1))
End Function

Public Sub RaiseXyzErr(strErrSpec As String)
Dim lngErrNum As Long
Dim strErrMessage As String
lngErrNum = DecodeXyzErrDef(strErrSpec, strErrMessage)
Err.Raise lngErrNum, , strErrMessage
End Sub

<...>

RaiseXyzErr xyzcErrHeyDontDoThat

<...>

</code>

Nov 12 '05 #4

P: n/a
On Tue, 28 Oct 2003 07:36:45 GMT, Steve Jorgensen <no****@nospam.nospam>
wrote:

I posted too soon. A Type declaration can clean this up still more...

<code>

Public Type xyztErrorDef
Number As Long
Description As String
End Type

Public Const xyzcErrNumBase = 500

Public Const xyzcErrHeyDontDoThat = _
"1:Hey! Don't do that!"

<...>

Public Function DecodeXyzErrDef( _
strErrorSpec As String _
) As xyztErrorDef
Dim lngColonPos As Long
Dim xyzReturn As xyztErrorDef
lngColonPos = InStr(strErrorSpec, ":")
With xyzReturn
.Number = _
xyzcErrNumBase + _
CLng(Left$(strErrorSpec, lngColonPos - 1))
.Description = Mid$(strErrorSpec, lngColonPos + 1)
End With
DecodeXyzErrDef = xyzReturn
End Function

Public Sub RaiseXyzErr(strErrSpec As String)
Dim lngErrNum As Long
Dim strErrDescription As String
With DecodeXyzErrDef(strErrSpec)
Err.Raise .Number, , .Description
End With
End Sub

<...>

RaiseXyzErr xyzcErrHeyDontDoThat

<...>

</code>

Nov 12 '05 #5

P: n/a
no****@nospam.nospam (Steve Jorgensen) wrote in
<0v********************************@4ax.com>:
On Mon, 27 Oct 2003 21:05:33 GMT, dX********@bway.net.invalid
(David W. Fenton) wrote:

...
See, Steve, to me, that's a wrongheaded way to do it, as it's a
kludge, to my way of thinking. Yes, it's fast to implement it,
but is it really that great? It also is susceptible to typos.

To me, since this is something you're going to re-use (right?),
it makes more sense to populate an array with the pairs and then
wrap that in a function or class that will raise the appropriate
error. It would require a bit of thought, yes, as you'd need
something in it that was similar to those nice easy-to-understand
constant names.


I thought of that idea, but then how do you name the values you
pass to the function? If you define constants, then you have
duplication between the constant declarations and the array offset
positioning, and they're also not in the same place in the code..


Well, gosh, you could put them in a table. Wouldn't that be horrid?
Another kludgy way to do it would be to pass the constant name to
a function and have your function Eval() the strings to get the
values. If you passed it "xyzcErrNumHeyDontDoThat", it would then
Eval("xyzcErrNumHeyDontDoThat") to get the number, and then
Eval(Left("xyzcErrNumHeyDontDoThat",7) & "Txt" &
Mid("xyzcErrNumHeyDontDoThat",11)).


Sure, but then you lose compile-time checking. You won't know if
you mis-typed the name until your code tries to raise the error.
Also, if I remember correctly, you can't evaluate global variables
or constants in an Eval, only global functions (I could be wrong,
but that's my recollection).


OK, I guess you're right about that after all.
Is that any less kludgy than your method? I think so, because the
result is that it gives you the same capability in a single
function without having to screw up your nice error constants.


I went for the approach I did because I was looking for something
the compiler would enforce. With my proposal, if you mis-type the
error you ahve to fix it before you can compile.


But you've now got constants that are useless without your code,
and that bothers me a lot.

I'm not sure I see the problem, myself, to be honest. I've never
raised a custom error in my life. If I were going to do so, I'm
pretty sure I'd store it in a data table, not in constants. Of
course, I don't know if that could lead to errors in your error
handler.

All told, I don't think saving typing one parameter is really worth
all the work, to be honest, unless the function you wrap around the
error raising command is going to do more than the plain
functionality.

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

P: n/a
On Tue, 28 Oct 2003 20:56:13 GMT, dX********@bway.net.invalid (David W.
Fenton) wrote:
....
I went for the approach I did because I was looking for something
the compiler would enforce. With my proposal, if you mis-type the
error you have to fix it before you can compile.
But you've now got constants that are useless without your code,
and that bothers me a lot.


Should it? The error constants are in the same module as the functions
that decode and make use of them. Any code that uses one would want to use
the other. What makes this different than defining an enumerated type for
a parameter to pass to a function? The function and the constants are
designed to work together.
I'm not sure I see the problem, myself, to be honest. I've never
raised a custom error in my life. If I were going to do so, I'm
pretty sure I'd store it in a data table, not in constants. Of
course, I don't know if that could lead to errors in your error
handler.
It's pretty common practice to use error code constants in a program. I
don't make really heavy use of custom error definitions, but several of my
programs have them. I like to check parameter validity at a high level so
errors are trapperd earlier where they're easier to debug. Sometimes, I
just raise an error 5, but sometimes I want a more descriptive error
message, so if it gets trapped by a handler and reported to a user, it's a
more friendly and useful message.

In addition, this idea isn't just useful for error handlers, it applies to
any case when you want a set of correlated constant values, and want to
figure out how to enforce the correlation in code. My answer is, encode
them into a string constant, and parse them out at run-time. See my other
sub-thread for a cleaner implementation than my original proposal.

Finally, a table simply doesn't do what I'm after. I want a named constant
that the compiler can check. Put it in a table, and you can break reams of
code by changing a character in one record, and the compiler will not know
anything went wrong.
All told, I don't think saving typing one parameter is really worth
all the work, to be honest, unless the function you wrap around the
error raising command is going to do more than the plain
functionality.


My respones are...
A. It's not about saving the typing, it's about preventing errors in
pairing the constants in various parts of the code. Is it not a good thing
to be able to enforce that A always goes with AA, B with BB, etc.?
B. In the evolution of a program, it would be unusual if the function did
not expand to do more, right?
Nov 12 '05 #7

P: n/a
rkc

"Steve Jorgensen" <no****@nospam.nospam> wrote in message
news:0v********************************@4ax.com...
On Mon, 27 Oct 2003 21:05:33 GMT, dX********@bway.net.invalid (David W.
Fenton) wrote:

...
See, Steve, to me, that's a wrongheaded way to do it, as it's a
kludge, to my way of thinking. Yes, it's fast to implement it, but
is it really that great? It also is susceptible to typos.

To me, since this is something you're going to re-use (right?), it
makes more sense to populate an array with the pairs and then wrap
that in a function or class that will raise the appropriate error.
It would require a bit of thought, yes, as you'd need something in
it that was similar to those nice easy-to-understand constant
names.
I thought of that idea, but then how do you name the values you pass to

the function? If you define constants, then you have duplication between the
constant declarations and the array offset positioning, and they're also
not in the same place in the code..


The first thing I thought of when you posted your solution (hadn't thought
about it at all before then) was to write an error class. Define the error
numbers and corresponding strings as private properties. Define a private
collection to hold the error strings using the error numbers as the key.
Load the collection in the Initialize event. Define getters for the error
numbers. Define a Public RaiseError method.

PublicSub RaiseError (errNum as Long)
use the errNum to retrieve the errText from the collection
do whatever the RaiseError method should do
End Sub

Call the RaiseError method like so e. RaiseError e.DagNabIt

I guess the major benefit to all this would be intellisense for the
errNumbers. The errNumbers and errText are defined and managed
by the class. You could also implement logging in the class if
you had a desire to do that.

Nov 12 '05 #8

P: n/a
no****@nospam.nospam (Steve Jorgensen) wrote in
<dc********************************@4ax.com>:
On Tue, 28 Oct 2003 20:56:13 GMT, dX********@bway.net.invalid
(David W. Fenton) wrote:
...
I went for the approach I did because I was looking for
something the compiler would enforce. With my proposal, if you
mis-type the error you have to fix it before you can compile.
But you've now got constants that are useless without your code,
and that bothers me a lot.


Should it? The error constants are in the same module as the
functions that decode and make use of them. Any code that uses
one would want to use the other. What makes this different than
defining an enumerated type for a parameter to pass to a function?
The function and the constants are designed to work together.


But they must be public and therefore the values make no sense.

I am more and more leaning to a class module solution for this, but
I haven't thought through all of it so I can't say what is best.
I'm not sure I see the problem, myself, to be honest. I've never
raised a custom error in my life. If I were going to do so, I'm
pretty sure I'd store it in a data table, not in constants. Of
course, I don't know if that could lead to errors in your error
handler.


It's pretty common practice to use error code constants in a
program. I don't make really heavy use of custom error
definitions, but several of my programs have them. I like to
check parameter validity at a high level so errors are trapperd
earlier where they're easier to debug. Sometimes, I just raise an
error 5, but sometimes I want a more descriptive error message, so
if it gets trapped by a handler and reported to a user, it's a
more friendly and useful message.

In addition, this idea isn't just useful for error handlers, it
applies to any case when you want a set of correlated constant
values, and want to figure out how to enforce the correlation in
code. My answer is, encode them into a string constant, and parse
them out at run-time. See my other sub-thread for a cleaner
implementation than my original proposal.


Well, I'm not sure I understand why a data table wouldn't make
sense, as you can have as many correlated values as needed (fields)
in a record accessible by its primary key (the error number).

If it's general practice to use constants for this, so what? What
does that get you in terms of functionality and reliability and
maintainability?
Finally, a table simply doesn't do what I'm after. I want a named
constant that the compiler can check. Put it in a table, and you
can break reams of code by changing a character in one record, and
the compiler will not know anything went wrong.
What about a class module with public members that are your
constants? Those would be checked by the compiler and you could use
them as the path into the data table.
All told, I don't think saving typing one parameter is really
worth all the work, to be honest, unless the function you wrap
around the error raising command is going to do more than the
plain functionality.


My respones are...
A. It's not about saving the typing, it's about preventing errors
in pairing the constants in various parts of the code. Is it not
a good thing to be able to enforce that A always goes with AA, B
with BB, etc.?


Then go all the way and replace it with a class module that takes
the error number and looks up the error message for you.
. . . B. In the evolution of a program, it would be
unusual if the function did not expand to do more, right?


You're the one who is always arguing that you shouldn't code for
requirements that do not yet exist, right?

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

P: n/a

On Wed, 29 Oct 2003 22:20:50 GMT, dX********@bway.net.invalid (David W.
Fenton) wrote:
no****@nospam.nospam (Steve Jorgensen) wrote in
<dc********************************@4ax.com>:
On Tue, 28 Oct 2003 20:56:13 GMT, dX********@bway.net.invalid
(David W. Fenton) wrote:
...
I went for the approach I did because I was looking for
something the compiler would enforce. With my proposal, if you
mis-type the error you have to fix it before you can compile.

But you've now got constants that are useless without your code,
and that bothers me a lot.
Should it? The error constants are in the same module as the
functions that decode and make use of them. Any code that uses
one would want to use the other. What makes this different than
defining an enumerated type for a parameter to pass to a function?
The function and the constants are designed to work together.


But they must be public and therefore the values make no sense.


No, they specifically need to be public. It may make sense to raise the
same error form more than one module, and the error definitely needs to be
analyzed (compare the error number with the one in the error definition,
however that's implemented) from more than one module (any module that
wants to check what error number was raised in the called procedure).
I am more and more leaning to a class module solution for this, but
I haven't thought through all of it so I can't say what is best.
I thought about a class module. I always think about a class module. I
couldn't see how it would improve this code at all or make it easier to
use, though. If anything, it seemed it would add steps to the calling
code.

....
Well, I'm not sure I understand why a data table wouldn't make
sense, as you can have as many correlated values as needed (fields)
in a record accessible by its primary key (the error number).

If it's general practice to use constants for this, so what? What
does that get you in terms of functionality and reliability and
maintainability?


Tables are fine for storing correlated data, but they are not part of what
is enforced by the compiler. I was expecting you to be more in agreement
with this idea because it's similar to using strong data typing which is
something I believe you've always been in favor of.

Here's the deal. If I put the definitions in a table, I have to look them
up with some key. The key has to be in the code because the code is what
defines which error is invoked. If the ID is numeric, we don't want
undocumented numeric constants in our code, so we define a constant, or we
use a string so it's self-documenting, but then there is a danger of
mis-typing the string, so again, we would define a constant rather than
using the raw string. So now, with the table, you have the choice of
either non-checked code or code with both constants and table records that
mirror each other with no enforcement that they are in sync.

If we can get all the data about an error into a single, named constant,
then we have compile-time checking, and we have avoided duplication,
keeping the data in a single place.
Finally, a table simply doesn't do what I'm after. I want a named
constant that the compiler can check. Put it in a table, and you
can break reams of code by changing a character in one record, and
the compiler will not know anything went wrong.


What about a class module with public members that are your
constants? Those would be checked by the compiler and you could use
them as the path into the data table.


And this is better than constants how? OK, we now have named variables
instead of named constants, and they're in a class now where we can't use
them unless we create an instance of the class. Furthermore, if they're
variables rather than constants, there now must be code to initialize them.

Now we're back to duplication. You first define a list of variables, then
you create a list of initializations that must match. The compiler will
tell you if you try to initialize one that's not there, but it won't tell
you if you added a variable and forgot to initialize it. There is no such
problem using the constants.
All told, I don't think saving typing one parameter is really
worth all the work, to be honest, unless the function you wrap
around the error raising command is going to do more than the
plain functionality.


My respones are...
A. It's not about saving the typing, it's about preventing errors
in pairing the constants in various parts of the code. Is it not
a good thing to be able to enforce that A always goes with AA, B
with BB, etc.?


Then go all the way and replace it with a class module that takes
the error number and looks up the error message for you.


Same point as above. I thought about approaches using class modules, but
they all added complexity and duplication rather than removing it. I feel
the solution I came up with is the most concise and safe one that I have
been made aware of.
. . . B. In the evolution of a program, it would be
unusual if the function did not expand to do more, right?


You're the one who is always arguing that you shouldn't code for
requirements that do not yet exist, right?


Yes, but I wasn't the one who thought this code was not appropriate to the
task at hand. In my opinion, this code was to remove duplication for a
requirement that does exist. In most cases where the argument could be
made that the code written to remove duplication looks excessive at the
time, I would argue that's OK, because it'll most likely get reused and
extended, and it'll be more likely to be reused because it now does exist
(it's hard to reuse code that doesn't yet exist <g>).
Nov 12 '05 #10

P: n/a
On Wed, 29 Oct 2003 15:21:12 GMT, "rkc"
<rk*@yabba.dabba.do.rochester.rr.bomb> wrote:

"Steve Jorgensen" <no****@nospam.nospam> wrote in message
news:0v********************************@4ax.com.. .
On Mon, 27 Oct 2003 21:05:33 GMT, dX********@bway.net.invalid (David W.
Fenton) wrote:

...
>See, Steve, to me, that's a wrongheaded way to do it, as it's a
>kludge, to my way of thinking. Yes, it's fast to implement it, but
>is it really that great? It also is susceptible to typos.
>
>To me, since this is something you're going to re-use (right?), it
>makes more sense to populate an array with the pairs and then wrap
>that in a function or class that will raise the appropriate error.
>It would require a bit of thought, yes, as you'd need something in
>it that was similar to those nice easy-to-understand constant
>names.
I thought of that idea, but then how do you name the values you pass to

the
function? If you define constants, then you have duplication between the
constant declarations and the array offset positioning, and they're also
not in the same place in the code..


The first thing I thought of when you posted your solution (hadn't thought
about it at all before then) was to write an error class. Define the error
numbers and corresponding strings as private properties. Define a private
collection to hold the error strings using the error numbers as the key.
Load the collection in the Initialize event. Define getters for the error
numbers. Define a Public RaiseError method.

PublicSub RaiseError (errNum as Long)
use the errNum to retrieve the errText from the collection
do whatever the RaiseError method should do
End Sub


I believe my reply to David's latest covers my response to this suggestion.
If you could find a way to eliminate the duplication, and ge the compiler
to ensure that every number was paired with a string, get the list of
numbers/names together where they're easy to read and check, etc., I'm all
ears (er - eyes).

Call the RaiseError method like so e. RaiseError e.DagNabIt

I guess the major benefit to all this would be intellisense for the
errNumbers. The errNumbers and errText are defined and managed
by the class. You could also implement logging in the class if
you had a desire to do that.


And the constants give you intellisense for the constant names using
ctrl+Space. They all start with the same prefix, so they're easy to find
and browse in the intellisense listing. Also, even with a standard module,
you can actually include the module name as a prefix, and get a lising of
all its public members in intellisense, though it's true the procedure's
parameter type doesn't make it happen automatically.
Nov 12 '05 #11

P: n/a
rkc

"Steve Jorgensen" <no****@nospam.nospam> wrote in message
news:m5********************************@4ax.com...
On Wed, 29 Oct 2003 15:21:12 GMT, "rkc"
<rk*@yabba.dabba.do.rochester.rr.bomb> wrote: I believe my reply to David's latest covers my response to this

suggestion.

Fair enough. Nothing further from me.
Nov 12 '05 #12

P: n/a
no****@nospam.nospam (Steve Jorgensen) wrote in
<nu********************************@4ax.com>:
On Wed, 29 Oct 2003 22:20:50 GMT, dX********@bway.net.invalid
(David W. Fenton) wrote:
no****@nospam.nospam (Steve Jorgensen) wrote in
<dc********************************@4ax.com>:
On Tue, 28 Oct 2003 20:56:13 GMT, dX********@bway.net.invalid
(David W. Fenton) wrote:
[]
Well, I'm not sure I understand why a data table wouldn't make
sense, as you can have as many correlated values as needed
(fields) in a record accessible by its primary key (the error
number).

If it's general practice to use constants for this, so what? What
does that get you in terms of functionality and reliability and
maintainability?
Tables are fine for storing correlated data, but they are not part
of what is enforced by the compiler. I was expecting you to be
more in agreement with this idea because it's similar to using
strong data typing which is something I believe you've always been
in favor of.

Here's the deal. If I put the definitions in a table, I have to
look them up with some key. The key has to be in the code because
the code is what defines which error is invoked. If the ID is
numeric, we don't want undocumented numeric constants in our code,
so we define a constant, or we use a string so it's
self-documenting, but then there is a danger of mis-typing the
string, so again, we would define a constant rather than using the
raw string. So now, with the table, you have the choice of either
non-checked code or code with both constants and table records
that mirror each other with no enforcement that they are in sync.

If we can get all the data about an error into a single, named
constant, then we have compile-time checking, and we have avoided
duplication, keeping the data in a single place.


RKC suggested an array or collection with the index numbers being
the error codes. A collection wouldn't work very well here, I don't
think, but an array certainly would, and could be populated from a
table.

However, it wouldn't give you compile-time errors. But it also
wouldn't fall over if you typed wrong, since you could make your
return function smart enough to handle it.
Finally, a table simply doesn't do what I'm after. I want a
named constant that the compiler can check. Put it in a table,
and you can break reams of code by changing a character in one
record, and the compiler will not know anything went wrong.


What about a class module with public members that are your
constants? Those would be checked by the compiler and you could
use them as the path into the data table.


And this is better than constants how? OK, we now have named
variables instead of named constants, and they're in a class now
where we can't use them unless we create an instance of the class.
Furthermore, if they're variables rather than constants, there
now must be code to initialize them.


Yes, from the table that holds the error code definitions.

Microsoft does this in the wizards for all kinds of things, not
just error messages.

The error code members are initialized to values from the tables in
the class module's .Initialize event.

Then you have a public function that you pass an error number
(using a public member) and it looks up the error message from the
data table (or you could have a recordset in memory if you wanted).

And that makes it possible to have more than a pair of values. You
could, for instance, have error classifications, such as CRITICAL,
INFORMATION, etc. (like in the Windows event log, for instance).

This means the data table is definitive for the definition of the
error number/description pairs. The hard part is assigning the
values to the variables, since you can't do it with Eval(), I don't
guess.

But that is little more typing than the constants you are typing,
except that you'd want to write a subroutine for taking a variable
and assigning the value, something like this:

c_YourConstant = GetConstant("c_YourConstant")

and that assumes that you've got a field in your data table with
the constant name in it. You'd need a line of this for each of your
constants.

And, yes, that means adding a variable at in the class declarations
and also adding a line in the subroutine that initializes those
variables, so, yes, it's not as "efficient" as your solution.

See, my objection is that a constant value like "1:YourTextHere"
looks like a multi-value field in a data table, which violates
normalization. Obviously, constants are not fields in a data table,
of course, but the idea of mixing values and unpacking them bothers
me.
Now we're back to duplication. You first define a list of
variables, then you create a list of initializations that must
match. The compiler will tell you if you try to initialize one
that's not there, but it won't tell you if you added a variable
and forgot to initialize it. There is no such problem using the
constants.


This is, indeed, a flaw of the class module-based solution.

Your solution has flaws, too, and the way you've proposed it,
solves only one problem and is not extensible.
All told, I don't think saving typing one parameter is really
worth all the work, to be honest, unless the function you wrap
around the error raising command is going to do more than the
plain functionality.

My respones are...
A. It's not about saving the typing, it's about preventing
errors in pairing the constants in various parts of the code.
Is it not a good thing to be able to enforce that A always goes
with AA, B with BB, etc.?


Then go all the way and replace it with a class module that takes
the error number and looks up the error message for you.


Same point as above. I thought about approaches using class
modules, but they all added complexity and duplication rather than
removing it. I feel the solution I came up with is the most
concise and safe one that I have been made aware of.


And I feel it's inelegant and problematic.
. . . B. In the evolution of a program, it would be
unusual if the function did not expand to do more, right?


You're the one who is always arguing that you shouldn't code for
requirements that do not yet exist, right?


Yes, but I wasn't the one who thought this code was not
appropriate to the task at hand. In my opinion, this code was to
remove duplication for a requirement that does exist. In most
cases where the argument could be made that the code written to
remove duplication looks excessive at the time, I would argue
that's OK, because it'll most likely get reused and extended, and
it'll be more likely to be reused because it now does exist (it's
hard to reuse code that doesn't yet exist <g>).


We'll just have to agree to disagree on this one.

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

P: n/a
rkc

"David W. Fenton" <dX********@bway.net.invalid> wrote in message
news:94***************************@24.168.128.74.. .
no****@nospam.nospam (Steve Jorgensen) wrote in
<nu********************************@4ax.com>:
On Wed, 29 Oct 2003 22:20:50 GMT, dX********@bway.net.invalid
(David W. Fenton) wrote:
no****@nospam.nospam (Steve Jorgensen) wrote in
<dc********************************@4ax.com>:

On Tue, 28 Oct 2003 20:56:13 GMT, dX********@bway.net.invalid
(David W. Fenton) wrote:
[]
Well, I'm not sure I understand why a data table wouldn't make
sense, as you can have as many correlated values as needed
(fields) in a record accessible by its primary key (the error
number).

If it's general practice to use constants for this, so what? What
does that get you in terms of functionality and reliability and
maintainability?


Tables are fine for storing correlated data, but they are not part
of what is enforced by the compiler. I was expecting you to be
more in agreement with this idea because it's similar to using
strong data typing which is something I believe you've always been
in favor of.

Here's the deal. If I put the definitions in a table, I have to
look them up with some key. The key has to be in the code because
the code is what defines which error is invoked. If the ID is
numeric, we don't want undocumented numeric constants in our code,
so we define a constant, or we use a string so it's
self-documenting, but then there is a danger of mis-typing the
string, so again, we would define a constant rather than using the
raw string. So now, with the table, you have the choice of either
non-checked code or code with both constants and table records
that mirror each other with no enforcement that they are in sync.

If we can get all the data about an error into a single, named
constant, then we have compile-time checking, and we have avoided
duplication, keeping the data in a single place.


RKC suggested an array or collection with the index numbers being
the error codes. A collection wouldn't work very well here, I don't
think, but an array certainly would, and could be populated from a
table.


I suggested a collection because of the named key. At the time I was
thinking of error codes like 100, 200, 300 instead of 1, 2, 3.

I don't see the point of introducing a table into the equation. It
breaks the self contained nature of a class solution and adds
the potential for errors when retrieving the data.

I also see no real problem with Steve's solution other than it
strikes me as being ugly and I personally wouldn't use it.


Nov 12 '05 #14

P: n/a
On Thu, 30 Oct 2003 20:56:38 GMT, dX********@bway.net.invalid (David W.
Fenton) wrote:

....
Here's the deal. If I put the definitions in a table, I have to
look them up with some key. The key has to be in the code because
the code is what defines which error is invoked. If the ID is
numeric, we don't want undocumented numeric constants in our code,
so we define a constant, or we use a string so it's
self-documenting, but then there is a danger of mis-typing the
string, so again, we would define a constant rather than using the
raw string. So now, with the table, you have the choice of either
non-checked code or code with both constants and table records
that mirror each other with no enforcement that they are in sync.

If we can get all the data about an error into a single, named
constant, then we have compile-time checking, and we have avoided
duplication, keeping the data in a single place.
RKC suggested an array or collection with the index numbers being
the error codes. A collection wouldn't work very well here, I don't
think, but an array certainly would, and could be populated from a
table.


We're all familliar with my arguments to that by now.
However, it wouldn't give you compile-time errors. But it also
wouldn't fall over if you typed wrong, since you could make your
return function smart enough to handle it.
Well, what do you mean by fall over. I guess it could simply say
"undefined <xyz> errror" which is not catastrophic, I guess. Applications
other than error reporting could be more sensitive, though.

....
And this is better than constants how? OK, we now have named
variables instead of named constants, and they're in a class now
where we can't use them unless we create an instance of the class.
Furthermore, if they're variables rather than constants, there
now must be code to initialize them.


Yes, from the table that holds the error code definitions.

Microsoft does this in the wizards for all kinds of things, not
just error messages.

The error code members are initialized to values from the tables in
the class module's .Initialize event.

Then you have a public function that you pass an error number
(using a public member) and it looks up the error message from the
data table (or you could have a recordset in memory if you wanted).

And that makes it possible to have more than a pair of values. You
could, for instance, have error classifications, such as CRITICAL,
INFORMATION, etc. (like in the Windows event log, for instance).

This means the data table is definitive for the definition of the
error number/description pairs. The hard part is assigning the
values to the variables, since you can't do it with Eval(), I don't
guess.

But that is little more typing than the constants you are typing,
except that you'd want to write a subroutine for taking a variable
and assigning the value, something like this:

c_YourConstant = GetConstant("c_YourConstant")

and that assumes that you've got a field in your data table with
the constant name in it. You'd need a line of this for each of your
constants.

And, yes, that means adding a variable at in the class declarations
and also adding a line in the subroutine that initializes those
variables, so, yes, it's not as "efficient" as your solution.


Which was a small consideration on my part, not a big one.
See, my objection is that a constant value like "1:YourTextHere"
looks like a multi-value field in a data table, which violates
normalization. Obviously, constants are not fields in a data table,
of course, but the idea of mixing values and unpacking them bothers
me.


Well, it didn't bother me because it reads well enough, but see more
comments below.
Now we're back to duplication. You first define a list of
variables, then you create a list of initializations that must
match. The compiler will tell you if you try to initialize one
that's not there, but it won't tell you if you added a variable
and forgot to initialize it. There is no such problem using the
constants.


This is, indeed, a flaw of the class module-based solution.

Your solution has flaws, too, and the way you've proposed it,
solves only one problem and is not extensible.
>All told, I don't think saving typing one parameter is really
>worth all the work, to be honest, unless the function you wrap
>around the error raising command is going to do more than the
>plain functionality.

My respones are...
A. It's not about saving the typing, it's about preventing
errors in pairing the constants in various parts of the code.
Is it not a good thing to be able to enforce that A always goes
with AA, B with BB, etc.?

Then go all the way and replace it with a class module that takes
the error number and looks up the error message for you.


Same point as above. I thought about approaches using class
modules, but they all added complexity and duplication rather than
removing it. I feel the solution I came up with is the most
concise and safe one that I have been made aware of.


And I feel it's inelegant and problematic.
. . . B. In the evolution of a program, it would be
unusual if the function did not expand to do more, right?

You're the one who is always arguing that you shouldn't code for
requirements that do not yet exist, right?


Yes, but I wasn't the one who thought this code was not
appropriate to the task at hand. In my opinion, this code was to
remove duplication for a requirement that does exist. In most
cases where the argument could be made that the code written to
remove duplication looks excessive at the time, I would argue
that's OK, because it'll most likely get reused and extended, and
it'll be more likely to be reused because it now does exist (it's
hard to reuse code that doesn't yet exist <g>).


We'll just have to agree to disagree on this one.


As usual, you say your giving up with me just when you finally say
something that convinces me you have a point. When there are more than 2
arguments per constant (which was actually one of the next requirements),
then the delimited string constant stops being easy to read, and starts
becoming a maintenance hassle. My arguments with the table approach still
hold, but they become balanced by new problems with the clarity of the
string constant approach.

How about -this- idea. Use a table, and write code to generate VBA code
-from- the table. The generated code doesn't have to be maintainable
because it is not maintained in its code form, it is regenerated from the
table as desired. The code wouldn't have to look anything up -from- the
table at run-time because that data would have been copied into the code by
the generator, though some fields might be looked up to make the code more
flexible after deployment (such as foreign language support, etc. - could
be added later when needed with little hassle).

Whaddaya think?
Nov 12 '05 #15

P: n/a
rkc

"Steve Jorgensen" <no****@nospam.nospam> wrote in message
news:n1********************************@4ax.com...
How about -this- idea. Use a table, and write code to generate VBA code
-from- the table. The generated code doesn't have to be maintainable
because it is not maintained in its code form, it is regenerated from the
table as desired. The code wouldn't have to look anything up -from- the
table at run-time because that data would have been copied into the code by the generator, though some fields might be looked up to make the code more
flexible after deployment (such as foreign language support, etc. - could
be added later when needed with little hassle).


Yowza! Off the hook again.

Nov 12 '05 #16

P: n/a
no****@nospam.nospam (Steve Jorgensen) wrote in
<n1********************************@4ax.com>:
On Thu, 30 Oct 2003 20:56:38 GMT, dX********@bway.net.invalid
(David W. Fenton) wrote:
We'll just have to agree to disagree on this one.
As usual, you say your giving up with me just when you finally say
something that convinces me you have a point. . . .


Hmm. Are you sure it's not a Pavlovian response on your part, I
give up, you see the light? ;)
. . . When there are more
than 2 arguments per constant (which was actually one of the next
requirements), then the delimited string constant stops being easy
to read, and starts becoming a maintenance hassle. My arguments
with the table approach still hold, but they become balanced by
new problems with the clarity of the string constant approach.
Except that was part of my argument from the beginning, not a new
part.
How about -this- idea. Use a table, and write code to generate
VBA code -from- the table. The generated code doesn't have to be
maintainable because it is not maintained in its code form, it is
regenerated from the table as desired. The code wouldn't have to
look anything up -from- the table at run-time because that data
would have been copied into the code by the generator, though some
fields might be looked up to make the code more flexible after
deployment (such as foreign language support, etc. - could be
added later when needed with little hassle).

Whaddaya think?


Well, it sounds interesting. In terms of implementation you'd have
large chunk of code repeatedly re-implemented (the functions that
do the work) and all you'd be rewriting would be the declarations
and the property GETs. It seems to me that it would be better to
take the constants out of the class module and be rid of the
properties, so that you'd write a module with your error number
constants, and the class module for processing that would remain
the same. Indeed, it would no longer need to be a class module at
all, just a pair of functions, one to return the appropriate
attribute of the error record, the other to raise the error.

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

P: n/a
On Fri, 31 Oct 2003 19:33:14 GMT, dX********@bway.net.invalid (David W.
Fenton) wrote:
As usual, you say your giving up with me just when you finally say
something that convinces me you have a point. . . .


Hmm. Are you sure it's not a Pavlovian response on your part, I
give up, you see the light? ;)


I might have the same thought myself, but I was agreeing with you before I
got to that part of your reply.
. . . When there are more
than 2 arguments per constant (which was actually one of the next
requirements), then the delimited string constant stops being easy
to read, and starts becoming a maintenance hassle. My arguments
with the table approach still hold, but they become balanced by
new problems with the clarity of the string constant approach.


Except that was part of my argument from the beginning, not a new
part.


What I was getting was that you thought it was a problem for even 2
components. I did not agree until you got me to thinking about what
happens when it goes past 2.
How about -this- idea. Use a table, and write code to generate
VBA code -from- the table. The generated code doesn't have to be
maintainable because it is not maintained in its code form, it is
regenerated from the table as desired. The code wouldn't have to
look anything up -from- the table at run-time because that data
would have been copied into the code by the generator, though some
fields might be looked up to make the code more flexible after
deployment (such as foreign language support, etc. - could be
added later when needed with little hassle).

Whaddaya think?


Well, it sounds interesting. In terms of implementation you'd have
large chunk of code repeatedly re-implemented (the functions that
do the work) and all you'd be rewriting would be the declarations
and the property GETs. It seems to me that it would be better to
take the constants out of the class module and be rid of the
properties, so that you'd write a module with your error number
constants, and the class module for processing that would remain
the same. Indeed, it would no longer need to be a class module at
all, just a pair of functions, one to return the appropriate
attribute of the error record, the other to raise the error.


Well, I hadn't gotten far enough with the concept to figure out whether a
class module would be a good target ot not. I'll just try something, and
tweak it until is looks clean. I'll post back if I come up with something
interesting.

Nov 12 '05 #18

This discussion thread is closed

Replies have been disabled for this discussion.