FB should support covariant type parameters for the overriding methods?
fxm wrote:dkl thank you for the filling this feature request:
#289 Covariant parameters and function results
When overriding virtual methods, I guess that you also propose the ability of covariant type parameters in order (in addition to covariant return type) to also avoid the unsafe down-castings inside the overriding method body.
I wonder why C++ and Java support the covariance only for function returns (is there an additional difficulty for parameters compared to function returns, when passing or returning by reference or pointer?)!
Eiffel supports the covariance for the function returns and the parameters.
1) Example with two independant inheritance structures:
- animal <- cat, dog
- Object <- animalFarming <- catFarming, dogFarming
Code: Select all
Type animal
Declare Constructor (Byref s As String = "")
Dim As String species
End Type
Constructor animal (Byref s As String = "")
This.species = s
End Constructor
Type cat Extends animal
Declare Constructor ()
Declare Constructor (Byref c As cat)
Declare Destructor ()
Static As Integer nb
End Type
Dim As integer cat.nb
Constructor cat ()
Base("cat")
This.nb += 1
End Constructor
Constructor cat (Byref c As cat)
Base(c.species)
This.nb += 1
End Constructor
Destructor cat ()
This.nb -= 1
End Destructor
Type dog Extends animal
Declare Constructor ()
Declare Constructor (Byref d As dog)
Declare Destructor ()
Static As Integer nb
End Type
Dim As integer dog.nb
Constructor dog ()
Base("dog")
This.nb += 1
End Constructor
Constructor dog (Byref d As dog)
Base(d.species)
This.nb += 1
End Constructor
Destructor dog ()
This.nb -= 1
End Destructor
Type animalFarming Extends Object
Declare Abstract Function clone (Byref a As animal) As animal Ptr
End Type
Type catFarming Extends animalFarming
' Declare Function clone (Byref c As cat) As cat Ptr Override '' with covariance
Declare Function clone (Byref a As animal) As animal Ptr Override '' without covariance
End Type
'Function catFarming.clone (Byref c As cat) As cat Ptr '' with covariance
Function catFarming.clone (Byref a As animal) As animal Ptr '' without covariance
' Return New cat(c) '' with covariance
Return New cat(*Cast(cat Ptr, @a)) '' without covariance
End Function
Type dogFarming Extends animalFarming
' Declare Function clone (Byref d As dog) As dog Ptr Override '' with covariance
Declare Function clone (Byref a As animal) As animal Ptr Override '' without covariance
End Type
'Function dogFarming.Clone (Byref d As dog) As dog Ptr '' with covariance
Function dogFarming.Clone (Byref a As animal) As animal Ptr '' without covariance
' Return New dog(d) '' with covariance
Return New dog(*Cast(dog Ptr, @a)) '' without covariance
End Function
Dim As cat c
Print c.species & " #" & c.nb
Dim As dog d
Print d.species & " #" & d.nb
Print
Dim As catFarming cf
Dim As dogFarming df
Dim As animalFarming Ptr paf
Print "catFarming:",
paf = @cf
Dim As cat Ptr pcc
'pcc = paf->clone(c) '' with covariance
pcc = Cast(cat Ptr, paf->clone(c)) '' without covariance
Print pcc->species & " #" & pcc->nb
Print "dogFarming:",
paf = @df
Dim As dog Ptr pdc
'pdc = paf->clone(d) '' with covariance
pdc = Cast(dog Ptr, paf->clone(d)) '' without covariance
Print pdc->species & " #" & pdc->nb
Sleep
Delete pcc
Delete pdc
Code: Select all
cat #1
dog #1
catFarming: cat #2
dogFarming: dog #2
But without covariant type parameter supported, an unsafe down-casting must be added inside the overriding method body, and then nothing in the code above can forbid:
- to clone a cat with a dog in catFarming:
[89] pcc = Cast(cat Ptr, paf->clone(d))
- to clone a dog with a cat in dogFarming:
[95] pdc = Cast(dog Ptr, pdf->clone(c))
Code: Select all
cat #1
dog #1
catFarming: dog #2
dogFarming: cat #2
2) In order to safe the code (without covariant type parameter supported), the overriding method bodies must be complemented (5 lines instead of 1 line) by checking the real reference types before to authorize cloning:
Code: Select all
Type animal Extends Object
Declare Constructor (Byref s As String = "")
Dim As String species
End Type
Constructor animal (Byref s As String = "")
This.species = s
End Constructor
Type cat Extends animal
Declare Constructor ()
Declare Constructor (Byref c As cat)
Declare Destructor ()
Static As Integer nb
End Type
Dim As Integer cat.nb
Constructor cat ()
Base("cat")
This.nb += 1
End Constructor
Constructor cat (Byref c As cat)
Base(c.species)
This.nb += 1
End Constructor
Destructor cat ()
This.nb -= 1
End Destructor
Type dog Extends animal
Declare Constructor ()
Declare Constructor (Byref d As dog)
Declare Destructor ()
Static As Integer nb
End Type
Dim As Integer dog.nb
Constructor dog ()
Base("dog")
This.nb += 1
End Constructor
Constructor dog (Byref d As dog)
Base(d.species)
This.nb += 1
End Constructor
Destructor dog ()
This.nb -= 1
End Destructor
Type animalFarming Extends Object
Declare Abstract Function clone (Byref a As animal) As animal Ptr
End Type
Type catFarming Extends animalFarming
' Declare Function clone (Byref c As cat) As cat Ptr Override '' with covariance
Declare Function clone (Byref a As animal) As animal Ptr Override '' without covariance
End Type
'Function catFarming.clone (Byref c As cat) As cat Ptr '' with covariance
Function catFarming.clone (Byref a As animal) As animal Ptr '' without covariance
' Return New cat(c) '' with covariance
Return Iif(a Is cat, New cat(*Cast(cat Ptr, @a)), 0) '' without covariance
End Function
Type dogFarming Extends animalFarming
' Declare Function clone (Byref d As dog) As dog Ptr Override '' with covariance
Declare Function clone (Byref a As animal) As animal Ptr Override '' without covariance
End Type
'Function dogFarming.Clone (Byref d As dog) As dog Ptr '' with covariance
Function dogFarming.Clone (Byref a As animal) As animal Ptr '' without covariance
' Return New dog(d) '' with covariance
Return Iif(a Is dog, New dog(*Cast(dog Ptr, @a)), 0) '' without covariance
End Function
Dim As cat c
Print c.species & " #" & c.nb
Dim As dog d
Print d.species & " #" & d.nb
Print
Dim As catFarming cf
Dim As dogFarming df
Dim As animalFarming Ptr paf
Print "catFarming:",
paf = @cf
Dim As cat Ptr pcc
'pcc = paf->clone(c) '' with covariance
'Print pcc->species & " #" & pcc->nb '' with covariance
pcc = Cast(cat Ptr, paf->clone(c)) '' without covariance
If pcc Then Print pcc->species & " #" & pcc->nb Else Print '' without covariance
Print "dogFarming:",
paf = @df
Dim As dog Ptr pdc
'pdc = paf->clone(d) '' with covariance
'Print pdc->species & " #" & pdc->nb '' with covariance
pdc = Cast(dog Ptr, paf->clone(d)) '' without covariance
If pdc Then Print pdc->species & " #" & pdc->nb Else Print '' without covariance
Sleep
Delete pcc
Delete pdc
Code: Select all
cat #1
dog #1
catFarming: cat #2
dogFarming: dog #2
By checking the real reference types before authorizing the cloning, this avoids:
- to clone a cat with a dog in catFarming:
[90] pcc = Cast(cat Ptr, pcf->clone(d))
- to clone a dog with a cat in dogFarming:
[97] pdc = Cast(dog Ptr, pdf->clone(c))
Code: Select all
cat #1
dog #1
catFarming:
dogFarming:
Urges the override covariance for return and parameter types!
2bis) As (fortunately) a covariant type parameter is also requested (in addition to covariant type return), a more simple workaround exists which is to replace the base abstract function by several virtual specialized (with the requested subtypes) functions.
To forbid base object constructions (because of base function no longer abstract), a constructor and a copy-constructor with protected access can be added:
Code: Select all
Type animal
Declare Constructor (Byref s As String = "")
Dim As String species
End Type
Constructor animal (Byref s As String = "")
This.species = s
End Constructor
Type cat Extends animal
Declare Constructor ()
Declare Constructor (Byref c As cat)
Declare Destructor ()
Static As Integer nb
End Type
Dim As integer cat.nb
Constructor cat ()
Base("cat")
This.nb += 1
End Constructor
Constructor cat (Byref c As cat)
Base(c.species)
This.nb += 1
End Constructor
Destructor cat ()
This.nb -= 1
End Destructor
Type dog Extends animal
Declare Constructor ()
Declare Constructor (Byref d As dog)
Declare Destructor ()
Static As Integer nb
End Type
Dim As integer dog.nb
Constructor dog ()
Base("dog")
This.nb += 1
End Constructor
Constructor dog (Byref d As dog)
Base(d.species)
This.nb += 1
End Constructor
Destructor dog ()
This.nb -= 1
End Destructor
Type animalFarming Extends Object
' Declare Abstract Function clone (Byref a As animal) As animal Ptr '' with covariance
Declare Virtual Function clone (Byref c As cat) As cat Ptr '' without covariance
Declare Virtual Function clone (Byref d As dog) As dog Ptr '' without covariance
Protected: '' without covariance
Declare Constructor () '' without covariance
Declare Constructor (Byref af As animalFarming) '' without covariance
End Type
Virtual Function animalFarming.clone (Byref c As cat) As cat Ptr
Return 0
End Function
Virtual Function animalFarming.clone (Byref d As dog) As dog Ptr
Return 0
End Function
Constructor animalFarming ()
End Constructor
Type catFarming Extends animalFarming
Declare Function clone (Byref c As cat) As cat Ptr Override
End Type
Function catFarming.clone (Byref c As cat) As cat Ptr
Return New cat(c)
End Function
Type dogFarming Extends animalFarming
Declare Function clone (Byref d As dog) As dog Ptr Override
End Type
Function dogFarming.Clone (Byref d As dog) As dog Ptr
Return New dog(d)
End Function
Dim As cat c
Print c.species & " #" & c.nb
Dim As dog d
Print d.species & " #" & d.nb
Print
Dim As catFarming cf
Dim As dogFarming df
Dim As animalFarming Ptr paf
Print "catFarming:",
paf = @cf
Dim As cat Ptr pcc
pcc = paf->clone(c)
'Print pcc->species & " #" & pcc->nb '' with covariance
If pcc Then Print pcc->species & " #" & pcc->nb Else Print '' without covariance
Print "dogFarming:",
paf = @df
Dim As dog Ptr pdc
pdc = paf->clone(d)
'Print pdc->species & " #" & pdc->nb '' with covariance
If pdc Then Print pdc->species & " #" & pdc->nb Else Print '' without covariance
Sleep
Delete pcc
Delete pdc
Code: Select all
cat #1
dog #1
catFarming: cat #2
dogFarming: dog #2
By assigning specialized pointers, this avoids:
- to clone a cat with a dog in catFarming:
[96] pcc = pcf->clone(d)
- to clone a dog with a cat in dogFarming:
[105] pdc = pdf->clone(c)
Code: Select all
Compiler output:
.....\FBIDETEMP.bas(96) error 180: Invalid assignment/conversion in 'pcc = paf->clone(d)'
.....\FBIDETEMP.bas(105) error 180: Invalid assignment/conversion in 'pdc = paf->clone(c)'
[edit]
- Code complemented (§2) to obtain a safer version without covariance supported.
- Added paragraph (§2bis).
- See the other version at
http://www.freebasic.net/forum/viewtopi ... 21#p202321 (by using a macro for 'dynamic cast to pointer').