argeedblu
Oct 9 2010, 10:30 AM
Do you use user defined types for function returns when you need to know two or more things that have happened in the function excution?" For example, if you are parsing some text in a function and you want to know
a) if "fubar" occurs, what are the next n characters in the string, and
b) what is the starting charcter position of the phrase "fubar " + n characters.
Glenn
BananaRepublic
Oct 9 2010, 10:42 AM
Yes, I've used UDTs as a return type for where I need more information that cannot be held in a single variable. I am of a mind that we want to localize the variables as much and the alternatives such as using global variables, even temporarily, is too risky and an invitation for hard-to-detect-and-fix bugs. Passing parameters and reading return values is most safe way, so for that reason, a UDT is also preferable so you know that the data you are reading is actually the result of what you sent to the function.
On the other hand, there has been times where I found UDT was not what I needed and the fact was that I needed to break my function up into two functions. So to borrow Glenn's scenario, if I find myself that only half of time, I do need to know a but don't care about b then UDT is possibly an overkill and b breaking the function up into two atomic functions, there's more control over what we need to obtain and it conforms with the idea that any function should only do one thing and do it well.
I hope that helps.
argeedblu
Oct 9 2010, 10:59 AM
Yes that definitely helps, Banana. What comes to mind as a result of your alternative scenario, then is the possibility of the "comprehensive" proc with a udt return type and two (or more) atomic procs to determine just a, or b, or c, etc. when just the single result is what you need.
Thanks very much,
Glenn
jleach
Oct 9 2010, 11:30 AM
Hi Glenn,
I'm not sure I fully understand the question (or what you need the information for), but anyway... I agree with Banana's comment, I find myself doing the same sometimes.
Sometimes though, I find it more practicle to make use of ByRef arguments passing to a procedure. For instance, some return types may not be able to return everything I want to know... this is typically a case where I want to perform an operation on a object and want an explicit return for "yes, it worked or no, it didn't", along with the modified object. Very similar in concept I supposed to many Win32 API calls.
CODE
Public Sub DoSomething()
Dim Something As Object
If ProcessSomething(Something) = False Then
'Error
End If
End Sub
Private Function ProcessSomething(ByRef Something) As Boolean
Something.Blah
Something.Blah(Blah) = Blah
If Not Error Then ProcessSomething = True
End Function
So the actual useful "return" of the function is through it's paramaters, but the function itself returns a boolean (or long) value describing how the process went.
Not sure if this helps at all, but seemed to be somewhat in the same line as what you were wondering about, I think.
argeedblu
Oct 9 2010, 11:40 AM
Yes, Jack, that helps, thanks.
Perhaps to explain my question a little further, it is actually how people handle explicit function returns and data types. Typically we have a variety of ways to do things, byRef arguments being an alternative to the UDT approach that I am asking about.
The reason for my question is that I am working on a code project that I will be posting to the code archive. At the back of my mind when I do this sort of work is an underlying concern that I not present bad code or convoluted ways of doing things. I want the code to do something useful but I also want it to be "good code" so that I am not setting bad examples to potential downloaders of the code.
Glenn
BananaRepublic
Oct 9 2010, 11:50 AM
Just wanted to share an alternative in case of answering the question of "Did the call succeed or not?" -
In C++, before there was the concept of exceptions, they used the idea of checking the return values and viewing the mutated object. However, in a complex process, it could come out something similar to this:
CODE
If DoSomething(MyObject) Then
If DoThis(MyObject) Then
If DoThatToo(MyObject) Then
'Do whatever
End If
End If
End If
then there's the other issue of where if we have a failed result, what state our object is in now? Is it still safe to use it or has it been hopelessly mutated and thus we need to either undo or restart the process? In C++ there is a means to write a signature that also promises not to mutate the object... We can do sorta same thing by using ByVal which technically allows the function to mutate only its own copy so that works well for primitive data types but I don't think it helps much when it's an object being passed by value because we're just copying a pointer to the object and thus would end up mutating the same object even though we have our own copy of pointer.
For this reason, I think that Err.Raise is the most appropriate way to handle success/failure so my procedures implicitly assumes that a call to a function must succeed and if it didn't, I'd have an error on my lap:
CODE
DoSomething(MyObject)
DoThis(MyObject)
DoThatToo(MyObject)
CODE
Public Function DoSomething(AObject As Object)
...
If SomethingWentWrong Then
Err.Raise vbObjectError, "DoSomething", "The object cannot be processed."
End If
...
Error_Handler:
Select Case Err.Number
Case Is >= vbObjectError
Err.Raise Err.Number, Err.Source, Err.Description
Case Else
MsgBox Err.Number & ", " & Err.Description
End Select
Resume Exit_Procedure
This does not help much with protecting against irreversibly mutating the object, but at least I don't have to test the return values - I will know if I've had a problem by getting an error and handling it so in my top-level procedure.
I hope this helps somewhat....
argeedblu
Oct 9 2010, 12:39 PM
Thanks Banana.
Glenn
pere_de_chipstick
Oct 9 2010, 05:41 PM
Hi Glenn
Not sure if this is what you are asking, but from my early Access days I found that the results variables can be passed to the function, amended, and then passed back to be read by the calling procedure e.g:
CODE
Public Function Testit()
Dim strAnsA As String, IntAnsB As Integer
fGetAns strAnsA, IntAnsB
MsgBox strAnsA & " " & IntAnsB
End Function
Public Function fGetAns(strAnsA As String, IntAnsB As Integer)
strAnsA = "The Answer is: "
IntAnsB = 10
End Function
FredWard
Oct 9 2010, 07:08 PM
UDT = User Defined Date Type. (Structure in 'C')
Not necessarily a recommendation as the following code is but one method of getting the job done.
However, there are advantages to using UDT's.
Even though udtPassed is passed ByRef, which it must be, function TestForString can not modify it as written.
Function TestForString only writes to its return FindInString UDT not to UDT udtPassed.
This in turn prevents over-writing the passed information.
A UDT has members and those members require the UDT name in order to 'get at them'.
This means it is safe to use reserved words or other procedure names as member names.
It also means that Intellisense works with members of UDT's and helps to reduce typos.
A UDT can also have its own member designated to return error information.
UDT's can have other UDT's as members meaning very complex UDT's can be both passed and returned.
UDT's can have arrays of parent UDT's meaning ReDim Preserve of the member array preserves all members of the parent UDT.
And so to a small demo:
CODE
Option Compare Database
Option Explicit
' Test case:
'a) if "fubar" occurs, what are the next n characters in the string, and
'b) what is the starting charcter position of the phrase "fubar " + n characters.
Public Type FindInString
ErrorNum As Long
FindIn As String
FindWhat As String
Compare As Long
Found As Boolean
Position As Long
NumChars As Long
ReturnStr As String
Truncated As Boolean
End Type
Sub TestIt()
Dim udtPassed As FindInString
With udtPassed
.FindIn = "Is fubar is in this string"
.FindWhat = "fubar "
.Compare = vbTextCompare
.NumChars = 100
End With
With TestForString(udtPassed)
If .ErrorNum Then
MsgBox "Function TestForString raised error: " & .ErrorNum
Else
MsgBox "String found: " & .Found & vbNewLine & _
"Position found: " & .Position & vbNewLine & _
"String returned: " & .ReturnStr & vbNewLine & _
"String truncated: " & .Truncated
End If
End With
End Sub
Public Function TestForString(ByRef udtArgs As FindInString) As FindInString
On Error Resume Next
With TestForString
.Position = InStr(1, udtArgs.FindIn, udtArgs.FindWhat, udtArgs.Compare)
If .Position > 0 Then
.Found = True
.ReturnStr = Mid(udtArgs.FindIn, .Position + Len(udtArgs.FindWhat), udtArgs.NumChars)
.Truncated = Len(.ReturnStr) < udtArgs.NumChars
End If
.ErrorNum = Err.Number
End With
Err.Clear
End Function
While in most cases passing and returning UDT's may be overkill they do have their advantages.
Personal opinion but I think UDT's are underutilized because people do not take the opportunity to play with them.
So, if the aim of the code archive submission is to educate why not push the envelope a bit even if the method employed is somewhat convoluted?
HTH
Fred.
argeedblu
Oct 10 2010, 04:34 AM
Thanks Bernie,
For whatever reason and certainly at least in part because most of my programming knowledge (and all of my VBA knowlege) has come from self-teaching, I have never been comfortable with VBA's defaulting to by Reference for procedure arguments. Way back when (and I think it was C I was trying to learn at the time) I recall reading something that I understood to indicate that the was not a good practice. So I have never been comfortable with using the by reference feature of VBA.
The particular case I am working with at the moment involves parsing some text. I want to know two things, the actual text located and its postion in the text parsed. The code is working quite well with a UDT but, as I think I mentioned earlier in this thread, I am concerned that I wouldn't be be passing along bad habits if I were to publish the utility I am writing for others to view and play with.
Thanks very much for your response.
Glenn
argeedblu
Oct 10 2010, 05:17 AM
Thanks Fred,
QUOTE
Even though udtPassed is passed ByRef, which it must be, function TestForString can not modify it as written.
Function TestForString only writes to its return FindInString UDT not to UDT udtPassed.
This in turn prevents over-writing the passed information.
Perhaps I am mis-understanding you here but I have found that I can declare I can refer to the udt just fine if I declare a function to be of the type UDT and assign the function result to a variable of the same type (Access 2010).
CODE
Private Type ParseBlockResult 'results of parsing a block of lines
strText As String 'text recovered
lngLastLine As Long 'final line processed
End Type
Public Function testudt()
Dim xy As ParseBlockResult
xy = fParseProcHeader("")
Debug.Print xy.lngLastLine
Debug.Print xy.strText
End Function
Public Function fParseProcHeader(rstrstring As String) As ParseBlockResult
' Procedure: fParseProcHeader
' DateTime: 09/10/2010 11:18:52 AM
' Author: Glenn Lloyd
' Description: parse a procedure header
'--
Const cstrProcedure = "fParseProcHeader"
On Error GoTo HandleError
fParseProcHeader.lngLastLine = 300
fParseProcHeader.strText = "zzz"
HandleExit:
Exit Function
HandleError:
ErrorHandle Err, Erl(), cstrModule & "." & cstrProcedure
Resume HandleExit
End Function
So, "must" is not quite accurate but is the reference method preferable.
Glenn
FredWard
Oct 10 2010, 06:35 AM
Glenn.
Your code sample does not pass a UDT it returns one.
If you did pass a UDT it would need to be passed ByRef.
Fred.
argeedblu
Oct 10 2010, 07:28 AM
Fred,
Yes you are correct of course. The context I had in mind in my original question (in mind, but not explicitely stated) as using a UDT as the return type in a function declaration. I can see some advantage in passing it by refererence so I will take a further look at my code.
Glenn
FredWard
Oct 10 2010, 08:06 AM
Glenn.
Provided you want it to work there is no advantage in passing a UDT by reference.
It simply can not be passed any other way and therefore there is nothing else over which to take advantage.
Try to force the passing of a UDT ByVal, it simply does not work.
Fred.
argeedblu
Oct 10 2010, 08:24 AM
Fred,
Thanks. Actually I was thinking in terms of advantage of passing a UDT vs. declaring a UDT as a function return type not passing by ref vs passing by value which you have said doesn't work.
Glenn
pere_de_chipstick
Oct 10 2010, 08:25 AM
Hi Glenn
Like you most of my programming knowledge (and all my VBA!) has come from self teaching, so I was interested in your comment that you weren't happy about using the default byRef procedure argument.
When I first 'dicovered' the technique I was not aware of 'byRef' as a procedure argument! and remained in blissful ignorance of it until the advent of the Ribbon callback functions where the byRef procedure argument was essential to the way it works!
So, if Microsoft use it as an essential method of passing arguments can it be seen as bad practice?

My point here is that there should be some valid reason for the method not to be used, not just that 'it is considered bad practice', and as the method allows you to pass multiple results back to the calling routine without needing to create UDT's - why not use it?
argeedblu
Oct 10 2010, 08:31 AM
QUOTE
My point here is that there should be some valid reason for the method not to be used, not just that 'it is considered bad practice', and as the method allows you to pass multiple results back to the calling routine without needing to create UDT's - why not use it?
Good point Bernie. It seems to be also a convenient way of passing multiple related arguments to the function as well. I don't think using a UDT in this way is a panacea but in some contexts it might be useful to assign values to some members of the UDT, pass it to the function, and have the function fill in other values based on the input that was passed.
Glenn
pere_de_chipstick
Oct 10 2010, 08:37 AM
Hi Glenn
Not sure if I may have misunderstood (I'm learning here too!)
>>... it might be useful to assign values to some members of the UDT, pass it to the function, and have the function fill in other values ..<<
Surely you can still do this by passing values in the byReg argument without needing UDTs?
BananaRepublic
Oct 10 2010, 10:09 AM
Just few random thought WRT UDT vs multiple ByRef parameters in no particular order:
1) Using UDT may obfuscate what is optional and what is not. We can only see a list of all memebers that UDT has but we don't know whether any of member are optional or not. One biggest annoyance I have with any Win32 API calls is that there would be parameters which are required but expected only in a certain format such as null pointer to fill in a "lpReserved" parameter. I'd be equally annoyed if I had to do something similar with a function using UDT but expecting some certain things.
2) Passing a set of variables into a function always does not make it clear that there is a common structure, especially if we have more than one calling code and those code call the same function differently. This can be mitigated somehow by always return the same UDT as a return type, but it's not as clean than simply passing UDT as a parameter and getting it back, IMHO.
3) If the set of variables is to correspond to a UDT, why not save all the extra typing and cut down on code maintenance by using a UDT?
4) We certainly could just pass UDT's member into the functions and if so desired, mutate it or not.
CODE
Public Function TryThis(ByRef InputString As String) As String
TryThis = "Hello, " & InputString
End Function
Public Sub TestIt()
Dim it As MyStruct
it.ThirdMember = "World"
it.ThirdMember = TryThis(it.ThirdMember)
Debug.Print it.ThirdMember
End Sub
Regarding the question of passing a UDT by value, this may work. Whether it's worth it is another question, though. At least we can promise that the original UDT will not be mutated by a call into that function.
CODE
Public Type MyStruct
FirstMember As Long
SecondMember As Long
ThirdMember As String
FourthMember As String
End Type
Public Function DoIt( _
ByVal FirstMember As Long, _
ByVal SecondMember As Long, _
ByVal ThirdMember As String, _
Optional ByVal FourthMember As String = "Myself" _
) As MyStruct
DoIt.FirstMember = FirstMember + 4
DoIt.SecondMember = SecondMember * 5
DoIt.ThirdMember = ThirdMember & ", World"
DoIt.FourthMember = "Goodbye, " & FourthMember
End Function
Public Sub TryIt()
Dim val As MyStruct
Dim ret As MyStruct
val.FirstMember = 1
val.SecondMember = 2
val.ThirdMember = "Hello"
val.FourthMember = "World"
ret = DoIt(val.FirstMember, val.SecondMember, val.ThirdMember, val.FourthMember)
Debug.Print val.FirstMember
Debug.Print ret.FirstMember
Debug.Print val.SecondMember
Debug.Print ret.SecondMember
Debug.Print val.ThirdMember
Debug.Print ret.ThirdMember
Debug.Print val.FourthMember
Debug.Print ret.FourthMember
End Sub
By having the function accepting multiple parameters, we now can document which parameters are in fact optional and will be supplied by the function anyway so that brings us closer to best of both world, albeit at expense of extra writing. Imagine having numbers of those function and suddenly we need to add a new member to the UDT. Argh.
It's still pretty much a toss-up for me, though I do admit that I've never used multiple ByRef parameters to deliberately mutate the variables. Come to think of it, I've implicitly assumed that functions shouldn't mutate those variables passed as a parameter.
jleach
Oct 10 2010, 11:30 AM
One of the other major advantages to a UDT is that it can quite easily be tossed from procedure to procedure. With a UDT you can use it anywhere really, without needing to know anything about special requirements for procedure parameters, such as when using ByRef.
For example, one UDT that I employ is a mechanical dimension type. The 'core' piece of data is the dimension as it appears on a blueprint, but along with that is a tolernace, an nominal value, and upper and lower bounds. All of these are stored in a UDT, which may be used in numerous points throughout the app. Now, if I were to try to do this using ByRef arguments, obviously that would be a disaster.
On the other hand, one could write a structure to return two things: a value and a return value. Send the UDT to a function, set the "value", and set the return value as a Long code for the "process notes" of the function performance. In this case, a UDT is not at all desirable, and a ByRef argument for the "operations" and a Long return value of the function itself for the Status makes much more sense.
Another advantage to a UDT is flexibility in the data within it. Take the MechDim UDT again, for instance... a dimension on a blueprint may be called out in various ways... as an Upper/Lower value, as a Nominal Value with a specified tolerance deviation, others...
Now with a UDT I can enter two pieces of data to it and send it to a function that calculates and sets the other values (upper and lower if they were not explicitly specified, or Nominal if only Upper and Lower were specified, etc etc). So at any point I can have a full range of usable values contained in a single type. This usage of them I suppose may be likened to using a query with calculated fields.
So, in some cases a ByRef is preferred, but sometimes a UDT just screams to be used and there's not really any better way to do it, such as I found to be the case with my implementation of mechanical dimensions and tolerances. (it might be noted here that a UDT can sometimes be considered the little brother of a Class... somewhere in between variable and a Class object. I think that in any case a UDT can be defined as a Class and gain more functionality from them, losing nothing. What it boils down to in the case is whether the structure is worth taking the time to define in a class or a simple UDT - make pretty colors or leave it grey, persay)
Cheers,
argeedblu
Oct 10 2010, 12:42 PM
Hi Bernie,
I am thinking of a specific situation where one is parsing a text file looking for certain values. You want to pass the file name, value being saught, and starting position in the file. You want back the number of lines parsed, whether the criteria value was found, and the location where it was found.
With a standard function return, you can only get one of the last three as a return value to assign or use. You could use the reference arguments to get the rest.
One alternative would be to declare a udt variable and declare the function to be the same type, assign the results in the function call and assign the return udt to the original udt variable.
The second alternative, which is what I had in mind is to created a udt variable and have a single argument (of udt type) in the function declaration. Internally, the function would work the same as the first alternative above but the function does not have to have an explicitely declared return type. This alternative risk a bit of obfuscation but everything you want to suppy and receive from the function is neatly packaged in the udt variable.
Someone can correct me if I am wrong but I think the second alternative is a way to access internal results of sub calls, as well.
Just before I started writing this reply, I notice that Banana has posted a thoughtful pro-con discussion of udts.
Glenn
argeedblu
Oct 10 2010, 12:44 PM
QUOTE (BananaRepublic @ Oct 10 2010, 11:09 AM)

Thank you for those thoughts, Banana. As always I appreciate your thoughtful discussion of the pros and cons.
Glenn
argeedblu
Oct 10 2010, 12:46 PM
QUOTE (jleach @ Oct 10 2010, 12:30 PM)

Thank you Jack.
Glenn
FredWard
Oct 10 2010, 01:49 PM
Glenn.
I think the only thing required is a decision on what is required.
If you wish to modify the data in the passed UDT then assign the values to it, and it does not need to be a Function.
If you wish to protect the data in the passed UDT then assign the values to the Function return UDT, this is what I did in post #9.
Fred.
argeedblu
Oct 10 2010, 03:08 PM
QUOTE (FredWard @ Oct 10 2010, 02:49 PM)

Glenn.
I think the only thing required is a decision on what is required.
If you wish to modify the data in the passed UDT then assign the values to it, and it does not need to be a Function.
If you wish to protect the data in the passed UDT then assign the values to the Function return UDT, this is what I did in post #9.
Fred.
Thanks Fred
bulsatar
Oct 10 2010, 03:34 PM
Thanks for this discussion on UDT's guys! I am just starting to try my hand at more advanced coding and wrapping my mind around UDT's and this was very helpful for forwarding my understanding.
Thanks
WildBird
Oct 10 2010, 08:19 PM
Just my 5 cents on the subject, I dont think I have ever used UDTs in VBA. I just use functions, I guess havent had to return more than one value, or I do it some other way. As for ByVal and ByRef, I default to ByVal, and only use ByRef when I am passing objects such as Excel sheets that I want to update.
CyberCow
Oct 10 2010, 09:58 PM
OK, I guess this is what I get for walking in the woods with my Sweety and our pups so often . . . . I'm attending one those threads that is a bit over my head, but am able to grasp vaguely. However, the slight grasp has certainly peaked my interest and I am going to read all the above again & again and conduct some trials and errors of my own to better understand what this ByRef, ByVal and UDT is all about.
And ditto what bulsatar said too!
These types of threads are the best seat in the Access house ! ! !
FredWard
Oct 11 2010, 02:07 AM
If you think it might be of some value, one of the ways to learn about the User Defined Data Type would be to ask a generic question in, say, the Access Modules forum.
That way we could ask and answer questions in one place without running the risk of high jacking this particular thread.
It might also help to concentrate information in one place so that a search can be done on the subject.
Perhaps as a starting Title…
What is a User Defined Data Type (UDT) and how can it be used?
Or any other Title you think more general and appropriate for a kick off.
Sub questions might include…
How do we define a UDT?
What data types can the members be?
What is the scope of a UDT?
How are they passed and returned to/from procedures?
Can they include other UDT’s.
Can we make an array of UDT’s?
Does ReDim Preserve preserve all members of a UDT?
Can UDT’s include methods?
Etcetera
Etcetera
Etcetera
Later, after the dust settles, it could be picked to pieces and placed in the Wiki section.
Fred.
argeedblu
Oct 11 2010, 03:29 AM
QUOTE
If you think it might be of some value, one of the ways to learn about the User Defined Data Type would be to ask a generic question in, say, the Access Modules forum.
That way we could ask and answer questions in one place without running the risk of high jacking this particular thread.
It might also help to concentrate information in one place so that a search can be done on the subject.
Hmmm. Odd suggestion given that this thread started with a generic question about User Defined Types in the Modules forum and people have been responding with what they know/would like to know about UDTs. Since I started the thread, I am not concerned about hijacking. It often helps people to learn when they see the topic in the context of a practical application
Of course anyone is free to start another more comprehensive theoretical thread such as you have suggested, Fred.
Glenn
argeedblu
Oct 11 2010, 03:42 AM
QUOTE (BananaRepublic @ Oct 10 2010, 11:09 AM)

Regarding the question of passing a UDT by value, this may work. Whether it's worth it is another question, though. At least we can promise that the original UDT will not be mutated by a call into that function.
Hi Banana,
UDTs have to be passed ByRef, as Fred mentioned a couple of times. Trying to use ByVal causes a compile error.
Glenn
jleach
Oct 11 2010, 03:44 AM
I had suggested to bulsatar that the
GetDirContents function in the Function Library is a good example of how a UDT would be used - often I find when trying to learn something new the more difficult part is knowing how it would be applied to know if I am trying to work with it correctly. GetDirContents is a real-world usable example (loading various file/directory properties and lists into a single return type).
FredWard
Oct 11 2010, 03:51 AM
In reply to post# 30
I hope not such an odd suggestion given that the title of this current thread does not mention UDT’s.
I was looking for a more generic thread/tile that may be searched on and help for others.
Also, questions not asked in this thread may go unanswered such as; can UDT’s contain methods?
Answer? both yes and no: -
CODE
Option Compare Database
Option Explicit
Public Type Procedure
Call As String
End Type
Public Type Wheel
Radius As Single
Area As Procedure
End Type
Sub TestIt_1() ' << Start here for testing.
Dim udtMyWheel As Wheel
With udtMyWheel
.Radius = 1.33333
.Area.Call = "RadiusToArea"
End With
MsgBox Eval(MakeCall(udtMyWheel.Area.Call, udtMyWheel.Radius))
End Sub
Function RadiusToArea(ByVal Radius As Single) As Single
RadiusToArea = 3.14159 * Radius * Radius
End Function
Public Function MakeCall(ByVal strFunctionName As String, _
ParamArray vntArgs() As Variant) As String
Dim lngElement As Long
Dim strFunction As String
' Add Function name and opening bracket
strFunction = strFunctionName & "("
' Loop through arguments
For lngElement = LBound(vntArgs) To UBound(vntArgs)
strFunction = strFunction & Chr$(34) & vntArgs(lngElement) & Chr$(34) & ","
Next lngElement
' Trim off trailing ","
If Right$(strFunction, 1) = "," Then
strFunction = Left$(strFunction, Len(strFunction) - 1)
End If
' Set return valve and closing bracket
MakeCall = strFunction & ")"
End Function
So a more generic thread may produce more generic results.
Edit(And no, I didn’t write the above in the last 20 minutes

)
Fred.
jleach
Oct 11 2010, 03:56 AM
Thats'a n inteteresting implementation of a Type. At that point I would think why just go with a Class? But that does bring up something previously unmentioned... nexted UDTs (having a Type which has one or more values that are other Types)
CODE
'Mechanical Dimension Tolerances (Tolerance UMs are inherited by typMechDim)
Public Type typMechDimTol
tlType As eMechTolTypes 'tolerance type, Range, PM, GTD
tdUpper As Double 'upper bound or Plus value
tdLower As Double 'lower bound or Minus value
tsGTD As String 'GTD string
End Type
'Mechanical Dimensions
Public Type typMechDim
tdDim As Double 'callout dimension
tdUM As Long 'UM (see UM handling)
ttTol As typMechDimTol 'callout tolerance
tdNomDim As Double 'calculated, nominal dimension
ttNomTol As typMechDimTol 'calculated, nominal tolerance
End Type
'Mechanical Tolerance Types
Public Enum eMechTolTypes
dsTolTypeRandom = -1 'Random
dsTolTypeDeviation = 0 'Plus/Minus
dsTolTypeRange = 1 'Max/Min
dsTolTypeGTD = 2 'GTD
End Enum
'============================================================================
==
' NAME: SetNominalMechTols
' AUTHOR: Jack D. Leach
' PURPOSE: set the nominal dimension and tolerance of a mechanical dimension
'
' tolerances are not rounded, but are precise to a double datatype. Rouding
' is recommended at Down for the High and Up for the Low, but will be up to
' the caller to modify as required, as this function has no vision on the
' required precision level to be rounded to
'
' returned values are Double datatype, conversions within the function are
' performed using Decimal to avoid float errors
'
'
' REVISIONS:
' REV | DATE | REV TYPE | DESCRIPTION
'------------------------------------------------------------------------------
' R01 8/21/2010 INITIAL
'============================================================================
==
'ErrHandler V3.01
Public Function SetNominalMechTols(ByRef t As typMechDim)
On Error GoTo Error_Proc
'=========================
Dim dH As Variant 'high
Dim dL As Variant 'low
'=========================
If (t.ttTol.tlType = dsTolTypeGTD) Or _
(t.ttTol.tlType = dsTolTypeRandom) Then
'these two types aren't suitable for nominal dimensioning
GoTo Exit_Proc
End If
If t.ttTol.tlType = dsTolTypeRange Then
'calculate a nominal value of a range
dH = t.ttTol.tdUpper
dL = t.ttTol.tdLower
t.tdNomDim = CDec(((CDec(dH) - CDec(dL)) / 2) + CDec(dL))
t.ttNomTol.tlType = dsTolTypeDeviation
t.ttNomTol.tdUpper = CDec(t.ttTol.tdUpper) - CDec(t.tdNomDim)
t.ttNomTol.tdLower = CDec(t.ttTol.tdLower) - CDec(t.tdNomDim)
GoTo Exit_Proc
End If
If t.ttTol.tlType = dsTolTypeDeviation Then
'calculate a nominal value using a devition tolerance
dH = CDec(t.tdDim) + CDec(t.ttTol.tdUpper)
dL = CDec(t.tdDim) + CDec(t.ttTol.tdLower)
t.tdNomDim = CDec((((CDec(dH) - CDec(dL)) / 2) + CDec(dL)))
t.ttNomTol.tlType = dsTolTypeDeviation
t.ttNomTol.tdUpper = CDec(dH) - CDec(t.tdNomDim)
t.ttNomTol.tdLower = CDec(dL) - CDec(t.tdNomDim)
GoTo Exit_Proc
End If
'=========================
Exit_Proc:
Exit Function
Error_Proc:
Select Case Err.Number
Case Else
MsgBox "Error: " & Trim(str(Err.Number)) & vbCrLf & _
"Desc: " & Err.Description & vbCrLf & vbCrLf & _
"Module: modMechDimAndTol, Procedure: SetNominalMechTols" _
, vbCritical, "Error!"
End Select
Resume Exit_Proc
Resume
End Function
Fun stuff!
FredWard
Oct 11 2010, 04:01 AM
Not exactly unmentioned, Jack. In post# 29 Can they include other UDT’s?
Fred
jleach
Oct 11 2010, 04:06 AM
Another "practice" question might be:
At what point do we decide that a structure is getting too complex and would be better handled as a Class? IMO, the last two examples give are very close to that.
FredWard
Oct 11 2010, 04:09 AM
That, Sir, is the question.
Fred
BananaRepublic
Oct 11 2010, 06:45 AM
QUOTE (argeedblu @ Oct 11 2010, 02:42 AM)

Hi Banana,
UDTs have to be passed ByRef, as Fred mentioned a couple of times. Trying to use ByVal causes a compile error.
Glenn
I apologize if I was not clear - my earlier post was meant to be a demonstration of working around the limitation arising out from inability of passing UDT ByVal by re-enumerating the members as parameters for a given function to give it an appearance of having passed it ByVal. Whether it's worth the efforts is entirely another question, unfortunately. A while ago, I was studying C++ and the ability to declare a parameter as const like this:
CODE
void myFunction(myStruct const &myInput) {
...
}
Essentially this enables us to pass a myInput ByRef (meaning there will be no copying and thus it is going be a bit faster than if we had to copy, especially for a big object) but also with assurance that the function myFunction will not mutate the object and any attempt to mutate within the function body will cause a compiler error. If we did not include the const keyword, compiler would not issue an error whenever a function mutate a member of the myStruct which could result in a program crash.
Back to VBA, as I mentioned, it dawned on me that when I write VBA, I've been implicitly assuming that even though we are passing parameters ByRef by default, I shouldn't be mutating them; only time I can mutate it is when I'm returning it. For example, both could be said to have equivalent effect:
CODE
myFunction1 myVariable
myVariable = myFunction2(myVariable)
where we define the functions as:
CODE
Public Sub myFunction1(ByRef myInput As String)
myInput = "Hello, " & myInput
End Sub
Public Function myFunction2(ByRef myInput As String) As String
myFunction2 = "Hello, " & myInput
End Function
But I'd wager that if we were to show those two versions to most VBA programmers, a majority would choose the 2nd format as one that they're familiar and they may be a bit horrified at the first version because there is no clear indication that "myFunction1" will in fact mutate "myVariable". In fact, it's even a Sub which is supposed to return nothing! I realize we got into this subject arising out from the suggestion of returning several return values by using parameter list as well as a return value and the above example only used one return value but I think the opacity is still there unfortunately. If we were to look at anyone's VBA code, we would see many opportunities where one could mutate the parameters but they will never do it.
I hope this helps.
jleach
Oct 11 2010, 06:55 AM
QUOTE
If we were to look at anyone's VBA code, we would see many opportunities where one could mutate the parameters but they will never do it.
For quite some time after I started writing VBA, I never knew what "byref" did... never knew that if I performed some operation on an argument from inside the procedure, it would reflect in the calling procedure. And, given that ByRef is the default for parameters, everything I did for a few years in VBA was passed as ByRef.
Scary to think that this slightly more advanced topic might have catastrophic results for the novice VBA programmer. When I first learned of what ByRef actually does, I was quite concerned to say the least about all the code I had written. I got just plain dumb lucky that I never relied on a variable after passing it to a seperate procedure where it might have been mutated.
I still to this day wonder what bugs may be lurking because of this (I never went back through everything I wrote in 2 years time to verify I was ok...).
I wonder, why would the default not be ByVal?
BananaRepublic
Oct 11 2010, 07:09 AM
To be honest, I was bamboozled when I found out that C# defaults to ByVal, not ByRef. I understood we defaulted to ByRef because it was cheaper than ByVal. If we call a variable ByVal, we are forcing the compiler to make a copy of variable behind the scene for us and that takes few additional cycles. Another reason for passing ByRef, especially for objects, is that there is no real difference when you pass a object ByVal or ByRef. This is off my memory so I may be wrong but here it goes:
Objects internally are just a pointer to a block of memory that contains data pertaining to the object itself. We do not really interact with pointers itself - when we pass a variable of DAO.Recordset, we don't have to know it's a pointer or care it was one since we know that we are passing a same DAO.Recordset from one procedure to another. Let's consider what it means to pass a pointer byref and byval.
ByRef pointer mean we are giving exactly same address to the pointer itself so the procedure can reference the same pointer and thus access the same object.
What about ByVal? Well, we are now creating a brand new *pointer* that points to the same object. We are not actually copying the object itself; only the pointer, and since no object-copying was done, passing ByVal does not actually promise that we won't mutate the original object (we will).
In either cases, we can mutate the same object, regardless of whether we pass a object ByRef or ByVal. Since VBA is primarily object-based, if not fully object-oriented, it makes sense to go with less confusing structure and that was ByRef.
jleach
Oct 11 2010, 07:23 AM
Both ByRef and ByVal are pointers to data. VBA somehow differentiates a variable's type, and for a non-complex datatype, ByRef sends the pointer to the memory block which contains the variable's value, ByVal sends a pointer to a different memory block that contains an exact copy of the original memory block?
BananaRepublic
Oct 11 2010, 07:58 AM
No, there is no distinction really. The trouble is in what we are copying. When we use a ByVal on say, a string, we are copying a memory block containing say, "Hello, World" so there's now two memory blocks that says "Hello, World". When we use a ByVal on an object (aka pointer), we are copying the memory address that the pointer is referencing to another memory block, so we end up having two memory block containing same memory address for our object.
Did that make sense?
argeedblu
Oct 11 2010, 08:00 AM
I'll throw this out for consideration but I have no idea of how significant the difference might prove in reality.
I once made a naive statement in a newsgroup that, in VBA, a sub and and function without a declared return type are the same. Someone in the newsgroup pointed out that there is a difference in that the function without a declared return type is type as a variant whereas a sub is not typed. Presumably there is a little bit of overhead involved in the function form.
So is there perhaps some saving of overhead in declaring a sub:
public sub mysub(xxx as udt)
vs
public function myfunc(xxx as udt)
public function myfunc(xxx as udt) as udt?
Of course there is a practcial problem witth last example because if you use assignments to call the function, you are actually using multiple calls to the same function with the sam argument:
strtxt1 = myfunc(xudt).text
intnum1 = myfunc(xudt).number
would involve two calls to myfunc.
Now, if using the sub version is more efficient, I think it would be good practice to include the ByRef qualifier even though VBA defaults to ByRef.
public sub mysub(ByRef xxx as udt) to make it clear that the sub will mutate the udt parameter. public sub mysub(xxx as udt)
If you don't intend for the sub (or function) to mutate the parameter then you might leave out the qualifier.
Glenn
jleach
Oct 11 2010, 08:10 AM
Quoting BR:
QUOTE
Did that make sense?
Not quite. You are implying that an object is a pointer, but a standard datatype is NOT a pointer to memory containing it's value? Apparently that's where I'm confused. I suppose it makes sense when put like that, but I for some reason have been of the impression that ALL parameters were simply pointers, where in fact this is only the case for Objects? If this is correct, then yes, it would make sense.
Quoting Glenn:
QUOTE
If you don't intend for the sub (or function) to mutate the parameter then you might leave out the qualifier.
I have adopted this practice and it works out well for me. If I see ByRef it throws a red flag to may saying "be careful, we know this will be mutated".
The Subs vs. Functions w/o return is a good debate as well (seeing that this thread is already far gone from the original question). I've seen people that always write functions, never subs, and I've seen people that only write functions when they return a value and subs otherwise (unless the Sub would be called from the Expression Service, in which case it needs to be Function). My take on this is, when you call a sub, you say "do something". When you call a function, you say "Do something and let me know how it went". My argument: how often do we want something done without caring to know whether it was done the way we wanted?
BananaRepublic
Oct 11 2010, 08:25 AM
QUOTE (argeedblu @ Oct 11 2010, 08:00 AM)

I once made a naive statement in a newsgroup that, in VBA, a sub and and function without a declared return type are the same. Someone in the newsgroup pointed out that there is a difference in that the function without a declared return type is type as a variant whereas a sub is not typed. Presumably there is a little bit of overhead involved in the function form.
I kind of wondered how we ended up with Subs, actually. In other languages, everything are functions, essentially. It's just that when we want to return nothing, we write function's return data type as void as thus:
CODE
void myFunction() {
...
}
and VB/VBA's Sub is exactly just that. VB/VBA's Function absent a return type is quite an oddball as you correctly points out that it returns a variant anyway which I think is really bad and thus become necessary & good practice for any programmer to always specify a return type, even if Variant is in fact the appropriate data type.
QUOTE
So is there perhaps some saving of overhead in declaring a sub:
In the same that we don't have to allocate another memory block to return something, yes. I guess that's probably why lots of C++ functions like to mutate the parameters as opposed to returning a value and assigning to a variable. I can't say this is exactly clean coding but well, there we go.
QUOTE
Of course there is a practcial problem witth last example because if you use assignments to call the function, you are actually using multiple calls to the same function with the sam argument:
Just to quickly point that this can be rectified by either using a With..End With or a variable that itself is also a UDT. Either way, should call the function only once.
QUOTE
I think it would be good practice to include the ByRef qualifier even though VBA defaults to ByRef.
That's exceptionally interesting thought, Glenn. I've seen few VBA code that does that and I recall it did communicate that ByRef was required in order for API calls to work correctly and I came to expect that my parameters passed into this would get mutated. So I guess it is not without precedence after all.
BananaRepublic
Oct 11 2010, 08:30 AM
QUOTE (jleach @ Oct 11 2010, 08:10 AM)

Not quite. You are implying that an object is a pointer, but a standard datatype is NOT a pointer to memory containing it's value? Apparently that's where I'm confused. I suppose it makes sense when put like that, but I for some reason have been of the impression that ALL parameters were simply pointers, where in fact this is only the case for Objects? If this is correct, then yes, it would make sense.
Standard datatypes such as strings and integers are treated as "not pointer" by the compiler, IINM. We need to double check this assertion but I recall that the compiler does some magic in such that there is a distinction between base datatypes and objects when we pass either by value or by references.
QUOTE
The Subs vs. Functions w/o return is a good debate as well (seeing that this thread is already far gone from the original question). I've seen people that always write functions, never subs, and I've seen people that only write functions when they return a value and subs otherwise (unless the Sub would be called from the Expression Service, in which case it needs to be Function). My take on this is, when you call a sub, you say "do something". When you call a function, you say "Do something and let me know how it went". My argument: how often do we want something done without caring to know whether it was done the way we wanted?

Indeed. A while ago, I was wrestling with what was the cleanest way to throw an problem which I did not expect to happen in normal operations. I hated the idea of checking the return value to see if a call was successful or not - do it enough and it get tedious *really* fast. Then it hit me - that's the whole point of having error handling. So lately, if I have something that does crap out, I just raise an error and throw it back to the calling code.
jleach
Oct 11 2010, 09:07 AM
QUOTE (BananaRepublic @ Oct 11 2010, 09:30 AM)

A while ago, I was wrestling with what was the cleanest way to throw an problem which I did not expect to happen in normal operations. I hated the idea of checking the return value to see if a call was successful or not - do it enough and it get tedious *really* fast. Then it hit me - that's the whole point of having error handling. So lately, if I have something that does crap out, I just raise an error and throw it back to the calling code.
That's a good point. Error propagation is a great practice that I've come to use a lot. But I still prefer functions (it helps keep the calling conventions consistent, and you don't have to remember which "subs" are "functions" just because they're being called through ES).
bulsatar
Oct 11 2010, 10:20 AM
Hopping to the Sub vs Function, for me personally, I use a sub when a procedure outputs to the UI directly or when manipulating controls. I use functions when I actually need to use the return further in the code. So I guess, subs when done with code and functions when the journey goes further.
And thanks for that explination of Void use Bananna! That has confused the [censored] out of me when looking at other languages!
I think after all of this, I am going to spend some time with the links that Jack sent me on the UDT's and start exploring how I can incorporate them into my coding. I use a lot of functions just to bounce data round for manipulation and this might save me a lot of typing
BananaRepublic
Oct 11 2010, 10:36 AM
QUOTE (jleach @ Oct 11 2010, 09:07 AM)

But I still prefer functions (it helps keep the calling conventions consistent, and you don't have to remember which "subs" are "functions" just because they're being called through ES).
Pardon me if I'm being dense but how is that important? I thought, off the hand, there is no functional difference in how we would call a function and a sub, except of course for ES's silly requirement that it return something but within VBA, I don't have to care if I'm calling a sub or a function?
For example, it's valid code to do this:
CODE
Public Sub DoIt()
CurrentDb
End Sub
where CurrentDb is technically a function that returns a DAO.Database object but this is valid way to achieve a side effect (e.g. refreshing all collections as an example). But maybe I've missed a different case and would love to know about it.
argeedblu
Oct 11 2010, 10:44 AM
Sorry folks, I have no idea what ES means. Can somebody enlighten me?
Thanks,
Glenn
This is a "lo-fi" version of our main content. To view the full version with more information, formatting and images, please
click here.