Results 1 to 20 of 20

Thread: Passing UDT as variant, for saving/loading UDTs

  1. #1

    Thread Starter
    Junior Member
    Join Date
    Oct 2021
    Posts
    23

    Passing UDT as variant, for saving/loading UDTs

    I have a program that reads/interprets user text; then uses the info to do other stuff.


    text (XL sheet) --> interpret routine --> UDT(s) (parameters(list) what the user text is saying)

    The routine for interpreting text is vastly complicated and slow.
    I want to save/load the interpreter UDT(s) results, so the program can attempt to skip the interpret routine when executed.


    I copied code for saving/loading UDTs, I'm a genius:
    https://www.vbforums.com/showthread....-Arrays-Easily

    Now, I want to make a universal utility class for saving/loading any UDT() or array.


    Code:
    Call mySaveLoadTool.saveUDT(anyUDT)

    But you cannot pass a UDT as a variant or cast it as an object reference.

    I cannot/will not change my UDT(s) to classes. This would be a nightmare, and not really solve the problem.



    Is there a smart/painless way of working around this?

  2. #2
    PowerPoster Elroy's Avatar
    Join Date
    Jun 2014
    Location
    Near Nashville TN
    Posts
    9,853

    Re: Passing UDT as variant, for saving/loading UDTs

    You have to declare your UDT in a TypeLib. You can create TypeLibs with VB6, but, when done that way, you have to distribute the TypeLib. Alternatively, you can learn about these TypeLibs and compile them with the MIDL compiler. Done that way, they don't have to be distributed with your EXE.

    And, once your UDT is declared in your TypeLib (as opposed to within your VB6 source code), you can place them into Variant variables.

    (I'd supply references, but I've got to go in a few.)
    Any software I post in these forums written by me is provided "AS IS" without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. To all, peace and happiness.

  3. #3
    PowerPoster Elroy's Avatar
    Join Date
    Jun 2014
    Location
    Near Nashville TN
    Posts
    9,853

    Re: Passing UDT as variant, for saving/loading UDTs

    Also, just to say it, be sure to declare all procedures in your forms and CLS modules as Friend (rather than Public). If you've got a public UDT, you can pass it around to Friend procedures without a problem, although the argument must be declared with the UDT's declaration name (and not passed as a Variant). Only way to get a UDT into a Variant is to use a TypeLib.
    Any software I post in these forums written by me is provided "AS IS" without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. To all, peace and happiness.

  4. #4

    Thread Starter
    Junior Member
    Join Date
    Oct 2021
    Posts
    23

    Re: Passing UDT as variant, for saving/loading UDTs

    Hang on, I think I know how.

  5. #5

    Thread Starter
    Junior Member
    Join Date
    Oct 2021
    Posts
    23

    Re: Passing UDT as variant, for saving/loading UDTs

    I got something. It was painful.
    I spent a while trying to figure out a work around. There is nothing.

    VB can't do reflections
    VB can't do overloads
    VB can't do GOTO labels outside of scope


    I made Class that has Save/Load functions. And Get/Let Properties with the same name(s) as the user-type...
    I made it as copy/paste-able as possible, for adding/removing user-types to class code.


    Here's the thing, I'm actually doing this in EXCEL-VBA. I posted it here because that is were the Save/Load UDT array Post was. I thought it made more sense to keep the post here because it had nothing to do with the Office App it was being run from.

    With VBA you can programmatically create code. According to the internet you can do the same in VB.
    So I made a Class that automatically creates the UDT SAVE/LOAD Class with functions for all the custom user-types. That way you don't have to manually edit UDT-SAVE/LOAD Class every time you add or remove a user-type.


    Attached is the code.
    For VB I think you just need to replace ThisWorkbook with Application in the makeUDTTool Class and it should run.


    • makeUDTtool.cls : Class for creating UDT save/load class
    • Module_demo_4_UDTtool.bas : routines for creating and using UDT save/load class
    • new1_UDTTools.cls : for reference, should not need to import
    • new2_UDTTools.cls : for reference, should not need to import



    1. Import the makeUDTTool class
    2. Import the Module_demo_4_UDTtool module
    3. Run the udtTool_maker_demo routine
      newX_UDTTools Class(s) should be created
    4. Run the udtTool_SaveLoad_demo routine
      Shows how the newly created new2_UDTTools Class is used.
    Attached Files Attached Files

  6. #6
    PowerPoster Elroy's Avatar
    Join Date
    Jun 2014
    Location
    Near Nashville TN
    Posts
    9,853

    Re: Passing UDT as variant, for saving/loading UDTs

    Tony, I'm sorry but I didn't look at your code. But yeah, you can wrap a UDT in an object (instantiated from a class), and then get that "wrapped" UDT into a variant. But, it's a pain. Once it's in a typelib, it's much easier to do.

    But yeah, you can wrap the UDT in a class, and do it.

    For example, here's some code for a BAS module:
    Code:
    
    Option Explicit
    
    Public Type MyUDT
        a As Long
        b As Long
    End Type
    
    
    
    And code for a Class1 CLS module:
    Code:
    
    Option Explicit
    
    Dim InternalUdt As MyUDT
    
    Friend Property Get TheUdt() As MyUDT
        TheUdt = InternalUdt
    End Property
    
    Friend Property Let TheUdt(udt As MyUDT)
        InternalUdt = udt
    End Property
    
    
    And finally, some test code for a Form1 FRM module:
    Code:
    
    Option Explicit
    
    Private Sub Form_Load()
    
        Dim c1 As New Class1    ' Our instantiated class used as a UDT wrapper.
        Dim u1 As MyUDT         ' A test UDT.
        u1.a = 5                ' Give it some non-zero values.
        u1.b = 6
    
        c1.TheUdt = u1          ' Copy it in the class wrapper.
    
        Dim u2 As MyUDT         ' Another test UDT.
        u2 = c1.TheUdt          ' Get UDT out of wrapper (as a copy).
    
        Debug.Print u2.a        ' Check:  Yep, it prints 5.
    
    
        Dim v As Variant        ' Variant for putting class into.
        Set v = c1              ' Stick our wrapper class into the variant (as a reference).
    
        Dim c2 As Class1        ' Another class variable for another reference of our class.
        Set c2 = v              ' Pull wrapper class out of variant (creating yet another reference to it).
    
        Dim u3 As MyUDT         ' Another UDT for testing.
        u3 = c2.TheUdt          ' Copy our UDT out of this new class reference.
        Debug.Print u3.a        ' Check and see if it's a good copy.  Yep, it prints 5.
    
    End Sub
    
    
    ---------

    And just FYI, you can reference TypeLibs in the VBA:

    Name:  VbaReferences.png
Views: 391
Size:  11.5 KB

    Name:  Browse.jpg
Views: 468
Size:  48.6 KB

    Name:  BrowseTlb.jpg
Views: 432
Size:  31.7 KB
    Any software I post in these forums written by me is provided "AS IS" without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. To all, peace and happiness.

  7. #7
    Fanatic Member
    Join Date
    Aug 2016
    Posts
    597

    Re: Passing UDT as variant, for saving/loading UDTs

    you can wrap a UDT in an object (instantiated from a class), and then get that "wrapped" UDT into a variant. But, it's a pain. Once it's in a typelib, it's much easier to do.
    Classic summary, thank you~

  8. #8

    Thread Starter
    Junior Member
    Join Date
    Oct 2021
    Posts
    23

    Re: Passing UDT as variant, for saving/loading UDTs

    Nvm, that is what I did... Just a more complete version...

    That is not what I did...

    I made these containers a Type and not a Class for a reason; I wanted them to behave like a value-type.
    I have Types inside Types inside an array in other Types and so on...

    %99.9 of the time I do NOT want the equal sign '=' to be setting things by reference.


    If they were classes I would have to make and update clone routines for every container created, and have nested clone procedures... And used Clone instead of '=', for hundreds times in the code

    Cloning is slow and makes the code hard to follow. Any small changed made to a container would also require changing X number of clone routines.

    I have a universal clone routine in .NET but it uses reflections, which VB does not have.

    If something is accidently set byref it can be a nightmare trying to figure out what and where the error is.
    Also, I would have to deal with initializing every container created.


    --------------------------------------

    I don't want to make a TypeLib because I cannot/will not use additional reference in any of my EXCEL-VBA programs.
    My programs are run by everyone, some of which no nothing of programming or how to add missing dlls, or have the privileges to do so.

    Programs need to run on various Windows Versions and Excel versions and 32b, and be update immune. A seemingly small extra step of adding a missing TypeLib to someone's computer can turn into a major hassle... I did it before, never again.


    --------------------------------------

    The code attached creates a class with Save/Load capabilities for every UDT used... Creates generic properties names so the operation is self explanatory.
    Look at Sub udtTool_SaveLoad_demo()




    Code:
    '________ some user-defined types for code demonstration _________________________________
    '
    '   !Types CANNOT conatain Objects or enumerations for UDT SAVE/LOAD tool to work
    '
    '   -Must be Public
    '   -Name Does NOT matter
    '       *if the name(s) are changed, the UDT-Tool Class must be re-made using 'makeUDTtool'
    
    Public Type myTYPE1
    
        num1 As Double
        str1 As String
    
    End Type
    
    
    Public Type myTYPE2
    
        type1_arr() As myTYPE1
        
        num2 As Integer
        
        num_arr2() As Integer
    
    End Type
    
    
    Public Type myTYPE3
        
        type2_arr() As myTYPE2
        
        str3 As String
        
    End Type
    
    '_______________________________________________________________________________
    
    
    
    
    '_____ UDT SAVE/LOAD Tool CLass Maker ___________________________________
    '
    '   only need to be called once
    '   will only make code if Class does Not exist yet
    '
    '   Creates a Class that has functions for Saveing/Load UDT arrays from file
    '
    '   myUDTmakerTool.makeClass [name of new-Class], [exact name of User-Type], [exact name of User-Type], ...
    '
    '   After this is run, the 'udtTool_SaveLoad_demo' can run
    '
    'myTYPE1
    'myTYPE2
    'myTYPE3
    Sub udtTool_maker_demo()
    
        Dim myUDTmakerTool As New makeUDTtool
        
    
        ' *attempt to make UDT tool with no type-names passed
        '
        '   *need UDT names, won't make anything
        '
        myUDTmakerTool.makeClass "new1_UDTTools"
        
        
        '- make UDTtool named 'new1_UDTTools' with only 'myTYPE1' functions/properties
        '
        myUDTmakerTool.makeClass "new1_UDTTools", "myTYPE1"
        'SUCCESS
        
    
        ' *attempt make UDTtool for 'myTYPE2' using the SAME NAME
        '   - will NOT do anything!
        '
        '   code will not overwrite existing Classes
        '       must delete Class manually to remake
        '
        myUDTmakerTool.makeClass "new1_UDTTools", "myTYPE2"
        
        
        
        ' make UDTtool named 'new2_UDTTools' with all user-type functions
        '
        myUDTmakerTool.makeClass "new2_UDTTools", "myTYPE1", "myTYPE2", "myTYPE3"
        'SUCCESS
        
        '   now Classes 'new1_UDTTools' & 'new2_UDTTools' are available
        '
        '   They'll have save/load functions for {myTYPE1} & {myTYPE1,myTYPE2,myTYPE3} (respectivly)
        '
    
    End Sub
    
    
    'myTYPE1
    'myTYPE2
    'myTYPE3
    Sub udtTool_SaveLoad_demo()
    
        Dim demo_type1() As myTYPE1
        Dim demo_type2() As myTYPE2
        Dim demo_type3() As myTYPE3
        
        Dim check_type1() As myTYPE1
        Dim check_type2() As myTYPE2
        Dim check_type3() As myTYPE3
        
        
        Dim myUDTtool As New new2_UDTTools  '<-- made in the above code
        
        '-- populate UDTs with crap
        Call populate_demo_types(demo_type1, demo_type2, demo_type3)
        
        
        '-- retain values (for program validation)
        check_type1 = demo_type1
        check_type2 = demo_type2
        check_type3 = demo_type3
    
    
        '____ Program will SAVE UDT arrays to file(s) _____________________________
    
        
        'STEP (1)
        '-- add UDT arrays to TOOL (type1&3)
        myUDTtool.myTYPE1 = demo_type1
        myUDTtool.myTYPE3 = demo_type3
        
        
        'STEP (2)
        '-- save to computer
        '
        '   saves both 'demo_type1', 'demo_type3'
        '
        '   !!  'demo_type2' is NOT saved
        '        it was not added to the udtTOOL @ STEP (1)
        '
        '   default filename is:
        '       "udt_array_[UDTname].txt"
        '
        myUDTtool.SaveUDT
        
        
        'STEP (3)
        '-- now add 'demo_type2'
        myUDTtool.myTYPE2 = demo_type2
        
        
        'STEP (4)
        '-- save all 3 to 'myFavoriteFile_[UDTname].txt'
        '
        '       "myFavoriteFile_myTYPE1.txt"
        '       "myFavoriteFile_myTYPE2.txt"
        '       "myFavoriteFile_myTYPE3.txt"
        '
        myUDTtool.SaveUDT "myFavoriteFile"
        
        
        'STEP (5)
        '-- save only demo_type2 to 'myThirdFile_[UDTname].txt'
        '
        '       "myThirdFile_myTYPE2.txt"
        '
        '       function will recongnize "myTYPE2" in 'FileName' argument
        '       and only save that type
        '
        myUDTtool.SaveUDT "myThirdFile" & "myTYPE2"
        
        
    
        '_______ This is what is Should be saved at this Point ________________________
        '
        '* in the same directory as the WorkBook ...
        '
        '   "udt_array_[UDTname].txt"
        '       -type1 & type3 saved there @ STEP (2)
        '           udt_array_myTYPE1.txt
        '           udt_array_myTYPE2.txt
        '
        '   "myFavoriteFile_[UDTname].txt"
        '       -type1 & type2 & type3 saved there @ STEP (4)
        '           myFavoriteFile_myTYPE1.txt
        '           myFavoriteFile_myTYPE2.txt
        '           myFavoriteFile_myTYPE3.txt
        '
        '   "myThirdFile_[UDTname].txt"
        '       -type2 saved there @ STEP (4)
        '           myThirdFile_myTYPE2.txt
        '_______________________________________________________________________________
    
        
        
        '____ Now, Program will LOAD UDT arrays from file(s) ___________________________
        
        
        'STEP (6)
        '-- load files saved with filename "udt_array_[UDTname].txt"
        '
        '   *remember, only type1&3 were save here @ STEP (2)
        '
        '
        Erase demo_type1    '<-- erase local arrays
        Erase demo_type2
        Erase demo_type3
        
        
        myUDTtool.LoadUDT "udt_array"
        
        demo_type1 = myUDTtool.myTYPE1
        demo_type3 = myUDTtool.myTYPE3
        
        demo_type2 = myUDTtool.myTYPE2  '<-- this will work, !!BUT it is NOT from the "udt_array" file
                                        '       It is from the file set @ STEP (3)
                                        '       Class still retains the type2 array that was set
                                        
        
        
        'STEP (7)
        '-- repeat STEP (6) but also set data in 'myUDTtool' to Nothing
        '
        '
        Erase demo_type1    '<-- erase local arrays
        Erase demo_type2
        Erase demo_type3
        
        myUDTtool.myTYPE1 = demo_type1      '< *Also reset arrays in Class
        myUDTtool.myTYPE2 = demo_type2
        myUDTtool.myTYPE3 = demo_type3
        
        
        myUDTtool.LoadUDT "udt_array"
        
        demo_type1 = myUDTtool.myTYPE1
        demo_type3 = myUDTtool.myTYPE3
        
        demo_type2 = myUDTtool.myTYPE2  '<-- !Now this will NOT work, unlike STEP (6)
                                        '    because the arrays were also reset in the Class
                                        '    -type2 was NOT save @ "udt_array...txt", so it is still set to NOTHING
                                        
                                        
        
        
        'STEP (8)
        '-- get ALL UDT arrays saved in "myFavoriteFile_[UDTname].txt"
        '
        '
        Erase demo_type1    '<-- erase local arrays
        Erase demo_type2
        Erase demo_type3
        
        myUDTtool.myTYPE1 = demo_type1      '< *Also reset arrays in Class
        myUDTtool.myTYPE2 = demo_type2
        myUDTtool.myTYPE3 = demo_type3
        
        
        myUDTtool.LoadUDT "myFavoriteFile"
        
        demo_type1 = myUDTtool.myTYPE1
        demo_type2 = myUDTtool.myTYPE2
        demo_type3 = myUDTtool.myTYPE3
        
        
        
        
        
        'STEP (9)
        '
        '   (A) get ONlY type1 from "udt_array_[UDTname].txt"
        '
        '   (B) get ONLY type2 from "myThirdFile_[UDTname].txt"
        '       * type2 is the only one saved there @ STEP (5)
        '
        '   (C) get ONLY type3 from "myFavoriteFile_[UDTname].txt"
        '
        '
        Erase demo_type1    '<-- erase local arrays
        Erase demo_type2
        Erase demo_type3
        
        myUDTtool.myTYPE1 = demo_type1      '< *Also reset arrays in Class
        myUDTtool.myTYPE2 = demo_type2
        myUDTtool.myTYPE3 = demo_type3
        
        
        '____(A)____________________________________________________________
        
        myUDTtool.LoadUDT "udt_array" & "myTYPE1"    '<-- load only type1 from "udt_array...txt"
        
        Erase demo_type1                    '<-- erase local arrays
        Erase demo_type2
        Erase demo_type3
        '-------------------------
        demo_type1 = myUDTtool.myTYPE1      '<-- should work
        demo_type2 = myUDTtool.myTYPE2      '<-- !should NOT work
        demo_type3 = myUDTtool.myTYPE3      '<-- !should NOT work
                                        
                                        
                                        
        '____(B)____________________________________________________________
          
        myUDTtool.LoadUDT "myThirdFile"     '<-- load only type2 from "myThirdFile...txt"
                                            '    *remember only type2 was saved there @ STEP (5)
                                            '     therefore, 'myTYPE2' does NOT need to be specified in the argument
        
        Erase demo_type1                    '<-- erase local arrays
        Erase demo_type2
        Erase demo_type3
        '-------------------------
        demo_type1 = myUDTtool.myTYPE1      '<-- should work, Class still holds UDT array loaded from "udt_array...txt" (above)
        demo_type2 = myUDTtool.myTYPE2      '<-- should work, Newly loaded from "myThirdFile...txt"
        demo_type3 = myUDTtool.myTYPE3      '<-- !should NOT work
        
        
        
        '____(C)____________________________________________________________
          
        myUDTtool.LoadUDT "myFavoriteFile" & "myTYPE3"    '<-- load only type3 from "myFavoriteFile...txt"
                                                        '    *remember, all types were saved there @ STEP (4)
        
        Erase demo_type1                    '<-- erase local arrays
        Erase demo_type2
        Erase demo_type3
        '-------------------------
        demo_type1 = myUDTtool.myTYPE1      '<-- should work, Class still holds UDT array loaded from "udt_array...txt" (above)
        demo_type2 = myUDTtool.myTYPE2      '<-- should work, Class still holds UDT array loaded from "myThirdFile...txt" (above)
        demo_type3 = myUDTtool.myTYPE3      '<-- should work, Newly loaded from "myThirdFile...txt"
        
                                        
        
    
    End Sub
    
    
    
    
    
    
    Sub populate_demo_types(ByRef demo_type1() As myTYPE1, _
                            ByRef demo_type2() As myTYPE2, _
                            ByRef demo_type3() As myTYPE3)
    
        Dim i, j As Integer
        Dim str As String
        
        ReDim demo_type1(9)
        For i = 0 To UBound(demo_type1)
        With demo_type1(i)
            .num1 = i
            .str1 = CStr(i)
        End With
        Next
    
        ReDim demo_type2(9)
        For i = 0 To UBound(demo_type2)
        With demo_type2(i)
            
            .type1_arr = demo_type1
            For j = 0 To UBound(.type1_arr)
            With .type1_arr(j)
                .num1 = .num1 + i * 10
                .str1 = CStr(.num1)
            End With
            Next
        
            .num2 = i * 10
            
            ReDim .num_arr2(5)
            For j = 0 To UBound(.num_arr2)
                .num_arr2(j) = .num2 + j
            Next
       
     
        End With
        Next
    
        ReDim demo_type3(7)
        For i = 0 To UBound(demo_type3)
        With demo_type3(i)
            
            .type2_arr = demo_type2
            For j = 0 To UBound(.type2_arr)
            With .type2_arr(j)
                .num2 = .num2 * 10
            End With
            Next
            
            .str3 = CStr(i * 100)
    
        End With
        Next
    
    
    End Sub
    Last edited by tonyd5; Oct 26th, 2021 at 08:58 AM.

  9. #9
    PowerPoster Elroy's Avatar
    Join Date
    Jun 2014
    Location
    Near Nashville TN
    Posts
    9,853

    Re: Passing UDT as variant, for saving/loading UDTs

    Glad you got it going.

    And yeah, I "get" it with respect to "needing dependencies (like typelibs) registered" on target computers. It can be a problem.
    Any software I post in these forums written by me is provided "AS IS" without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. To all, peace and happiness.

  10. #10
    Fanatic Member
    Join Date
    Aug 2016
    Posts
    597

    Re: Passing UDT as variant, for saving/loading UDTs

    I suggest you read "http://sandsprite.com/CodeStuff/Understanding_UDTs.html"

  11. #11

    Thread Starter
    Junior Member
    Join Date
    Oct 2021
    Posts
    23

    Re: Passing UDT as variant, for saving/loading UDTs

    Quote Originally Posted by DaveDavis View Post
    I suggest you read "http://sandsprite.com/CodeStuff/Understanding_UDTs.html"
    I looked into that, CopyMemory, & Lset.
    I think I could not get it to work with UDT w/ dynamic arrays.

    And it would not solve the problem of passing/returning an arbitrary UDT to Save/Load functions...

    UDT--> ByteArray
    Still would have the same problem with passing UDT argument
    UDT_to_ByteArr(arbitrary_UDT as Varient) As Byte()

    ByteArray[Save | Load ]

    UDT <-- Byte Array
    Still would have the same problem returning UDT argument
    ByteArr_to_UDT(input_byteArr() as Byte) As Varient)

    I would have to do the UDT<--> Byte() operations within the current scope... At that point it would be easier just calling Put() & Get()

  12. #12
    PowerPoster Elroy's Avatar
    Join Date
    Jun 2014
    Location
    Near Nashville TN
    Posts
    9,853

    Re: Passing UDT as variant, for saving/loading UDTs

    Tony, I'm not sure what you're trying to do, but this thread might help you. It shows you how to "serialize" a UDT by using a Windows pipe. Then, if you like, you can write that serialized data to wherever you want. Olaf also has the de-serializing code there as well in case you want to put the byte-string back into a UDT.

    But yeah, UDTs have all kinds of difficult internal spacing, as well as many things that are "referenced" (like strings and objects). By the way, I doubt that serialization will work if you have any objects in your UDT, but I doubt you do.
    Any software I post in these forums written by me is provided "AS IS" without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. To all, peace and happiness.

  13. #13
    Frenzied Member
    Join Date
    Jun 2015
    Posts
    1,055

    Re: Passing UDT as variant, for saving/loading UDTs

    Kind of a specialty case but maybe useful, you can use something like a memory structure class to generically
    hold the different types in one configurable container which can be serialized/deserialized at any time.

    Downside you do not get intellisense on the field element names. More designed for dealing with file formats,
    and extracting structs from raw memory, files, packet buffers etc

    https://github.com/dzzie/libs/blob/m.../Form1.frm#L67

  14. #14

    Thread Starter
    Junior Member
    Join Date
    Oct 2021
    Posts
    23

    Re: Passing UDT as variant, for saving/loading UDTs

    Quote Originally Posted by Elroy View Post
    Tony, I'm not sure what you're trying to do, but this thread might help you. It shows you how to "serialize" a UDT by using a Windows pipe. Then, if you like, you can write that serialized data to wherever you want. Olaf also has the de-serializing code there as well in case you want to put the byte-string back into a UDT.

    But yeah, UDTs have all kinds of difficult internal spacing, as well as many things that are "referenced" (like strings and objects). By the way, I doubt that serialization will work if you have any objects in your UDT, but I doubt you do.
    I also looked into that briefly, that is very cool.

    The goal is for me or anyone to make Types like normal and be able to use a universal Save/Load function without doing anything extra what so ever.
    I believe I accomplished that. I can save/load ANY udt with 2 lines of code in the calling routine; 3 if you count the Class declaration.
    Just look:

    Code:
    ' UDTTools (CLass)	'< ! this gets made automatically with Let/Get properties for all UDTs
    '----------------------------------------------------------------------------------------
    '	LET: myUDTtool.[Name of UDT](input() as [Name of UDT]) 
    '	GET: myUDTtool.[Name of UDT]() as [Name of UDT] 
    '
    '	myUDTtool.[SaveUDT|LoadUDT](Optional [Filename & Optional [Name of UDT]] as String)
    '		myUDTtool.[SaveUDT|LoadUDT]()				'<-- [save|load] all UDT arrays [to|from] 'udt_array_[Name of UDT].txt' (for each type)
    '		myUDTtool.[SaveUDT|LoadUDT]("myfile")			'<-- [save|load] all UDT arrays [to|from] 'myfile_[Name of UDT].txt' (for each type)
    '		myUDTtool.[SaveUDT|LoadUDT]("myfile" & "myTYPE1")	'<-- [save|load] only 'myTYPE1()' [to|from] 'myfile_myTYPE1.txt' 
    '	
    '
    
    
    Dim Xvar1() As myTYPE1
    Dim Yvar1() As myTYPE1
    '
    Dim Xvar2() As myTYPE2
    Dim Yvar2() As myTYPE2
    '
    Dim myUDTtool As New UDTTools
    
    '
    'add whatever to 'Xvar()[1,2]'
    '
    
    '- Save Xvar[1,2] to file
    myUDTtool.myTYPE1 = Xvar1
    myUDTtool.myTYPE2 = Xvar2
    myUDTtool.SaveUDT
    
    
    '- Load Yvar[1,2] from file
    myUDTtool.LoadUDT
    Yvar1=myUDTtool.myTYPE1
    Yvar2=myUDTtool.myTYPE2

  15. #15
    PowerPoster Elroy's Avatar
    Join Date
    Jun 2014
    Location
    Near Nashville TN
    Posts
    9,853

    Re: Passing UDT as variant, for saving/loading UDTs

    You know? It dawns on me that we could easily put these serialized UDTs into a Variant. Variants can trivially hold Byte arrays, which is what the serialized UDT becomes. And then, when we were ready to use it, just de-serialize it.

    Also, personally, I'd take Olaf's work out of that CLS module, and just make them straight-up procedure calls in a BAS module. But that's just me. I use CLS modules often, but I do think people go overboard with their use. However, after examination, his approach does have the advantage of being able to raise an event for the caller.
    Last edited by Elroy; Oct 28th, 2021 at 11:44 AM.
    Any software I post in these forums written by me is provided "AS IS" without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. To all, peace and happiness.

  16. #16

    Thread Starter
    Junior Member
    Join Date
    Oct 2021
    Posts
    23

    Re: Passing UDT as variant, for saving/loading UDTs

    The serialization of UDTs is very power.

    Its basically a UDT --> Variant converter. Opens up a lot of options.
    I can add this feature to the existing solution I did without too much work.

    UDT--> Byte() solution still has 2 problems with it.


    Wrapping UDT-->Byte() routine in to universal call:
    myBYTarr=UDT_2_ByteArr(AnyUDT)
    My existing solution already has the ability to work around this problem.

    Determining which UDT type the byte array is serializing:
    i.e. Which UDT to deserialize to.

    This is more tricky.
    You could manually specifying which UDT the byte array is supposed to be in code, but this could be a big limitation.

    Maybe the name of the UDT can also be stored in the serialized Byte()...


    Code:
    ' used after the input arg is serialized to Byte array
    Sub DoDifferent_StuFF(any_ser_UDT() As Byte)
    
        Dim loc_Type1 As SomeUDT
        Dim loc_Type2 As AnotherUDT
        
        '!!**!! Which Type is it??
        [loc_Type1|loc_Type2] = Derserialize_2_UDT(any_ser_UDT)
        
        
        ' do different operations depending on the input type
        Select Case [which_type]
        
            Case which_type = SomeUDT
                '
                ' do stuff
                '
            
            Case which_type = AnotherUDT
                '
                ' do different stuff
                '
        End Select
    
    End Sub

  17. #17
    PowerPoster
    Join Date
    Jun 2013
    Posts
    7,219

    Re: Passing UDT as variant, for saving/loading UDTs

    Quote Originally Posted by Elroy View Post
    However, after examination, his approach does have the advantage of being able to raise an event for the caller.
    Yep... and the two Events are, what makes the routines within the Class-Code generically usable (in a way).

    The built-in VB6-serialization we make use of there,
    will always want the concrete UDT-Type(Symbol) in the VB6 Put- or Get statements -
    there's no way around that really (when you want to avoid "hacking" and much larger code-sources).

    So the two Events offer a simple way, to "isolate and move" the Put- and Get-calls into a specific "consumer"-Form or Class-
    where one can adjust to a specific UDT-Symbol in just a single extra-code-line (leaving the rest of the Code in the Helper-Class).

    And of course - a direct generic conversion-function would be nicer...
    but the Event-based approach requires only one single implementation line (a Put or Get call) -
    in addition to the Load- or Save- trigger calls on the Class-instance.

    Olaf

  18. #18
    PowerPoster wqweto's Avatar
    Join Date
    May 2011
    Location
    Sofia, Bulgaria
    Posts
    5,120

    Re: Passing UDT as variant, for saving/loading UDTs

    Put/Get on UDT must perform a lot of codegen underneath to execute actual [de]serialization.

    And that's probably the reason why this feature is still delayed in TwinBasic -- volumes of codegen for all (nested) data-types and edge cases *and* has to be compatible with VB6 undocumented format.

    Nowadays I would always go for JSON earlier on feature design phase, which means restricting interchanged data to nested Collections of primitive data-types.

    cheers,
    </wqw>

  19. #19
    The Idiot
    Join Date
    Dec 2014
    Posts
    2,721

    Re: Passing UDT as variant, for saving/loading UDTs

    loading and saving UDT is quite slow.
    comparing to parse the UDT, its many times slower.
    I remember using a loading/saving UDT method and eventually when the UDT was above 5MB it was taking 10 seconds to load/save.
    with the parsing method, it takes me under 1 second to load now and the UDT is at this moment 13MB.

    the parsing is quite easy to do but it can be quite long.
    I use something like: (example)

    Code:
    With MyUDT
    .exp = GetByte
    .Speed = GetInteger
    .Money = GetDecimal
    .ms = GetSingle
    .Name = GetString
    End With
    and each one is a call to a function, like:
    Code:
    Private Function GetDecimal() As Variant
        CopyMemory GetDecimal, data(Size), 16
        Size = Size + 16
    End Function
    and of course to "save" I do the reverse.
    example of my "decimal" set

    Code:
    Private Sub AddDecimal(SourceDecimal As Variant)
        CopyMemory data(Size), SourceDecimal, 16
        Size = Size + 16
    End Sub
    when data is all filled up, I simply save the entire array.
    Last edited by baka; Oct 30th, 2021 at 03:23 AM.

  20. #20
    Fanatic Member
    Join Date
    Aug 2016
    Posts
    597

    Re: Passing UDT as variant, for saving/loading UDTs

    I just done some fresh code in Serialize and Deserialize in C#:
    Code:
    private byte[] StructureToToByteArray<T>(T structure) where T : struct //Serialize
            {
    
                //var size = Marshal.SizeOf(structure.GetType());
                //var data = new byte[size];
                //IntPtr pnt = Marshal.AllocHGlobal(size);
                //try
                //{
                //    Marshal.StructureToPtr(structure, pnt, true);
                //    // Copy the array to unmanaged memory.
                //    Marshal.Copy(pnt, data, 0, size);
                //    return data;
                //}
                //finally
                //{
                //    // Free the unmanaged memory.
                //    Marshal.FreeHGlobal(pnt);
                //}
                var size = Marshal.SizeOf(typeof(T));
                byte[] buff = new byte[size];
                var handle = GCHandle.Alloc(buff, GCHandleType.Pinned);
                try
                {
                    Marshal.StructureToPtr(structure, handle.AddrOfPinnedObject(), false);
                    return buff;
                }
                finally
                {
                    handle.Free();
                }            
            }
    
            private T ByteArrayToStructure<T>(byte[] bytes) where T : struct //Deserialize
            {            
                var handle = GCHandle.Alloc(bytes, GCHandleType.Pinned);
                try
                {
                    //https://stackoverflow.com/questions/2871/reading-a-c-c-data-structure-in-c-sharp-from-a-byte-array
                    //https://stackoverflow.com/questions/6335153/casting-a-byte-array-to-a-managed-structure
                    return Marshal.PtrToStructure<T>(handle.AddrOfPinnedObject());               
                }
                finally
                {
                    handle.Free();
                }
                //unsafe T ByteArrayToStructure<T>(byte[] bytes) where T : struct
                //{
                //    fixed (byte* ptr = &bytes[0])
                //    {
                //        return (T)Marshal.PtrToStructure((IntPtr)ptr, typeof(T));
                //    }
                //}           
            }

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width