|
-
Aug 18th, 2021, 02:53 PM
#1
Re: TwinBasic
GUYS I HAVE FINALLY SOLVED IT!!!
Ok, to recap what I'm talking about for anyone just coming in. Yesterday I raised an issue with the Set keyword clashing with generics in TwinBASIC. Writing generic classes that perform assignments internally created situation where a generic class could not work for both classes and non-classes like Longs, Strings etc as the generic type. This was because assignment between class type variables needed to be performed with the Set keyword and assignment between non-class types must be performed without them. There was no way to perform an assignment that works with both types of assignment.
After much discussion in this thread about it, Wayne Phillips pointed out that I could use IsObject to test whether a variable was an object or not and the program can use that information to decide how to perform the assignment. I tried it and it seemed to work in a little scratch pad application I wrote to specifically test it. I thought I had a workable solution. I went into what I was actually using this for which is a List(Of T) and HashTable(Of String,TValue) implementation I'm working on which is going to be 100% written in TwinBASIC code with no external dependencies. When I went back to the List(Of T) code to implement the IsObject solution, it failed. Turns out that for some reason it doesn't work when the assignments involved arrays of type T. Having already spent the better part of the day already on this, I was too burnt out to start this all over again in an attempt to figure out why arrays are now a problem.
The overall problem was that the TwinBASIC compiler is determined not to allow assignments to be perform with the wrong statement, Let for non-objects variable and Set for object variables. If the compile even smells a possibility of this happening, it will complain. I did come up with a way around it by using a bunch on pointer tricks but they are just curiosities. These tricks are only good for showing off and not good for actual use. I went to bed last night defeated. I wrote it off generics as usable until this thing with Set could be sorted out.
But for some reason when I got up today, I decided to give it one more try and I don't know if it was the sleep I needed or what but in 5 minutes, I came up with a way to solve this problem entirely by removing Set from the equation altogether and it didn't involve any fancy tricks with pointers or anything like this. Turns out you can bypass Set completely with a very easy method. Use a function like this:-
Code:
Private Sub SetVarValue(ByRef varToSet As Variant, ByVal value As Variant)
varToSet = value
End Sub
I'm not even sure why that works as well as it does but it works. That function will allow you to perform an assignment to a variable regardless of if an Object is being assigned or not. Here is the full test program:-
Code:
Module MainModule
' This project type is set to 'Standard EXE' in the Settings file, so you need a Main() subroutine to run when the EXE is started.
Public Sub Main()
Dim objVal As TestClass = New TestClass
Dim strVal As String = "BOO!"
Dim lngVal As Long = 29
Dim objVar As TestClass
Dim strVar As String
Dim lngVar As Long
SetVarValue(objVar, objVal)
SetVarValue(strVar, strVal)
SetVarValue(lngVar, lngVal)
Debug.Print objVar.Value
Debug.Print strVar
Debug.Print lngVar
End Sub
Private Sub SetVarValue(ByRef varToSet As Variant, ByVal value As Variant)
varToSet = value
End Sub
End Module
Private Class TestClass
Public Sub new()
End Sub
Public Property Get Value() As String
Return "Class Property Value"
End Property
End Class
I popped that into the List(Of T) class I'm writing and it works. The List(Of T) class can now work with both class and non-class types with no problem. And it has no problem working with arrays.
-
Aug 18th, 2021, 03:36 PM
#2
Re: TwinBasic
 Originally Posted by Niya
GUYS I HAVE FINALLY SOLVED IT!!!
Ok, to recap what I'm talking about for anyone just coming in. Yesterday I raised an issue with the Set keyword clashing with generics in TwinBASIC. Writing generic classes that perform assignments internally created situation where a generic class could not work for both classes and non-classes like Longs, Strings etc as the generic type. This was because assignment between class type variables needed to be performed with the Set keyword and assignment between non-class types must be performed without them. There was no way to perform an assignment that works with both types of assignment.
After much discussion in this thread about it, Wayne Phillips pointed out that I could use IsObject to test whether a variable was an object or not and the program can use that information to decide how to perform the assignment. I tried it and it seemed to work in a little scratch pad application I wrote to specifically test it. I thought I had a workable solution. I went into what I was actually using this for which is a List(Of T) and HashTable(Of String,TValue) implementation I'm working on which is going to be 100% written in TwinBASIC code with no external dependencies. When I went back to the List(Of T) code to implement the IsObject solution, it failed. Turns out that for some reason it doesn't work when the assignments involved arrays of type T. Having already spent the better part of the day already on this, I was too burnt out to start this all over again in an attempt to figure out why arrays are now a problem.
The overall problem was that the TwinBASIC compiler is determined not to allow assignments to be perform with the wrong statement, Let for non-objects variable and Set for object variables. If the compile even smells a possibility of this happening, it will complain. I did come up with a way around it by using a bunch on pointer tricks but they are just curiosities. These tricks are only good for showing off and not good for actual use. I went to bed last night defeated. I wrote it off generics as usable until this thing with Set could be sorted out.
But for some reason when I got up today, I decided to give it one more try and I don't know if it was the sleep I needed or what but in 5 minutes, I came up with a way to solve this problem entirely by removing Set from the equation altogether and it didn't involve any fancy tricks with pointers or anything like this. Turns out you can bypass Set completely with a very easy method. Use a function like this:-
Code:
Private Sub SetVarValue(ByRef varToSet As Variant, ByVal value As Variant)
varToSet = value
End Sub
I'm not even sure why that works as well as it does but it works. That function will allow you to perform an assignment to a variable regardless of if an Object is being assigned or not. Here is the full test program:-
Code:
Module MainModule
' This project type is set to 'Standard EXE' in the Settings file, so you need a Main() subroutine to run when the EXE is started.
Public Sub Main()
Dim objVal As TestClass = New TestClass
Dim strVal As String = "BOO!"
Dim lngVal As Long = 29
Dim objVar As TestClass
Dim strVar As String
Dim lngVar As Long
SetVarValue(objVar, objVal)
SetVarValue(strVar, strVal)
SetVarValue(lngVar, lngVal)
Debug.Print objVar.Value
Debug.Print strVar
Debug.Print lngVar
End Sub
Private Sub SetVarValue(ByRef varToSet As Variant, ByVal value As Variant)
varToSet = value
End Sub
End Module
Private Class TestClass
Public Sub new()
End Sub
Public Property Get Value() As String
Return "Class Property Value"
End Property
End Class
I popped that into the List(Of T) class I'm writing and it works. The List(Of T) class can now work with both class and non-class types with no problem. And it has no problem working with arrays.
Don't get too excited... that looks like a bug in tB, as it should be trying to call the default prop-let member in that assignment inside SetVarValue if the LHS is an object type. That will be fixed very soon, and then your code will break.
You could put the IsObject check into the SetVarValue call to switch between the two types of assignments, and that should satisfy the compiler and be correct. Though of course you are passing the values through Variants for that to work, which as @wqweto mentioned earlier, goes against what you're trying to achieve by using generics.
Code:
Private Sub SetVarValue(ByRef varToSet As Variant, ByVal Value As Variant)
If IsObject(varToSet) Then
Set varToSet = Value
Else
varToSet = Value
End If
End Sub
Last edited by WaynePhillipsEA; Aug 18th, 2021 at 03:44 PM.
-
Aug 18th, 2021, 04:45 PM
#3
Re: TwinBasic
 Originally Posted by WaynePhillipsEA
Don't get too excited... that looks like a bug in tB, as it should be trying to call the default prop-let member in that assignment inside SetVarValue if the LHS is an object type. That will be fixed very soon, and then your code will break.
You could put the IsObject check into the SetVarValue call to switch between the two types of assignments, and that should satisfy the compiler and be correct.
I would be totally fine with that IsObject logic as long as it prevents the compiler from interfering with what I'm trying to do. The real value here is being able to use Variants on both sides so that the compiler doesn't try to tell me what I can and cannot do. With Variants involved on both sides of the assignment, the compiler cannot make any assumptions, it has to be evaluated at runtime for correctness, which is exactly what I want. The other big win here is that I don't have to refactor my class code, all I have to do is use the function where I'm performing assignments that involve type T. That's a very minor cost to pay in my opinion.
 Originally Posted by WaynePhillipsEA
Though of course you are passing the values through Variants for that to work, which as @wqweto mentioned earlier, goes against what you're trying to achieve by using generics.
This is true. However, I'm willing to live with that. Generics provide more value than just performance. I'd argue that the bigger pay off is type safety. I'm willing to sacrifice a bit of performance for the type safety that generics give you. If I have a list of Longs, I only want Longs in that list and I would like the compiler to stop me if I ever make this mistake of trying to put in a String. This is a big win for me that helps prevent all kinds of errors in code. If I have to lose a few clock cycles for it, so be it.
This is the sole reason I hate JavaScript with its duck typing nonsense. It spoils an otherwise very beautiful language. It puts too much of a burden on you to always make sure you're using the right data types. There is a lot of value in a compiler that has ways of easing this burden so I can put my mental more towards what I'm actually doing rather than how I'm doing it. This is just my opinion though, there is no right or wrong here. It's just preferences.
 Originally Posted by Eduardo-
BTW, this code:
Code:
SetVarValue(objVar, objVal)
SetVarValue(strVar, strVal)
SetVarValue(lngVar, lngVal)
is also not allowed in VB6 (arguments in parentheses without the Call keyword).
Oh boy. This is another big discussion. I really don't want to go down that path right now. I'll just say this, like Set, this is another one of those garbage things present in the VB6 language that makes zero sense and adds no value. No programming language I know of would allow something like that. I'm a huge fan of consistency in a programming language, and a function/sub call should always be in brackets. I'm only speculating here but I suspect that VB6 inherited that nonsense from QuickBasic to support Print and statements like it so you can do stuff like this:-
Code:
Debug.Print "This is ";
Debug.Print "one line"
Requiring brackets there would make the syntax look too cluttered. Of course I'm just speculating here. I really don't know why BASIC was the only mainstream language that allowed this. Suffice to say, it's confusing as hell when you start switching between writing code in BASIC and some other language such as C. Sometimes you bring back the habit of bracketing your function calls to BASIC and it can become annoying when the compiler stops you and demand that you fix it. I've experienced it and still do to this day when switching between VB6 and VB.Net which is consistent about function call syntax.
-
Aug 18th, 2021, 07:32 PM
#4
Re: TwinBasic
 Originally Posted by WaynePhillipsEA
You could put the IsObject check into the SetVarValue call to switch between the two types of assignments, and that should satisfy the compiler and be correct.
Ok I just tested this and it doesn't work. Whenever Set is used to perform an assignment to a ByRef argument, the value doesn't propagate back to the variable that was passed through that ByRef argument. Guess it's back to the drawing board.
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
|