-
Apr 6th, 2014, 04:39 PM
#1
Thread Starter
Frenzied Member
Why do I keep getting this error?
I'm trying to recursively use a user defined type.
My first attempt was
Code:
Private Type testtype
t As testtype
b As Byte
End Type
but that didn't work because you can't use something's own type in its own definition.
Then I tried
Code:
Private Type testtype
v As Variant
b As Byte
End Type
and VB6 allowed that so I then went on to this code to test it.
Code:
Private Sub Form_Load()
Dim a As testtype
Dim a2 As testtype
a.v = a2
End Sub
But that gave the error:
"only user-defined types defined in public object modules can be coerced to or from a variant passed to late-bound functions"
That was the clue I needed, so then I just took the type definition code, changed it to public and put it in a "module" like this so as to fullfill the "public object module" requirement mentioned in the error message.
Code:
Public Type testtype
v As Variant
b As Byte
End Type
I then again used this code in my form.
Code:
Private Sub Form_Load()
Dim a As testtype
Dim a2 As testtype
a.v = a2
End Sub
But I still get the error message:
"only user-defined types defined in public object modules can be coerced to or from a variant passed to late-bound functions"
There appears to be no way to do what I'm trying to do. I am trying to get the ability to recursively use User Defined Types so that I can use them as nodes in a tree, specifically a huffman tree, so I can write my own huffman compression algorithm. But whenever I try to use UDTs recursively, VB keeps standing in my way! I'm pretty sure any other programming language could accomplish this, though none are as easy to use as VB. ARGH!!!!!!!!!!!!
-
Apr 6th, 2014, 04:49 PM
#2
Lively Member
Re: Why do I keep getting this error?
I played around with Huffman encoding for a while, maybe 15 years ag, and I know I never had to do anything like that. That just doesn't look right.
You might save yourself a lot of time and frustration by looking at some of the source code available for building tree structures. I'm just about to get started reading a book that has a number of examples of creating trees in legacy VB, so I'm afraid I can't help you yet.
AMD FX-4300, WinXPSP3, VB6SP6
-
Apr 6th, 2014, 05:40 PM
#3
Re: Why do I keep getting this error?
Code:
Private Sub Form_Load()
Dim a As testtype
Dim a2 As testtype
a.v = a2
End Sub
You are trying to set an element of a UDT= a UDT
You can create an Array of a UDT
You may even be able to define an element of a second UDT as a different UDT
You can use a class with properties instead of a UDT
In any case your definition is a UDT with the fields then you try to set one of those fields= the UDT but it is not expecting a UDT it is expecting a value
This of course would work but not what your are trying to do
-
Apr 6th, 2014, 07:56 PM
#4
Re: Why do I keep getting this error?
I'm pretty sure that having a UDT type contain a field that is the UDT itself should be impossible.
UDTs are not classes, so are not a reference type that can be set to Nothing.
When you create a UDT variable, the storage for the UDT is allocated immediately, it doesn't have to be instantiated, like a class.
So, if a UDT reference its own type, then the UDT field would have to be allocated, which contains the UDT, which has to be allocated, recursively until you run out of memory.
So, as DataMiser has already said, if you want to implement a recursive structure, it will have to be a Class, not a UDT.
-
Apr 6th, 2014, 09:22 PM
#5
Re: Why do I keep getting this error?
Originally Posted by Ben321
But I still get the error message:
"only user-defined types defined in public object modules can be coerced to or from a variant passed to late-bound functions"
Read the message more closely:
only user-defined types defined in public object modules can be coerced to or from a variant passed to late-bound functions
This is talking about Forms, UserControls, Classes, and the like where the Instancing property is greater than 1 (Private), i.e. modules that publish an interface. This module property is hidden in the IDE for a Standard EXE project because it can't be changed from Private anyway.
A static module (.bas) cannot be published in a VB6 project of any type so that's useless as a solution.
Basically this is telling you that what you are trying to do is better accomplished by using classes instead of UDTs.
While a private (as in unpublished) UDT can be a lightweight thing meeting a few specific needs (stand-in for a struct in an API call, use of creaky old VB Random files, and a few other things) they are obsolete in most respects. A published UDT (i.e. those defined in a DLL, OCX, ActiveX project) has a huge amount of plumbing attached to it, turning it into a rather heavyweight specialized kind of class.
-
Apr 7th, 2014, 01:30 AM
#6
Re: Why do I keep getting this error?
Originally Posted by dilettante
While a private (as in unpublished) UDT can be a lightweight thing meeting a few specific needs (stand-in for a struct in an API call, use of creaky old VB Random files, and a few other things) they are obsolete in most respects. A published UDT (i.e. those defined in a DLL, OCX, ActiveX project) has a huge amount of plumbing attached to it, turning it into a rather heavyweight specialized kind of class.
It is true that a published UDT has a bit of "plumbing" attached to it (in a way...when we talk about Variants or Dispatch-(LateBound-)Calls)...
Though such a class-defined Public UDT (*.tlb-Defs would work too) is not larger "mem-allocation-wise" -
meaning, you can pass variables of such types into API-functions without any difference to "normally
defined UDTs".
Also performance-wise (e.g. when you use a larger array of such an UDT-def) there's no difference.
The difference is, that as soon as you assign (copy) such a public defined UDT into a Variant (and back) -
which works without problems - VB (or COM-OleAut) assigns not only a pointer to the allocated
memory for the "plain udt-copy" into Variant-Offset 8 (same position as with other Variant-PointerMembers)...
what's different from other pointer-types is, that 4 Bytes *after* that (at Offset 12 in the Variant),
a pointer to a COM-Class-instance which implements IRecordInfo is placed (describing the UDT-
members with their names and sizes, and it is also responsible for copying etc.).
This IRecordInfo-instance is only created *once* per Type (even when you fill a lot of different
Variant-Variables with a lot of your UDT-allocations):
In a Public Class of an ActiveX-Dll-Project (which was added as part of a ProjectGroup)
Code:
Option Explicit
Public Type TestType
V As Variant
B As Byte
End Type
In a TestForm within the StdExe-Project (our first member in the ProjectGroup, needs a reference to 'Project2' above)
Code:
Option Explicit
Private Declare Sub RtlMoveMemory Lib "kernel32" (Dst As Any, Src As Any, ByVal Bytes&)
Private Sub Form_Load()
Dim a0 As TestType, a1 As TestType, a2 As TestType
Debug.Print LenB(a0) '<- prints 20
Debug.Print TypeName(a0.V), VarType(a0.V) '<- prints 'Empty', 0
a1.B = 1
a0.V = a1
Debug.Print TypeName(a0.V), VarType(a0.V) '<- prints 'TestType', 36 = vbUserDefinedType
a2.B = 2
a1.V = a2
a0.V.V = a2
Debug.Print a0.B, a1.B, a2.B
Debug.Print a0.B, a0.V.B, a0.V.V.B
'the IRecordInfo-instance is re-used (always the same Pointer in the different Variants)
TestIRecordInfoPtr a0.V
TestIRecordInfoPtr a0.V.V
TestIRecordInfoPtr a1.V
End Sub
Private Sub TestIRecordInfoPtr(ByVal V)
Dim pIRecInfo As Long
RtlMoveMemory pIRecInfo, ByVal VarPtr(V) + 12, 4
Debug.Print pIRecInfo
End Sub
To get a better picture about the behaviour and inner workings of this mechanism -
I've implemented a light-weight-COM version of IRecordInfo in a *.bas module some time ago,
which allows to avoid an external COM-Dll or TypeLib (the UDTs become Variant-wrappable
then within a normal StdExe-project, using the *.bas-Module implemented IRecordInfo-
wrapper.
Here's the thread (in a german forum, but the comments in the code-modules are in english).
http://foren.activevb.de/archiv/vb-c...GetFieldNames/
Olaf
Last edited by Schmidt; Apr 7th, 2014 at 02:03 AM.
-
Apr 7th, 2014, 07:40 AM
#7
Re: Why do I keep getting this error?
The cost comes from when IRecordInfo gets used at run time for reading or writing each member of the UDT. It is true this only occurs when such UDT values get used in Variants:
You only need to use IRecordInfo to pass a UDT by reference that is stored in a VARIANT. If the UDT is not stored in a VARIANT you can pass a pointer to the UDT because an IRecordInfo pointer can be retrieved from the UDT type description.
IRecordInfo is used to get the amount of storage needed for a UDT, clear the storage for a UDT, copy or access the fields in a UDT. However, the layout of a UDT is the same as the layout of structures in C++, therefore you may not have to use IRecordInfo for clearing, copying and accessing the fields of the storage.
As long as the IRecordInfo is available to the compiler (early binding) the overhead is optimized away. When Variant copies are used this optimization doesn't apply and "interpretive" calls into IRecordInfo methods passing field names must be made by the generated code.
But to create hierarchies (from simple lists, to trees, or generalized DOM-like structures) you may as well use VB Classes and be done with it.
-
Apr 8th, 2014, 12:16 PM
#8
Thread Starter
Frenzied Member
Re: Why do I keep getting this error?
A variant SHOULD be able to be set to ANYTHING, including a UDT, so having a UDT with a Variant field, should allow you to at runtime stick a variable of even the same UDT type in there. But it doesn't. It gives me that stupid crap about latebound bla bla bla instead.
-
Apr 8th, 2014, 01:04 PM
#9
Re: Why do I keep getting this error?
A UDT is a single object. It is not multi instance.
You can not set an object in any UDT to the UDT itself.
You need to use a class that can be multi instance or use an array of the UDT. What you are trying to do will not work and there really is no need to do it that way.
On second look you don't even need a UDT You only have two items there and one is trying to point back to itself so you really only have 1 and that is a byte. Seems like a Byte array is what you need here
Last edited by DataMiser; Apr 8th, 2014 at 01:08 PM.
-
Apr 8th, 2014, 01:34 PM
#10
Re: Why do I keep getting this error?
Originally Posted by Ben321
... I'm pretty sure any other programming language could accomplish this, though none are as easy to use as VB. ARGH!!!!!!!!!!!!
I'm pretty sure any other programming language wouldn't support using a value type in such a manner. Almost every example of nodes in lists or trees, etc will use a reference type so you can embed pointers to the objects that make up the nodes.
By trying to use a variant, you're trying to provide a "reference" mechanism to the UDT. You can see the effort needed to make such a thing work, based on the previous posts.
Why the resistance to using a Class instead of a UDT, when most every example of nodes in any language would use a class, or pointers to dynamically allocated memory in languages that don't have classes?
If you willing to use a UDT, then you wouldn't have to implement the properties "properly", you could just make them public and access them directly.
So the class in your example would simply be (using the default name Class1):
Code:
Option Explicit
Public t As Class1
Public b As Byte
And the example test
Code:
Option Explicit
Private Sub Form_Load()
Dim a As New Class1
Dim a2 As New Class1
Set a.t = a2
End Sub
It's not that much extra effort, although I'm sure some would say that is not a good or correct way to use a class...
I have used UDTs to implement trees, but in those cases I had known size limits that I wouldn't exceed so could create an array of the maximum UDTs I would allow, and use the array as a pool of UDTs to "allocate" from, and have Integer (or Long) fields in the UDT to server as pointers (i.e. the array index) to create linked lists, doubly linked lists, or binary tree nodes from.
It works fine and is quick, but is not suited for unknown dynamic allocation, generally.
-
Apr 8th, 2014, 06:53 PM
#11
Thread Starter
Frenzied Member
Re: Why do I keep getting this error?
Originally Posted by passel
I'm pretty sure any other programming language wouldn't support using a value type in such a manner. Almost every example of nodes in lists or trees, etc will use a reference type so you can embed pointers to the objects that make up the nodes.
By trying to use a variant, you're trying to provide a "reference" mechanism to the UDT. You can see the effort needed to make such a thing work, based on the previous posts.
Why the resistance to using a Class instead of a UDT, when most every example of nodes in any language would use a class, or pointers to dynamically allocated memory in languages that don't have classes?
If you willing to use a UDT, then you wouldn't have to implement the properties "properly", you could just make them public and access them directly.
So the class in your example would simply be (using the default name Class1):
Code:
Option Explicit
Public t As Class1
Public b As Byte
And the example test
Code:
Option Explicit
Private Sub Form_Load()
Dim a As New Class1
Dim a2 As New Class1
Set a.t = a2
End Sub
It's not that much extra effort, although I'm sure some would say that is not a good or correct way to use a class...
I have used UDTs to implement trees, but in those cases I had known size limits that I wouldn't exceed so could create an array of the maximum UDTs I would allow, and use the array as a pool of UDTs to "allocate" from, and have Integer (or Long) fields in the UDT to server as pointers (i.e. the array index) to create linked lists, doubly linked lists, or binary tree nodes from.
It works fine and is quick, but is not suited for unknown dynamic allocation, generally.
What is the trick to getting it to work using a Variant in a UDT? You say it's not easy, but that means you must know how to do it. Please let me in on the secret that you seem to know.
-
Apr 8th, 2014, 09:06 PM
#12
Re: Why do I keep getting this error?
I don't see in there anywhere where Passel said it was possible. Personally I wouldn't use an UDT in this case... A class seems much more appropriate for this case.
-tg
-
Apr 9th, 2014, 11:18 AM
#13
Re: Why do I keep getting this error?
Schmidt in post #6 covered what it would take, and you can refer to dilettante's posts before and after that for additional discussion.
Bottom line, you would have to publish the UDT as part of something, like an ActiveX Dll.
At the end post #6 Schmidt points to an example that creates a "light weight" COM object to implement the necessary published interface, without requiring an external Dll or type lib, and allows the UDT to be wrapped in a variant (because the published interface requirement is met).
By doing that, you essentially coerce the UDT into a reference type (the Variant being the reference holder), but that seems like so much work and effort to turn a UDT into a quasi class object, when you could just use a simple class to start with. I find it hard to imagine that there would be any speed advantage to wrapping the UDT into a variant as opposed to using a class, so seems like an unnecessary complication to me.
You mentioned a Huffman tree, and I guess if I wanted to use UDTs and a worse case number of nodes needed can be determined, I would just create an array of them in a pool, as I stated before, and use the index of where the UDT is in the array as the pointers to link them.
I know I did something similar as a possible coding competition entry years ago to try to quickly determine the frequency of words in a novel, but the competition never came about. That was done in VB6 but I would have to track the code down, it isn't on this machine.
Don't know if you care to see it. I know in the initial version I didn't try to balance the tree in any way.
I later tried to modify it in several ways, to try to add some measure of balancing, but in the case of a short novel, a little over 100,000 words, the overhead of the checks for balancing either slowed the process down, or didn't make enough of a speed difference to be worth the effort.
-
Apr 9th, 2014, 06:26 PM
#14
Re: Why do I keep getting this error?
Originally Posted by passel
Schmidt in post #6 covered what it would take, ...
Yep, and it isn't much what is needed, to cover the requirements of the OP, just this simple code in a Public Class of an AX-Dll:
Code:
Public Type TestType
V As Variant
B As Byte
End Type
what the OP would need after compiling that simple snippet (which contains only an interface-description),
is to just reference this compiled (Typelib-like) Dll in his Main-App.
After that he can stuff these TypeLib-defined UDTs into Variants as he originally intended.
Nothing complicated at all with that approach.
And to DataMiser and techgnome - it is entirely possible and reliable to work this way
with UDTs (stuffing them into Variants). It is just not practised - but that's more due
to the simple reason, that many VBers don't like to work with ActiveX-Dlls.
This approach would also be (much) more lightweight in terms of memory (compared with the
otherwise recommended Class-based approaches).
*But* it would require to work LateBound "through" the Variant-Wrappers of these
(nested) UDT-definitions.
And that would cost performance, compared with the class-based approach.
So, a classbased approach would be faster (as long as no larger byte-content
in the Megabyte-Range gets huffman-encoded) - whereas the Variant+UDT
nesting-approach would need less memory (a VB-Class-Instance needs ~110Bytes -
a Variant as the "outer hull" would take up only 16Bytes).
So, both approaches are not optimal - and need to be avoided in a decent implementation.
There's much easier (and faster) ways to accomplish performant tree-structures in VB6...
as passel already pointed out, the simplest way to do it would be, to exchange the
"reference-type" in the UDT with an Index-member (a VB-Long for example) - and
use that Long to "refer" (by Index) into another allocation which then can be taken
up in contiguous memory in larger block-reallocates (as e.g. in a "linear" array, or "vector")
instead of a bunch of isolated, separated smaller ones.
Originally Posted by passel
You mentioned a Huffman tree, and I guess if I wanted to use UDTs and a worse case number of nodes needed can be determined, I would just create an array of them in a pool, as I stated before, and use the index of where the UDT is in the array as the pointers to link them.
Yes, exactly (should have read further...)
Originally Posted by passel
I know I did something similar as a possible coding competition entry years ago to try to quickly determine the frequency of words in a novel, but the competition never came about. That was done in VB6 but I would have to track the code down, it isn't on this machine.
Sounds suspicously like a contest I've also taken part in (is it by any chance the german "dotnet-pro journal",
formerly known as "basic-pro" we're talking about)?
Will try to find my submission (it was also tree-based, and came out only 10-20% slower, compared to the
C++-based winner).
Olaf
-
Apr 9th, 2014, 07:00 PM
#15
Re: Why do I keep getting this error?
And to DataMiser and techgnome - it is entirely possible and reliable to work this way
with UDTs (stuffing them into Variants). It is just not practised - but that's more due
to the simple reason, that many VBers don't like to work with ActiveX-Dlls.
You should note that I was referring to using them within the program which is what the OP was trying to do, not wrapping them in a dll which ironically needs to use a class in order to make this work.
Why not just use a class and be done with it.
-
Apr 9th, 2014, 10:04 PM
#16
Re: Why do I keep getting this error?
Originally Posted by DataMiser
You should note that I was referring to using them within the program which is what the OP was trying to do, not wrapping them in a dll which ironically needs to use a class in order to make this work.
Why not just use a class and be done with it.
Thought I've explained the advantage of using a UDT in a Variant-Hull already (it has the far leaner Mem-Consumption).
Wrapping the Memory-Allocation of two simple members this way in a Class:
Code:
Option Explicit
Public t As Class1
Public b As Byte
will allocate 8Byte for the two members + 110Bytes overhead per ("naked") VB-class-instance.
...whilst the following definition is not really a Class - it's a plain Typelib-definition, which
just "happens to be noted in a class".
Code:
Public Type TestType
V As Variant
B As Byte
End Type
...and it will allocate 20 Bytes for the two members + again 20 Bytes for additional entries of itself in V.
There's another thing you got wrong, since you wrote: "A UDT is a single object. It is not multi instance."
An UDT is just a Memory-allocation - you can have as many UDT-allocations of the same type
as you want (behind Variables or Arrays) - and apparently also nested (with a Variant as the
Reference-Container).
There's C/C++ (or FreeBasic FWIW), ...any language which supports pointer-operators which
allow a convenient access per "referenced-member-specifier":
pStruct->SomeMember
instead of:
Struct.SomeMember
With this kind of "Pointerbased, reference-nesting of Structs" the whole thing becomes
a bit easier to handle - but a Struct is still the same thing as an UDT in VB - just an
information for the compiler, how many bytes to allocate when such a thing gets used -
(along with information, at which offset in those allocated Bytes the Members start).
Classes always have more overhead than Structs (even in C++, where they are lighter than in VB -
they (don't know the exact amount) have an overhead of perhaps 12 or 16Bytes there ...
A Struct does not have *any* Overhead (other than the one caused by alignment-fillers -
but the alignment-rules would cause the very same small overhead also on the members of
C++ Classes.
Olaf
Last edited by Schmidt; Apr 9th, 2014 at 11:31 PM.
-
Apr 9th, 2014, 10:54 PM
#17
Re: Why do I keep getting this error?
Yes I was a bit confused when I mentioned the UDT being a single object, actually it could be more than one even in a module though you could not specify the UDT as a member of that same UDT.
I guess I must be missing something here as the whole notion seems very odd to even want to try, especially considering that there is only a single byte of actual data being stored.
-
Apr 10th, 2014, 05:18 AM
#18
Re: Why do I keep getting this error?
Originally Posted by DataMiser
I guess I must be missing something here as the whole notion seems very odd to even want to try, especially considering that there is only a single byte of actual data being stored.
True... but you know how it is...we never get the full story and do get a lot of "That was for example, the real situation is different."
-tg
-
Apr 10th, 2014, 09:37 AM
#19
Re: Why do I keep getting this error?
Yep... hard to give good advice when not presented with real info. In the example case imagine what the code would look like to access an element 10 levels deep
x=A.v.v.v.v.v.v.v.v.v.v.b
where it could be
x=b(9)
-
Apr 10th, 2014, 10:11 AM
#20
Re: Why do I keep getting this error?
Originally Posted by DataMiser
Yep... hard to give good advice when not presented with real info. In the example case imagine what the code would look like to access an element 10 levels deep
x=A.v.v.v.v.v.v.v.v.v.v.b
where it could be
x=b(9)
Yep - your x=b(9) code would be the use of an index as an alternative way to reference
a Type - that's how one can work (with a few additional arrays and stuff) also performant
with VB6 in such a scenario.
Though what you wrote at the top is not quite, how it would be used - many parts in
a Huffman (or general tree-like) implementation will need a "traversing the hierarchy" -
and this would look more like:
Do While CheckOutNode(A) = enmContinueTraversing
A = A.v
Loop
Or something in analogy to that.
Olaf
Posting Permissions
- You may not post new threads
- You may not post replies
- You may not post attachments
- You may not edit your posts
-
Forum Rules
|
Click Here to Expand Forum to Full Width
|