Currently a 'With..End With' block can support a temporary instance of a Type !

General FreeBASIC programming questions.
fxm
Moderator
Posts: 12110
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Currently a 'With..End With' block can support a temporary instance of a Type !

Post by fxm »

@Jeff, and the others,

'With...End With' block:

- Internally, a reference to the variable is taken at the start of the 'With' block, and then is used to calculate any element accesses within the block.

- But, normally, creating a reference (by 'Dim Byref...') to a temporary variable is forbidden.


Examples:

- With a local instance:

Code: Select all

Type UDT
    Dim As Integer I
    Dim As Integer J
End Type

Dim As UDT u = Type<UDT>(1, 2)

With u
    Print .I
    Print .J
End With
is equivalent to:

Code: Select all

Type UDT
    Dim As Integer I
    Dim As Integer J
End Type

Dim As UDT u = Type<UDT>(1, 2)

Scope
    Dim Byref As UDT ref = u
    Print ref.I
    Print ref.J
End Scope

- With a temporary instance:

Code: Select all

Type UDT
    Dim As Integer I
    Dim As Integer J
    Declare Constructor(Byval i0 As Integer, Byval j0 As Integer)
End Type

Constructor UDT(Byval i0 As Integer, Byval j0 As Integer)
    This.I = i0
    This.J = j0
End Constructor

With UDT(1, 2)
    Print .I
    Print .J
End With
should normally be equivalent to:

Code: Select all

Type UDT
    Dim As Integer I
    Dim As Integer J
    Declare Constructor(Byval i0 As Integer, Byval j0 As Integer)
End Type

Constructor UDT(Byval i0 As Integer, Byval j0 As Integer)
    This.I = i0
    This.J = j0
End Constructor

Scope
    Dim Byref As UDT ref = UDT(1, 2)  '' error 24: Invalid data types
    Print ref.I
    Print ref.J
End Scope

Is this 'With...End With' functionality desired for a temporary instance and will therefore be maintained in future versions ?
fxm
Moderator
Posts: 12110
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Currently a 'With..End With' block can support a temporary instance of a Type !

Post by fxm »

Otherwise, this current functionality is safe because the temporary instance is only destroyed at the end of the 'With' block:

Code: Select all

Type UDT
    Dim As Integer I = 1
    Dim As Integer J = 2
    Declare Constructor()
    Declare Destructor()
End Type

Constructor UDT()
    Print "constructor", @This
End Constructor

Destructor UDT()
    Print "destructor", @This
End destructor

With UDT
    Print "-----"
    Print .I
    Print .J
    Print "-----"
End With

Sleep

Code: Select all

constructor   1703572
-----
 1
 2
-----
destructor    1703572
fxm
Moderator
Posts: 12110
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Currently a 'With..End With' block can support a temporary instance of a Type !

Post by fxm »

fxm wrote: Feb 16, 2023 7:04 ... normally, creating a reference (by 'Dim Byref...') to a temporary variable is forbidden.

On the other hand, one can create a pointer to such a temporary instance kind:

Code: Select all

Type UDT
    Dim As Integer I
    Dim As Integer J
    Declare Constructor(Byval i0 As Integer, Byval j0 As Integer)
End Type

Constructor UDT(Byval i0 As Integer, Byval j0 As Integer)
    This.I = i0
    This.J = j0
End Constructor

Dim As UDT Ptr p = @UDT(1, 2)
fxm
Moderator
Posts: 12110
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Currently a 'With..End With' block can support a temporary instance of a Type !

Post by fxm »

fxm wrote: Feb 16, 2023 7:04 Is this 'With...End With' functionality desired for a temporary instance and will therefore be maintained in future versions ?

If this current functionality is confirmed for future revisions (with temporary instance only destroyed at the end of the 'While' block), the WITH documentation page could be updated as follows:
.....
It can be used as a shorthand to save typing and avoid cluttering the source. With can also be used with dereferenced pointers, as the second example shows. With can also be used with a temporary instance because the temporary instance is only destroyed at the end of the With block.
.....
Internally, a reference to the address of the variable is taken at the start of the With block, and then is used to calculate any element accesses within the block. Note that this means that Goto should not be used to jump into a With block, otherwise the reference address will not have been set, and the results of trying to access it will be undefined.
.....
adeyblue
Posts: 300
Joined: Nov 07, 2019 20:08

Re: Currently a 'With..End With' block can support a temporary instance of a Type !

Post by adeyblue »

Why is this a problem? Looks like a perfect way to emulate c# using statement, which is very useful and good for things like this

Code: Select all

Type ScopedLock
    Dim As Any Ptr m
    Declare Constructor(ByVal newtex as Any Ptr)
    Declare Destructor()
End Type

Constructor ScopedLock(ByVal newtex as Any Ptr)
    m = newtex
    MutexLock(m)
End Constructor

Destructor ScopedLock()
    MutexUnlock(m)
End destructor

Sub FantasticallyInterestingSub(Byval mutex as Any Ptr)

    '' interesting code

    With ScopedLock(mutex)
        if Rnd < 0.5 Then Exit Sub '' mutex unlocked on early exit

        '' other super interesting code

    End With '' also unlocked at normal exit
    
End Sub

dim as Any Ptr m  = MutexCreate()
FantasticallyInterestingSub(m)
MutexDestroy(m)
If you disallow that, you also have to disallow this

Code: Select all

Sub Test(ByRef lck as ScopedLock)

End Sub

Test(ScopedLock(m))
Because it's the same thing - the compiler extending the life of a temporary and letting you use a reference to it. This isn't a failure of the compiler, in this case, it's the manual overstepping its authority. The manual shouldn't concern itself with how things work inside, just how to use them. It's supposed to be a description of Freebasic, not FBC. This is all those two paragraphs need to say
It can be used as a shorthand to save typing and avoid cluttering the source. With can also be used with dereferenced pointers, as the second example shows; and temporary objects, as in the third.

Do not use Goto to jump into a With block, otherwise the results of trying to access the variable will be undefined.
And then add a third example showing it.
fxm
Moderator
Posts: 12110
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Currently a 'With..End With' block can support a temporary instance of a Type !

Post by fxm »

adeyblue wrote: Feb 16, 2023 21:10 Why is this a problem? Looks like a perfect way to emulate c# using statement, which is very useful and good for things like this
.....
If you disallow that, you also have to disallow this
.....

Where do you see that I ask to remove this present feature.
I only ask if this feature will be safely maintained in future revisions, and if so to update the documentation.
fxm
Moderator
Posts: 12110
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Currently a 'With..End With' block can support a temporary instance of a Type !

Post by fxm »

adeyblue wrote: Feb 16, 2023 21:10 The manual shouldn't concern itself with how things work inside, just how to use them. It's supposed to be a description of Freebasic, not FBC.

I do not conceive of each page of the manual as a simple cooking recipe card.
To better assimilate the "how", it is necessary to understand the "why" a little and therefore to enter a little into the internal functioning.

If I take the threading keywords as an example, if only the "how" to use them were described without going into the "why" a bit, I do not see how an ordinary user could use them in the environment of their own programs.
fxm
Moderator
Posts: 12110
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Currently a 'With..End With' block can support a temporary instance of a Type !

Post by fxm »

fxm wrote: Feb 16, 2023 9:15 Otherwise, this current functionality is safe because the temporary instance is only destroyed at the end of the 'With' block:

Code: Select all

Type UDT
    Dim As Integer I = 1
    Dim As Integer J = 2
    Declare Constructor()
    Declare Destructor()
End Type

Constructor UDT()
    Print "constructor", @This
End Constructor

Destructor UDT()
    Print "destructor", @This
End destructor

With UDT
    Print "-----"
    Print .I
    Print .J
    Print "-----"
End With

Sleep

Code: Select all

constructor   1703572
-----
 1
 2
-----
destructor    1703572

1) This functionality is safe for a temporary instance which is really constructed (implicitly or explicitly), but not for a simple temporary type as follows:
  • Code: Select all

    Type UDT
        Dim As Integer I
        Dim As Integer J
        Declare Destructor()
    End Type
    
    Destructor UDT()
        This.I = 0
        This.J = 0
        Print "destructor"
    End destructor
    
    With Type<UDT>(1, 2)  '' temporary type created here not maintained up to the end of the 'With...End With' block
        Print "-----"
        Print .I
        Print .J
        Print "-----"
    End With
    
    Sleep
    

    Code: Select all

    destructor
    -----
     0
     0
    -----
    
    In this case, the temporary type is not held up to the end of the 'With...End With' block, but "destroyed" after the 'With' line.
2) Otherwise, by only adding a constructor (explicitly in the below case), the functionality becomes safe (in this case with constructor, 'Type<UDT>(1, 2)' is in fact an alias of 'UDT(1, 2)'):
  • Code: Select all

    Type UDT
        Dim As Integer I
        Dim As Integer J
        Declare Constructor(Byval i0 As Integer, Byval j0 As Integer)
        Declare Destructor()
    End Type
    
    Constructor UDT(Byval i0 As Integer, Byval j0 As Integer)
        Print "constructor"
        This.I = i0
        This.J = j0
    End Constructor
    
    Destructor UDT()
        This.I = 0
        This.J = 0
        Print "destructor"
    End destructor
    
    With Type<UDT>(1, 2)  '' temporary instance created here maintained up to the end of the 'With...End With' block
        Print "-----"
        Print .I
        Print .J
        Print "-----"
    End With
    
    Sleep
    

    Code: Select all

    constructor
    -----
     1
     2
    -----
    destructor
    
    In this case, the temporary instance is held up to the end of the 'With...End With' block.

We can clearly see here on these 2 examples that things are not as simple as one might think or say.
Lost Zergling
Posts: 538
Joined: Dec 02, 2011 22:51
Location: France

Re: Currently a 'With..End With' block can support a temporary instance of a Type !

Post by Lost Zergling »

To better assimilate the "how", it is necessary to understand the "why" a little and therefore to enter a little into the internal functioning.
I fully subscribe to this spirit.
fxm
Moderator
Posts: 12110
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Currently a 'With..End With' block can support a temporary instance of a Type !

Post by fxm »

The WITH documentation page could be updated as follows:
.....
It can be used as a shorthand to save typing and avoid cluttering the source.
'With' can also be used with dereferenced pointers, as the second example shows, and with temporary instances (with explicit or implicit constructor) as the third example (because destruction of such temporary instances is deferred to the end of the 'With' scope).
Temporary types (without any constructor) are not supported (because destruction of such temporary types would be called immediately after the 'With' statement).

.....
Internally, a reference to the address of the variable is taken at the start of the With block, and then is used to calculate any element accesses within the block.
Note that this means that Goto should not be used to jump into a With block, otherwise the reference address will not have been set, and the results of trying to access it will be undefined.
.....
+ insertion of a third example (with temporary instance).
coderJeff
Site Admin
Posts: 4326
Joined: Nov 04, 2005 14:23
Location: Ontario, Canada
Contact:

Re: Currently a 'With..End With' block can support a temporary instance of a Type !

Post by coderJeff »

I think we should want WITH TYPE<UDT>(...) to be valid no matter if it has an implicit or explicit constructor.

One of the challenges is that implicit constructors (assignments built by the compiler) and explicit constructors (procedure call built by the compiler) are two different kinds internal data structures and each need to be handled. There is much in common, but enough differences that we can see behaviour differences between them when maybe we shouldn't because not is all handled - either make it work or throw an error.

Internally, both UDT's with implicit and explicit constructor:
- implicitly create a temporary variable
- initialize the temporary variable
- allow statements inside the WITH block to access that temporary variable

The difference is that:
1) the destructor call for the UDT with explicit constructor is deferred to the end of the WITH scope (the lifetime of all temporary variables involved are extended to the END WITH statement)
2) the destructor call for the UDT with implicit constructor / explicit destructor is called immediately after the WITH statement
3) a temporary UDT with neither explicit constructor / destructor is not allowed
3) a temporary UDT with neither explicit constructor / destructor is not allowed when no initializers given - but this is currently the case for most if not all uses of TYPE<UDT>() as an expression for a temporary UDT of this kind (DIM initializer, argument passing, etc).

After having a quick look at compiler source, 2 looks tricky to solve to make behave like 1. But if 2 is solved, then I think maybe 3 will be easier to make work like 1 & 2.
SARG
Posts: 1766
Joined: May 27, 2005 7:15
Location: FRANCE

Re: Currently a 'With..End With' block can support a temporary instance of a Type !

Post by SARG »

Nothing simple :wink:

This code causes a crash. Due to an implicit pointer to the temporary variable not assigned . However there is a warning : "test2.bas(7) warning 14(2): Branch crossing local variable definition, to label: LABEL1, variable: LT_0007

Code: Select all

Type UDT
    Dim As Integer I
    Dim As Integer J
End Type

print "before goto"
goto label1:
With Type<UDT>(1, 2)
    Print .I
    label1:
    Print .J
End With
print "after with"
But this one is ok, no really address or reference taken, direct address in the code.

Code: Select all

Type UDT2
    Dim As Integer I
    Dim As Integer J
End Type

dim as udt2 u
u.i=78
u.j=79
print "before goto"
goto label2:
print "with u skipped ?"
with u
	print .i
	label2:
	print .j
End With
print "after with"

Sleep
fxm
Moderator
Posts: 12110
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Currently a 'With..End With' block can support a temporary instance of a Type !

Post by fxm »

coderJeff wrote: Feb 19, 2023 13:48 I think we should want WITH TYPE<UDT>(...) to be valid no matter if it has an implicit or explicit constructor.

I did not dare ask !
fxm
Moderator
Posts: 12110
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Currently a 'With..End With' block can support a temporary instance of a Type !

Post by fxm »

@Jeff,

I personally have a problem with the terminology relating to "explicit" or "implicit" constructor or "no" constructor:

- For me, the following example corresponds to "explicit" constructor for 'Type<UDT>(1)' because 'UDT(1)' works:

Code: Select all

Type UDT
    Dim As Integer I
    Declare Constructor(Byval i0 As Integer)
    Declare Destructor()
End Type

Constructor UDT(Byval i0 As Integer)
    This.I = i0
End Constructor

Destructor UDT()
    This.I = 0
    Print "destructor"
End Destructor

With Type<UDT>(1)
    Print "-----"
    Print .I
    Print "-----"
End With

Sleep

Code: Select all

-----
 1
-----
destructor

- For me, the following example corresponds to "implicit" constructor for 'Type<UDT>()' because 'UDT()' works:

Code: Select all

Type UDT
    Dim As Integer I = 1
    Declare Destructor()
End Type

Destructor UDT()
    This.I = 0
    Print "destructor"
End Destructor

With Type<UDT>()
    Print "-----"
    Print .I
    Print "-----"
End With

Sleep

Code: Select all

-----
 1
-----
destructor

- For me, the following example corresponds to "no" constructor for 'Type<UDT>(1)' because 'UDT(1)' does not work:

Code: Select all

Type UDT
    Dim As Integer I
    Declare Destructor()
End Type

Destructor UDT()
    This.I = 0
    Print "destructor"
End Destructor

With Type<UDT>(1)
    Print "-----"
    Print .I
    Print "-----"
End With

Sleep

Code: Select all

destructor
-----
 0
-----
coderJeff
Site Admin
Posts: 4326
Joined: Nov 04, 2005 14:23
Location: Ontario, Canada
Contact:

Re: Currently a 'With..End With' block can support a temporary instance of a Type !

Post by coderJeff »

fxm wrote: Feb 19, 2023 14:54 I personally have a problem with the terminology relating to "explicit" or "implicit" constructor or "no" constructor:
ya, seems reasonable to me as well.
explicit - constructor procedure written by user
implicit - constructor procedure generated by compiler
no constructor - inline assignments generated by compiler

I feel like the "WITH TYPE<UDT>(...)" and "WITH UDT(...)" should match up with initializers where possible - and obviously not the empty or "= (...)" cases.

However, throwing an error for "WITH TYPE<UDT>(...)" when no constructor exists seems like a simple way to manage something that doesn't work now and (is probably) hard to make work.

Code: Select all

'' explicit constructor procedure written by user
type udt1
	b as byte
	c as byte
	declare constructor( byval a1 as byte = 1, byval a2 as byte = 2)
end type
constructor udt1( byval a1 as byte, byval a2 as byte )
end constructor
scope
	'' arguments are optional if constructor is delcared with optional args
	dim x1 as udt1      
	dim x2 as udt1 = (1)  
	dim x3 as udt1 = (1,2)            '' error - why?  
	dim x4 as udt1 = UDT1()      
	dim x5 as udt1 = UDT1(1)  
	dim x6 as udt1 = UDT1(1,2)  
	dim x7 as udt1 = type<UDT1>()      
	dim x8 as udt1 = type<UDT1>(1)  
	dim x9 as udt1 = type<UDT1>(1,2)  
end scope

'' implicit constructor procedure generated by compiler
type udt2
	b as byte = 1
	c as byte = 2
end type
scope
	'' initializer arguments not allowed since only default constructor
	'' is implicitly generated
	dim x1 as udt2      
	dim x2 as udt2 = (1)              '' error  
	dim x3 as udt2 = (1,2)            '' error  
	dim x4 as udt2 = UDT2()      
	dim x5 as udt2 = UDT2(1)          '' error  
	dim x6 as udt2 = UDT2(1,2)        '' error  
	dim x7 as udt2 = type<UDT2>()      
	dim x8 as udt2 = type<UDT2>(1)    '' error  
	dim x9 as udt2 = type<UDT2>(1,2)  '' error  
end scope

'' no constructor, no procedure call
type udt3
	b as byte
	c as byte
end type
scope
	'' at least one initializer argument required, rest are optional
	'' initializer code is implicitly generated by compiler inline 	
	dim x1 as udt3  
	dim x2 as udt3 = (1)  
	dim x3 as udt3 = (1,2)  
	dim x4 as udt3 = UDT3()           '' error - no constructor procedure  
	dim x5 as udt3 = UDT3(1)          '' error - no constructor procedure  
	dim x6 as udt3 = UDT3(1,2)        '' error - no constructor procedure  
	dim x7 as udt3 = type<UDT3>()     '' error - empty list not allowed  
	dim x8 as udt3 = type<UDT3>(1)  
	dim x9 as udt3 = type<UDT3>(1,2)  
end scope
Post Reply