Archived

This topic is now archived and is closed to further replies.

Shannon Barber

Collection of a UDT in VB6

Recommended Posts

I say:
  
''Module ReportInfo
Public Type CostInfoChunk
    Level As Integer
    BaseID As String
    LotID As String
    SubID As String
End Type

Public Type CostInfo
    Info() As CostInfoChunk
    MaterialCost As Currency
End Type

''WorkOrder class
Dim m_MaterialCostCollection as Collection
''...
Dim CI as ReportInfo.CostInfo

CI.Info(1..n) = ''stuff
CI.MaterialCost = SumMaterialCost(CI)
m_MaterialCostCollection.Add(CI)
  
And VB says:
quote:
Compile error: Only user-defined types defined in public object modules can be coerced to or from a variant or passed to late-bound functions
At the .Add <scratches head> ummm, it IS a user-defined type in a public module!!!! so I says fine:
  
''Class CostInfo
Public Info() As ReportInfo.CostInfoChunk
Public MaterialCost As Currency
  
As VB says:
quote:
Constant, fixed-length strings, arrays, user-defined types and Declare statements not allowed as public members of object modules
Right, it''d be way too danergous to let a VB program to that... Fine, I''ll give in to the dark-side
  
Dim m_Info() As ReportInfo.CostInfoChunk
Dim m_MaterialCost As Currency

Property Set Info(i As Integer, chunk As ReportInfo.CostInfoChunk)
    Info(i) = chunk
End Property
Property Get Info(i As Integer) As ReportInfo.CostInfoChunk
    Info = m_Info(i)
End Property
  
quoteth VB:
quote:
Compile Error: Only public user defined typed define in public modules can be used as parameters or return types for public procedure of class modules or as fields of public user defined types
(at the Property Set) AAAAAAAARRRRRRRRRRRRRGGGGGGGGGGGGGHHHHHHHHHHHHHHHHHHH Please, someone tell me how stupid I am, and give me a clue how to make a collection of a UDT? (VB6 SP5) Magmai Kai Holmlor "Oh, like you''ve never written buggy code" - Lee "What I see is a system that _could do anything - but currently does nothing !" - Anonymous CEO

Share this post


Link to post
Share on other sites
The only thing that is stupid is VB ...

I have hit this problem in the past and according to some good friends who code in VB all the time, you just can''t do it. There is no good reason, just a microsoftism.

I hate to say it, but any UDT''s that you require collections of must be made into classes... well the VB definition of a class anyhow.

Hope that is some consolation, no, it wasn''t to me either.

Share this post


Link to post
Share on other sites
  
'Class: MyClass
Option Explicit

Public Key As String

Private mvarMyClassProperty As Long

Public Property Let MyClassProperty(ByVal vData As Long)
mvarMyClassProperty = vData
End Property


Public Property Get MyClassProperty() As Long
MyClassProperty = mvarMyClassProperty
End Property


  
'Class: MyCollection
Option Explicit

Private mCol As Collection

Public Function Add(Key As String, MyClassProperty As Long, Optional sKey As String) As MyClass
'create a new object
Dim objNewMember As MyClass
Set objNewMember = New MyClass

'set the properties passed into the method
objNewMember.Key = Key
objNewMember.MyClassProperty = MyClassProperty
If Len(sKey) = 0 Then
mCol.Add objNewMember
Else
mCol.Add objNewMember, sKey
End If

'return the object created
Set Add = objNewMember
Set objNewMember = Nothing

End Function

Public Property Get Item(vntIndexKey As Variant) As MyClass
Set Item = mCol(vntIndexKey)
End Property

Public Property Get Count() As Long
Count = mCol.Count
End Property

Public Sub Remove(vntIndexKey As Variant)
mCol.Remove vntIndexKey
End Sub

Public Property Get NewEnum() As IUnknown
Set NewEnum = mCol.[_NewEnum]
End Property

Private Sub Class_Initialize()
Set mCol = New Collection
End Sub

Private Sub Class_Terminate()
Set mCol = Nothing
End Sub


Basically, VB classes (including the collection) and UDTs don't mix. My guess is that they were going to get rid of the UDTs, except they realised that in many ways they are better than the class system they had devised. (Things like file saving, speed and simplicity)

When order isn't important, you can beat a collection hands down for speed (and code cleanliness). I haven't done tests for when the order within the container is needed to be sustained, but I haven't even thought about it.

What you *can* do, if you are in a position where you need to use UDTs, is something like this:

  
Sub PutUDTInArray(MyUDT As udtBlah, PutIn() As Byte)
Redim PutIn(Len(MyUDT) - 1) 'Assumes 0 based array

CopyMemory ByVal VarPtr(PutIn(0)), MyUDT.FirstElement, Len(MyUDT)
End Sub

Function GetUDTFromArray(GetFrom() As Byte) As udtBlah
CopyMemory ByVal GetUDTFromArray.FirstElement, VarPtr(PutIn(0)), Len(GetUDTFromArray)
End Function


But, you have to be careful, because if you use that with a udt with indirect data types (variable length strings, dynamic arrays), then copying just that memory will not be enough, but you have resize the destination and copy across there too. Could turn into a lot more work than you want.

So, probably the best solution is to just make a dynamic array with a wrapper around it so it acts like a collection. It will probably be faster, since the VB collection is one of those great "lets use Variants for everything since they are flexible" type things. For games, this is what I do, and performance is fine, but when I do it, I don't need to maintain order.

btw, Magmai, are you Thraka at another message board?

Trying is the first step towards failure.

Edit: Switched from codeblock? tags to source tags... the tag I always forget . Oh, and I might as well try and squeeze this in here, because I meant to say that squashing the udt into a byte array is ugly... although I'm sure you noticed... and I don't know the copymemory api call off the top of my head.

Edited by - ragonastick on January 4, 2002 5:26:17 AM

Share this post


Link to post
Share on other sites
For those interested parties ..

Public Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As Long)

Share this post


Link to post
Share on other sites
the VB compiler is probably the worst compiler ever, and VB is just as bad.

im not sure, but i think that every native type in VB is actually a variant... native types get built in error code for type checking, notice that type mismatches are run time only. native examples would be integers, variant strings, and classes.

variants can not be fixed length structures for some reason, and the compiler enforces this (design time).

if this is true, then the reason classes dont like UDT is that the class wants a variant, and a UDT is obvuously not a variant (UDT = fixed-length).

you need to do everything with classes, not UDTs; the VB designers were clearly stoned when they said VB was done.

if its possible to make your UDT inherit from a variant, then maybe u can fix ur problem. i forget if this is possible, but i think that only classes can inherit.

Edited by - evilcrap on January 4, 2002 6:33:31 AM

Share this post


Link to post
Share on other sites
No, thats not true. Theregular data types are real. However, VB does all conversions implicitly. I usually notate conversions implicitly, but it won''t make a difference.

-----------------------------
The sad thing about artificial intelligence is that it lacks artifice and therefore intelligence.

Share this post


Link to post
Share on other sites
quote:
im not sure, but i think that every native type in VB is actually a variant... native types get built in error code for type checking, notice that type mismatches are run time only. native examples would be integers, variant strings, and classes.

Definately not. Have a look at the memory used, for a variant type to work, there needs to be a few extra bits used to store the type, this isn't the case with any of VBs native types.
Unfortunately for so many programmers, VB will convert between numerical types on the fly, so:

Dim A As Long
A = 5

Will compile just fine. While it should read
Dim A As Long
A = 5&

Also, to my horror, I just found that:

Dim A As String
A = 5

Will work, but worse is that the reverse
Dim A As Long
A = "5"

will work too. I can see why there is so much slow VB code

quote:
variants can not be fixed length structures for some reason, and the compiler enforces this (design time).

I've never had a situation where using variants would give me any benefit. Maybe for business apps, but even then, I think I would still prefer to code my own "variant" type.

quote:
you need to do everything with classes, not UDTs; the VB designers were clearly stoned when they said VB was done.

Or the opposite, and don't use classes at all. Usually, this is a lot faster.

quote:
if its possible to make your UDT inherit from a variant, then maybe u can fix ur problem. i forget if this is possible, but i think that only classes can inherit.

No, it is only semi-inheritance at best with VB. Really, classes aren't very useful since they are missing those useful things which C++ classes have. Not that you can't do what C++ programs do, but that


Edited by - ragonastick on January 4, 2002 7:20:09 AM

Share this post


Link to post
Share on other sites
Ok, so your susposed to use property Let/Get - what''s Set for then?


Ah ha!
  
''class CostInfoChunk
Public Level As Integer
Public BaseID As String
Public LotID As String
Public SubID As String

''class CostInfo
Public Info As Collection
Public MaterialCost As Currency

''...
Dim g_CostInfoCollection as Collection

Dim CI as CostInfo
Dim chunk as CostInfoChunk
For Each CI in g_CostInfoCollection
For Each chunk in CI.Info
''mess with chunk
Next
Next


...
quote:

btw, Magmai, are you Thraka at another message board?


Afriad not, I''m MKH just about everywhere. A long time ago I used Silpheed (before the Internet went home).

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
The reason a UDT is not supported as a valid parameter of a public interface is deeply rooted in COM Automation Interface rules. You can only pass a valid VARIANT type on a COM Automation Interface, which all VB classes are based on at very low level. This includes SAFEARRAY as well (which is a vb array, with both an upper and lower bound stored as part of the array object contrary to C/C++ which implements arrays as contiguous memory), the only caveat being that you can only have SAFEARRAY''s of VARIANT objects! It''s a little redundant but it makes sense from a low level. COM has to marshall all that stuff and it has to know how to pack and unpack memory to marshal it across processes, apartments, and machines. A UDT in vb is an unknown to COM. COM has no way of knowing how big your UDT is or how to stream it''s information (it may have special streaming requirements). This is the reason why you can''t have UDT''s on a public interface. I suggest you convert your UDT''s to classes as suggested by an earlier post. It''s more object oriented and a cleaner aproach. See Java and C#.

Share this post


Link to post
Share on other sites
The problem isn''t COM - it''s VB. Those automation rules were made for C++ (and other languages) to follow so that it could work with VB. If you abandon automation (VB/VBScript/JScript/ActiveX), you can marshal anything (other than a void*).

VB knows all the type information of the UDT, it could use custom marshlaing to tell COM how to handle those apartment issues. If you did something under handed, like stuff a HWND or void* into a LONG it woudl break, but if you used the types normally all the information needed is there.

There''s even a SAFEARRAYEX interface to handle manipulating safearrays of UDTs (Delphi 5 SP3 doesn''t natively support it btw, 6 may...)

Share this post


Link to post
Share on other sites
let is for anything thats not a class i think
property let x(lx as int)
m_x = lx
end property

set is for classes
property set x(c as class)
set m_x = c
end property

Share this post


Link to post
Share on other sites