Conversion/Inheritance relating to UDTs via constructors and operators

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

Conversion/Inheritance relating to UDTs via constructors and operators

Post by fxm »

Conversion

I took advantage of the 'BUG : Illicit "error 202: Illegal member access, UDT.operator.cast" compiler error message' analysis to redo and complete all the conversion cases relating to UDTs.

I also updated and completed the below paragraph of the Coercion and Conversion (Variables and Datatypes) documentation page of the Programmer's Guide:
  • .....
Conversions using User Data Type constructors and operators
  • For conversion between built-in types (among standard types like between numeric types as above or between string types), the compiler knows what to do without the need for instructions from user.
    This is called the implicit internal conversion (or coercion).

    When one of the two types is at least a UDT (User Defined Type), the user has to code some UDT procedures to define how do the conversion.
    Then, the conversion execution can be explicit if the user specifies what UDT procedure must be used, or implicit if the user leaves the choice to compiler.

    In the world of UDTs, conversions can be controlled by means of three member procedures:
    • - Single-argument constructor: allow conversion from a particular type to initialize an object.
      - Assignment operator: allow conversion from a particular type on assignment.
      - Type-cast operator: allow conversion to a particular type.
    For a construction with implicit initialization ('Dim As UDT u = v'), the compiler searches by priority:
    • - Firstly a matched constructor (for u type from v type).
      - Secondly a cast operator (for v type to special type (*)) and its matching conversion-constructor (for u type from special type (*)).
      - Thirdly finally a matched cast operator (for v type to u type).
      (a matched let operator (for u type from v type) is not searched by compiler on a construction with implicit initialization)
    For an implicit copy-construction ('Byval As u_type') when passing a v type parameter, the compiler searches by priority:
    • - Firstly a matched cast operator (for v type to u type).
      - Secondly a matched constructor (for u type from v type).
      - Thirdly finally a cast operator (for v type to special type (*)) and its matching conversion-constructor (for u type from special type (*)).
      (a matched let operator (for u type from v type) is not searched by compiler on a construction with implicit initialization)
    For an implicit assignment ('u = v'), or an implicit return from function by assigning ('Function = v') with function returning u type, the compiler searches by priority:
    • - Firstly a matched let operator (for u type from v type).
      - Secondly a cast operator (for v type to special type (*)) and its matching let operator (for u type from special type (*)).
      - Thirdly a matched cast operator (for v type to u type).
      - Fourthly finally a matched constructor (for u type from v type) and an explicit copy-let operator (u type).
    For an implicit return from function by exiting immediately ('Return v') with function returning u type, the compiler searches by priority:
    • - Firstly a matched constructor (for u type from v type).
      - Secondly a cast operator (for v type to special type (*)) and its matching conversion-constructor (for u type from special type (*)).
      If an explicit copy-constructor (u type) exists:
      - Thirdly a matched cast operator (for v type to u type).
      - Fourthly finally a matched let operator (for u type from v type).
      Else (an explicit copy-constructor (u type) does not exist):
      - Thirdly a matched let operator (for u type from v type).
      - Fourthly finally a matched cast operator (for v type to u type).
    special type (*) : pointer or string, or UDT if there is no explicit copy-constructor / explicit copy-assignment operator for u type
.....



Code to check the precedence list above:
This code allows to check the precedence of the constructors/operators for each conversion case (among the five) by commenting or not the #define lines only.

Code: Select all

'' This code allows to check the precedence of the constructors/operators
''    for each conversion case (among the five)
''    by commenting or not the #define lines only.

'' test cases selected:
#define dim_as_TU_from_TV
#define byval_as_TU_from_TV
#define equal_as_TU_from_TV
#define function_equal_from_TV_to_TU
#define return_from_TV_to_TU

'' constructors/operators defined:
#define TU_ctor_from_TV
#define TU_ctor_from_string
#define TU_let_from_TV
#define TU_let_from_string
#define TU_ctor_from_TU
#define TU_let_from_TU
#define TV_cast_to_string
#define TV_cast_to_TU

Type _TV As TV

Type TU
    Dim As Integer I
    #ifdef TU_ctor_from_TV
        Declare Constructor(Byref v As _TV)
    #endif
    #ifdef TU_ctor_from_string
        Declare Constructor(Byref s As String)
    #endif
    #ifdef TU_let_from_TV
        Declare Operator Let(Byref v As _TV)
    #endif
    #ifdef TU_let_from_string
        Declare Operator Let(Byref s As String)
    #endif
    
    Declare Constructor()
    #ifdef TU_ctor_from_TU
        Declare Constructor(Byref u As TU)
    #endif
    #ifdef TU_let_from_TU
        Declare Operator Let(Byref u As TU)
    #endif
End Type

Type TV
    Dim As Integer I
    #ifdef TV_cast_to_string
        Declare Operator Cast() As String
    #endif
    #ifdef TV_cast_to_TU
        Declare Operator Cast() As TU
    #endif
End Type

Dim Shared As TU u0
Dim Shared As TV v

#ifdef TU_ctor_from_TV
    Constructor TU(Byref v As TV)
        Print "   Constructor TU(Byref As TV)"
    End Constructor
#endif

#ifdef TU_ctor_from_string
    Constructor TU(Byref s As String)
        Print "   Constructor TU(Byref As String)"
    End Constructor
#endif

#ifdef TU_let_from_TV
    Operator TU.Let(Byref v As TV)
        Print "   Operator TU.Let(Byref As TV)"
    End Operator
#endif

#ifdef TU_let_from_string
    Operator TU.Let(Byref s As String)
        Print "   Operator TU.Let(Byref As String)"
    End Operator
#endif

Constructor TU()
End Constructor

#ifdef TU_ctor_from_TU
    Constructor TU(Byref u As TU)
        Print "   Constructor TU(Byref As TU)"
    End Constructor
#endif

#ifdef TU_let_from_TU
    Operator TU.Let(Byref u As TU)
        Print "   Operator TU.Let(Byref As TU)"
    End Operator
#endif

#ifdef TV_cast_to_string
    Operator TV.Cast() As String
        Print "   Operator TV.Cast() As String"
        Return ""
    End Operator
#endif

#ifdef TV_cast_to_TU
    Operator TV.Cast() As TU
        Print "   Operator TV.Cast() As TU"
        Return u0
    End Operator
#endif

#ifdef dim_as_TU_from_TV
    Print "'Dim As TU u = v':"
    Dim As TU u = v
    Print
#else
    Dim As TU u
#endif

#ifdef byval_as_TU_from_TV
    Sub s(Byval u As TU)
    End Sub
    Print "'Byval As TU' (from TV):"
    s(v)
    Print
#endif

#ifdef equal_as_TU_from_TV
    Print "'u = v':"
    u = v
    Print
#endif

#ifdef function_equal_from_TV_to_TU
    Function f1() As TU
        Function = v
    End function
    Print "'Function = v' to TU:"
    f1()
    Print
#endif

#ifdef return_from_TV_to_TU
    Function f2() As TU
        Return v
    End function
    Print "'Return v' to TU:"
    f2()
    Print
#endif

Sleep

For the conversion case : 'Dim As TU u = v'

Code: Select all

'' test cases selected:
#define dim_as_TU_from_TV
'#define byval_as_TU_from_TV
'#define equal_as_TU_from_TV
'#define function_equal_from_TV_to_TU
'#define return_from_TV_to_TU
- Precedence 1:

Code: Select all

'' constructors/operators defined:
#define TU_ctor_from_TV
#define TU_ctor_from_string
#define TU_let_from_TV
#define TU_let_from_string
#define TU_ctor_from_TU
#define TU_let_from_TU
#define TV_cast_to_string
#define TV_cast_to_TU

Code: Select all

'Dim As TU u = v':
   Constructor TU(Byref As TV)
- Precedence 2:

Code: Select all

'' constructors/operators defined:
'#define TU_ctor_from_TV
#define TU_ctor_from_string
#define TU_let_from_TV
#define TU_let_from_string
#define TU_ctor_from_TU
#define TU_let_from_TU
#define TV_cast_to_string
#define TV_cast_to_TU

Code: Select all

'Dim As TU u = v':
   Operator TV.Cast() As String
   Constructor TU(Byref As String)
- Precedence 3 (also works with just the implicit TU.copy-ctor):

Code: Select all

'' constructors/operators defined:
'#define TU_ctor_from_TV
'#define TU_ctor_from_string
#define TU_let_from_TV
#define TU_let_from_string
#define TU_ctor_from_TU
#define TU_let_from_TU
'#define TV_cast_to_string
#define TV_cast_to_TU

Code: Select all

'Dim As TU u = v':
   Operator TV.Cast() As TU
   Constructor TU(Byref As TU)
   Constructor TU(Byref As TU)

For the conversion case : 'Byval As TU' (from TV)

Code: Select all

'' test cases selected:
'#define dim_as_TU_from_TV
#define byval_as_TU_from_TV
'#define equal_as_TU_from_TV
'#define function_equal_from_TV_to_TU
'#define return_from_TV_to_TU
- Precedence 1 (also works with just the implicit TU.copy-ctor):

Code: Select all

'' constructors/operators defined:
#define TU_ctor_from_TV
#define TU_ctor_from_string
#define TU_let_from_TV
#define TU_let_from_string
#define TU_ctor_from_TU
#define TU_let_from_TU
#define TV_cast_to_string
#define TV_cast_to_TU

Code: Select all

'Byval As TU' (from TV):
   Operator TV.Cast() As TU
   Constructor TU(Byref As TU)
   Constructor TU(Byref As TU)
- Precedence 2:

Code: Select all

'' constructors/operators defined:
#define TU_ctor_from_TV
#define TU_ctor_from_string
#define TU_let_from_TV
#define TU_let_from_string
#define TU_ctor_from_TU
#define TU_let_from_TU
#define TV_cast_to_string
'#define TV_cast_to_TU

Code: Select all

'Byval As TU' (from TV):
   Constructor TU(Byref As TV)
- Precedence 3:

Code: Select all

'' constructors/operators defined:
'#define TU_ctor_from_TV
#define TU_ctor_from_string
#define TU_let_from_TV
#define TU_let_from_string
#define TU_ctor_from_TU
#define TU_let_from_TU
#define TV_cast_to_string
'#define TV_cast_to_TU

Code: Select all

'Byval As TU' (from TV):
   Operator TV.Cast() As String
   Constructor TU(Byref As String)

For the conversion case : 'u = v'

Code: Select all

'' test cases selected:
'#define dim_as_TU_from_TV
'#define byval_as_TU_from_TV
#define equal_as_TU_from_TV
'#define function_equal_from_TV_to_TU
'#define return_from_TV_to_TU
- Precedence 1:

Code: Select all

'' constructors/operators defined:
#define TU_ctor_from_TV
#define TU_ctor_from_string
#define TU_let_from_TV
#define TU_let_from_string
#define TU_ctor_from_TU
#define TU_let_from_TU
#define TV_cast_to_string
#define TV_cast_to_TU

Code: Select all

'u = v':
   Operator TU.Let(Byref As TV)
- Precedence 2:

Code: Select all

'' constructors/operators defined:
#define TU_ctor_from_TV
#define TU_ctor_from_string
'#define TU_let_from_TV
#define TU_let_from_string
#define TU_ctor_from_TU
#define TU_let_from_TU
#define TV_cast_to_string
#define TV_cast_to_TU

Code: Select all

'u = v':
   Operator TV.Cast() As String
   Operator TU.Let(Byref As String)
- Precedence 3 (also works with just the implicit TU.copy-ctor and just the implicit TU.copy-let operato):

Code: Select all

'' constructors/operators defined:
#define TU_ctor_from_TV
#define TU_ctor_from_string
'#define TU_let_from_TV
'#define TU_let_from_string
#define TU_ctor_from_TU
#define TU_let_from_TU
'#define TV_cast_to_string
#define TV_cast_to_TU

Code: Select all

'u = v':
   Operator TV.Cast() As TU
   Constructor TU(Byref As TU)
   Operator TU.Let(Byref As TU)
- Precedence 4:

Code: Select all

'' constructors/operators defined:
#define TU_ctor_from_TV
#define TU_ctor_from_string
'#define TU_let_from_TV
'#define TU_let_from_string
#define TU_ctor_from_TU
#define TU_let_from_TU
'#define TV_cast_to_string
'#define TV_cast_to_TU

Code: Select all

'u = v':
   Constructor TU(Byref As TV)
   Operator TU.Let(Byref As TU)

For the conversion case : 'Function = v' to TU

Code: Select all

'' test cases selected:
'#define dim_as_TU_from_TV
'#define byval_as_TU_from_TV
'#define equal_as_TU_from_TV
#define function_equal_from_TV_to_TU
'#define return_from_TV_to_TU
- Precedence 1:

Code: Select all

'' constructors/operators defined:
#define TU_ctor_from_TV
#define TU_ctor_from_string
#define TU_let_from_TV
#define TU_let_from_string
#define TU_ctor_from_TU
#define TU_let_from_TU
#define TV_cast_to_string
#define TV_cast_to_TU

Code: Select all

'Function = v' to TU:
   Operator TU.Let(Byref As TV)
- Precedence 2:

Code: Select all

'' constructors/operators defined:
#define TU_ctor_from_TV
#define TU_ctor_from_string
'#define TU_let_from_TV
#define TU_let_from_string
#define TU_ctor_from_TU
#define TU_let_from_TU
#define TV_cast_to_string
#define TV_cast_to_TU

Code: Select all

'Function = v' to TU:
   Operator TV.Cast() As String
   Operator TU.Let(Byref As String)
- Precedence 3 (also works with just the implicit TU.copy-ctor and just the implicit TU.copy-let operator):

Code: Select all

'' constructors/operators defined:
#define TU_ctor_from_TV
#define TU_ctor_from_string
'#define TU_let_from_TV
'#define TU_let_from_string
#define TU_ctor_from_TU
#define TU_let_from_TU
'#define TV_cast_to_string
#define TV_cast_to_TU

Code: Select all

'Function = v' to TU:
   Operator TV.Cast() As TU
   Constructor TU(Byref As TU)
   Operator TU.Let(Byref As TU)
- Precedence 4:

Code: Select all

'' constructors/operators defined:
#define TU_ctor_from_TV
#define TU_ctor_from_string
'#define TU_let_from_TV
'#define TU_let_from_string
#define TU_ctor_from_TU
#define TU_let_from_TU
'#define TV_cast_to_string
'#define TV_cast_to_TU

Code: Select all

'Function = v' to TU:
   Constructor TU(Byref As TV)
   Operator TU.Let(Byref As TU)

For the conversion case : 'Return v' to TU

Code: Select all

'' test cases selected:
'#define dim_as_TU_from_TV
'#define byval_as_TU_from_TV
'#define equal_as_TU_from_TV
'#define function_equal_from_TV_to_TU
#define return_from_TV_to_TU
- Precedence 1:

Code: Select all

'' constructors/operators defined:
#define TU_ctor_from_TV
#define TU_ctor_from_string
#define TU_let_from_TV
#define TU_let_from_string
#define TU_ctor_from_TU
#define TU_let_from_TU
#define TV_cast_to_string
#define TV_cast_to_TU

Code: Select all

'Return v' to TU:
   Constructor TU(Byref As TV)
- Precedence 2:

Code: Select all

'' constructors/operators defined:
'#define TU_ctor_from_TV
#define TU_ctor_from_string
#define TU_let_from_TV
#define TU_let_from_string
#define TU_ctor_from_TU
#define TU_let_from_TU
#define TV_cast_to_string
#define TV_cast_to_TU

Code: Select all

'Return v' to TU:
   Operator TV.Cast() As String
   Constructor TU(Byref As String)
- Precedence 3 if explicit TU.copy-ctor defined:

Code: Select all

'' constructors/operators defined:
'#define TU_ctor_from_TV
'#define TU_ctor_from_string
#define TU_let_from_TV
#define TU_let_from_string
#define TU_ctor_from_TU
#define TU_let_from_TU
'#define TV_cast_to_string
#define TV_cast_to_TU

Code: Select all

'Return v' to TU:
   Operator TV.Cast() As TU
   Constructor TU(Byref As TU)
   Constructor TU(Byref As TU)
- Precedence 4 if explicit TU.copy-ctor defined:

Code: Select all

'' constructors/operators defined:
'#define TU_ctor_from_TV
'#define TU_ctor_from_string
#define TU_let_from_TV
#define TU_let_from_string
#define TU_ctor_from_TU
#define TU_let_from_TU
'#define TV_cast_to_string
'#define TV_cast_to_TU

Code: Select all

'Return v' to TU:
   Operator TU.Let(Byref As TV)
- Precedence 3 if explicit TU.copy-ctor not defined:

Code: Select all

'' constructors/operators defined:
'#define TU_ctor_from_TV
'#define TU_ctor_from_string
#define TU_let_from_TV
#define TU_let_from_string
'#define TU_ctor_from_TU
#define TU_let_from_TU
'#define TV_cast_to_string
#define TV_cast_to_TU

Code: Select all

'Return v' to TU:
   Operator TU.Let(Byref As TV)
- Precedence 4 if explicit TU.copy-ctor not defined (also works with just the implicit TU.copy-let operator):

Code: Select all

'' constructors/operators defined:
'#define TU_ctor_from_TV
'#define TU_ctor_from_string
'#define TU_let_from_TV
#define TU_let_from_string
'#define TU_ctor_from_TU
#define TU_let_from_TU
'#define TV_cast_to_string
#define TV_cast_to_TU

Code: Select all

'Return v' to TU:
   Operator TV.Cast() As TU
   Operator TU.Let(Byref As TU)
fxm
Moderator
Posts: 12081
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Conversion/Inheritance relating to UDTs via constructors and operators

Post by fxm »

Conversion

If we focus only on the configuration tested first ("Firstly" .....) for the 5 conversion cases ('Dim As UDT u = v', 'Byval As u_type', 'u = v', 'Function = v', 'Return v'), we can notice that in general either the matching constructor or the matching let operator is firstly tested (existing), except for the 'Byval ... As ...' conversion for which the matching cast operator is first tested (existing).

For the 'Dim As UDT u = v' conversion case, the test order is:
1. exists a matching constructor,
2. exists a cast operator and its matching conversion-constructor,
3. exists a matching cast operator.
The expected in the first position is rightly the matching constructor.
Example:

Code: Select all

'' This code allows to check the precedence of the constructors/operators
''    for each conversion case (among the five)
''    by commenting or not the #define lines only.

'' test cases selected:
#define dim_as_TU_from_TV
'#define byval_as_TU_from_TV
'#define equal_as_TU_from_TV
'#define function_equal_from_TV_to_TU
'#define return_from_TV_to_TU

'' constructors/operators defined:
#define TU_ctor_from_TV
#define TU_ctor_from_string
#define TU_let_from_TV
#define TU_let_from_string
#define TU_ctor_from_TU
#define TU_let_from_TU
#define TV_cast_to_string
#define TV_cast_to_TU

Type _TV As TV

Type TU
    Dim As Integer I
    #ifdef TU_ctor_from_TV
        Declare Constructor(Byref v As _TV)
    #endif
    #ifdef TU_ctor_from_string
        Declare Constructor(Byref s As String)
    #endif
    #ifdef TU_let_from_TV
        Declare Operator Let(Byref v As _TV)
    #endif
    #ifdef TU_let_from_string
        Declare Operator Let(Byref s As String)
    #endif
    
    Declare Constructor()
    #ifdef TU_ctor_from_TU
        Declare Constructor(Byref u As TU)
    #endif
    #ifdef TU_let_from_TU
        Declare Operator Let(Byref u As TU)
    #endif
End Type

Type TV
    Dim As Integer I
    #ifdef TV_cast_to_string
        Declare Operator Cast() As String
    #endif
    #ifdef TV_cast_to_TU
        Declare Operator Cast() As TU
    #endif
End Type

Dim Shared As TU u0
Dim Shared As TV v

#ifdef TU_ctor_from_TV
    Constructor TU(Byref v As TV)
        Print "   Constructor TU(Byref As TV)"
    End Constructor
#endif

#ifdef TU_ctor_from_string
    Constructor TU(Byref s As String)
        Print "   Constructor TU(Byref As String)"
    End Constructor
#endif

#ifdef TU_let_from_TV
    Operator TU.Let(Byref v As TV)
        Print "   Operator TU.Let(Byref As TV)"
    End Operator
#endif

#ifdef TU_let_from_string
    Operator TU.Let(Byref s As String)
        Print "   Operator TU.Let(Byref As String)"
    End Operator
#endif

Constructor TU()
End Constructor

#ifdef TU_ctor_from_TU
    Constructor TU(Byref u As TU)
        Print "   Constructor TU(Byref As TU)"
    End Constructor
#endif

#ifdef TU_let_from_TU
    Operator TU.Let(Byref u As TU)
        Print "   Operator TU.Let(Byref As TU)"
    End Operator
#endif

#ifdef TV_cast_to_string
    Operator TV.Cast() As String
        Print "   Operator TV.Cast() As String"
        Return ""
    End Operator
#endif

#ifdef TV_cast_to_TU
    Operator TV.Cast() As TU
        Print "   Operator TV.Cast() As TU"
        Return u0
    End Operator
#endif

#ifdef dim_as_TU_from_TV
    Print "'Dim As TU u = v':"
    Dim As TU u = v
    Print
#else
    Dim As TU u
#endif

#ifdef byval_as_TU_from_TV
    Sub s(Byval u As TU)
    End Sub
    Print "'Byval As TU' (from TV):"
    s(v)
    Print
#endif

#ifdef equal_as_TU_from_TV
    Print "'u = v':"
    u = v
    Print
#endif

#ifdef function_equal_from_TV_to_TU
    Function f1() As TU
        Function = v
    End function
    Print "'Function = v' to TU:"
    f1()
    Print
#endif

#ifdef return_from_TV_to_TU
    Function f2() As TU
        Return v
    End function
    Print "'Return v' to TU:"
    f2()
    Print
#endif

Sleep

Code: Select all

'Dim As TU u = v':
   Constructor TU(Byref As TV)

For this ''Byval As u_type'' conversion case, the test order is:
1. exists a matching cast operator,
2. exists a matching constructor,
3. exists a cast operator and its matching conversion-constructor.
The expected in the first position would be the matching constructor rather the matching cast operator.
Example:

Code: Select all

'' This code allows to check the precedence of the constructors/operators
''    for each conversion case (among the five)
''    by commenting or not the #define lines only.

'' test cases selected:
'#define dim_as_TU_from_TV
#define byval_as_TU_from_TV
'#define equal_as_TU_from_TV
'#define function_equal_from_TV_to_TU
'#define return_from_TV_to_TU

'' constructors/operators defined:
#define TU_ctor_from_TV
#define TU_ctor_from_string
#define TU_let_from_TV
#define TU_let_from_string
#define TU_ctor_from_TU
#define TU_let_from_TU
#define TV_cast_to_string
#define TV_cast_to_TU

Type _TV As TV

Type TU
    Dim As Integer I
    #ifdef TU_ctor_from_TV
        Declare Constructor(Byref v As _TV)
    #endif
    #ifdef TU_ctor_from_string
        Declare Constructor(Byref s As String)
    #endif
    #ifdef TU_let_from_TV
        Declare Operator Let(Byref v As _TV)
    #endif
    #ifdef TU_let_from_string
        Declare Operator Let(Byref s As String)
    #endif
    
    Declare Constructor()
    #ifdef TU_ctor_from_TU
        Declare Constructor(Byref u As TU)
    #endif
    #ifdef TU_let_from_TU
        Declare Operator Let(Byref u As TU)
    #endif
End Type

Type TV
    Dim As Integer I
    #ifdef TV_cast_to_string
        Declare Operator Cast() As String
    #endif
    #ifdef TV_cast_to_TU
        Declare Operator Cast() As TU
    #endif
End Type

Dim Shared As TU u0
Dim Shared As TV v

#ifdef TU_ctor_from_TV
    Constructor TU(Byref v As TV)
        Print "   Constructor TU(Byref As TV)"
    End Constructor
#endif

#ifdef TU_ctor_from_string
    Constructor TU(Byref s As String)
        Print "   Constructor TU(Byref As String)"
    End Constructor
#endif

#ifdef TU_let_from_TV
    Operator TU.Let(Byref v As TV)
        Print "   Operator TU.Let(Byref As TV)"
    End Operator
#endif

#ifdef TU_let_from_string
    Operator TU.Let(Byref s As String)
        Print "   Operator TU.Let(Byref As String)"
    End Operator
#endif

Constructor TU()
End Constructor

#ifdef TU_ctor_from_TU
    Constructor TU(Byref u As TU)
        Print "   Constructor TU(Byref As TU)"
    End Constructor
#endif

#ifdef TU_let_from_TU
    Operator TU.Let(Byref u As TU)
        Print "   Operator TU.Let(Byref As TU)"
    End Operator
#endif

#ifdef TV_cast_to_string
    Operator TV.Cast() As String
        Print "   Operator TV.Cast() As String"
        Return ""
    End Operator
#endif

#ifdef TV_cast_to_TU
    Operator TV.Cast() As TU
        Print "   Operator TV.Cast() As TU"
        Return u0
    End Operator
#endif

#ifdef dim_as_TU_from_TV
    Print "'Dim As TU u = v':"
    Dim As TU u = v
    Print
#else
    Dim As TU u
#endif

#ifdef byval_as_TU_from_TV
    Sub s(Byval u As TU)
    End Sub
    Print "'Byval As TU' (from TV):"
    s(v)
    Print
#endif

#ifdef equal_as_TU_from_TV
    Print "'u = v':"
    u = v
    Print
#endif

#ifdef function_equal_from_TV_to_TU
    Function f1() As TU
        Function = v
    End function
    Print "'Function = v' to TU:"
    f1()
    Print
#endif

#ifdef return_from_TV_to_TU
    Function f2() As TU
        Return v
    End function
    Print "'Return v' to TU:"
    f2()
    Print
#endif

Sleep

Code: Select all

'Byval As TU' (from TV):
   Operator TV.Cast() As TU
   Constructor TU(Byref As TU)
   Constructor TU(Byref As TU)
fxm
Moderator
Posts: 12081
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Conversion/Inheritance relating to UDTs via constructors and operators

Post by fxm »

Conversion

On the other hand, we can see in the first post above that both the matched constructor (for u type from v type) and the matched let operator (for u type from v type) can be used (among the 4 possibilities) for the last two conversion cases, but only the matched constructor (for u type from v type) can be used for the first two conversion cases.
(while we could also have had the matched let operator (for u type from v type) used (in a 4th possibility) for the first two conversion cases)

Example that fails:

Code: Select all

'' This code allows to check the precedence of the constructors/operators
''    for each conversion case (among the five)
''    by commenting or not the #define lines only.

'' test cases selected:
#define dim_as_TU_from_TV
#define byval_as_TU_from_TV
'#define equal_as_TU_from_TV
'#define function_equal_from_TV_to_TU
'#define return_from_TV_to_TU

'' constructors/operators defined:
'#define TU_ctor_from_TV
'#define TU_ctor_from_string
#define TU_let_from_TV
#define TU_let_from_string
#define TU_ctor_from_TU
#define TU_let_from_TU
'#define TV_cast_to_string
'#define TV_cast_to_TU

Type _TV As TV

Type TU
    Dim As Integer I
    #ifdef TU_ctor_from_TV
        Declare Constructor(Byref v As _TV)
    #endif
    #ifdef TU_ctor_from_string
        Declare Constructor(Byref s As String)
    #endif
    #ifdef TU_let_from_TV
        Declare Operator Let(Byref v As _TV)
    #endif
    #ifdef TU_let_from_string
        Declare Operator Let(Byref s As String)
    #endif
    
    Declare Constructor()
    #ifdef TU_ctor_from_TU
        Declare Constructor(Byref u As TU)
    #endif
    #ifdef TU_let_from_TU
        Declare Operator Let(Byref u As TU)
    #endif
End Type

Type TV
    Dim As Integer I
    #ifdef TV_cast_to_string
        Declare Operator Cast() As String
    #endif
    #ifdef TV_cast_to_TU
        Declare Operator Cast() As TU
    #endif
End Type

Dim Shared As TU u0
Dim Shared As TV v

#ifdef TU_ctor_from_TV
    Constructor TU(Byref v As TV)
        Print "   Constructor TU(Byref As TV)"
    End Constructor
#endif

#ifdef TU_ctor_from_string
    Constructor TU(Byref s As String)
        Print "   Constructor TU(Byref As String)"
    End Constructor
#endif

#ifdef TU_let_from_TV
    Operator TU.Let(Byref v As TV)
        Print "   Operator TU.Let(Byref As TV)"
    End Operator
#endif

#ifdef TU_let_from_string
    Operator TU.Let(Byref s As String)
        Print "   Operator TU.Let(Byref As String)"
    End Operator
#endif

Constructor TU()
End Constructor

#ifdef TU_ctor_from_TU
    Constructor TU(Byref u As TU)
        Print "   Constructor TU(Byref As TU)"
    End Constructor
#endif

#ifdef TU_let_from_TU
    Operator TU.Let(Byref u As TU)
        Print "   Operator TU.Let(Byref As TU)"
    End Operator
#endif

#ifdef TV_cast_to_string
    Operator TV.Cast() As String
        Print "   Operator TV.Cast() As String"
        Return ""
    End Operator
#endif

#ifdef TV_cast_to_TU
    Operator TV.Cast() As TU
        Print "   Operator TV.Cast() As TU"
        Return u0
    End Operator
#endif

#ifdef dim_as_TU_from_TV
    Print "'Dim As TU u = v':"
    Dim As TU u = v
    Print
#else
    Dim As TU u
#endif

#ifdef byval_as_TU_from_TV
    Sub s(Byval u As TU)
    End Sub
    Print "'Byval As TU' (from TV):"
    s(v)
    Print
#endif

#ifdef equal_as_TU_from_TV
    Print "'u = v':"
    u = v
    Print
#endif

#ifdef function_equal_from_TV_to_TU
    Function f1() As TU
        Function = v
    End function
    Print "'Function = v' to TU:"
    f1()
    Print
#endif

#ifdef return_from_TV_to_TU
    Function f2() As TU
        Return v
    End function
    Print "'Return v' to TU:"
    f2()
    Print
#endif

Sleep
Wished output for example:

Code: Select all

'Dim As TU u = v':
   Operator TU.Let(Byref As TV)
   Constructor TU(Byref As TU)

'Byval As TU' (from TV):
   Operator TU.Let(Byref As TV)
   Constructor TU(Byref As TU)
fxm
Moderator
Posts: 12081
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Conversion/Inheritance relating to UDTs via constructors and operators

Post by fxm »

fxm wrote: Nov 10, 2022 13:27 If we focus only on the configuration tested first ("Firstly" .....) for the 5 conversion cases ('Dim As UDT u = v', 'Byval As u_type', 'u = v', 'Function = v', 'Return v'), we can notice that in general either the matching constructor or the matching let operator is firstly tested (existing), except for the 'Byval As ...' conversion for which the matching cast operator is first tested (existing).

Inheritance

Similarly, if we go from conversion to inheritance (Parent <- Child), for the following 5 expression cases:
'Dim Shared As Child c1'
'Dim As Child c2 = c1'
'Byval c As Child'
'Function = c1'
'Return c1'

We can notice below that the explicit Parent's constructor and the explicit Parent's let operator are both called, except for the 'Byval c As Child' expression for which this is true only if an explicit Parent's destructor is additionally defined (otherwise an internal construction and a shallow copy are executed for the Parent's part).

Test code:

Code: Select all

#define Parent_dtor

Type Parent
    Dim As Integer I
    Declare Constructor()
    Declare Constructor(Byref u As Parent)
    Declare Operator Let(Byref u As Parent)
    #ifdef Parent_dtor
        Declare Destructor()
    #endif
End Type

Constructor Parent()
    Print "   Constructor Parent()"
End Constructor

Constructor Parent(Byref u As Parent)
    Print "   Constructor Parent(Byref As Parent)"
End Constructor

Operator Parent.Let(Byref u As Parent)
    Print "   Operator Parent.Let(Byref As Parent)"
End Operator

#ifdef Parent_dtor
    Destructor Parent()
    End destructor
#endif

Type Child Extends Parent
End Type

Dim Shared As Child c1
Print

Print "'Dim As Child c2 = c1':"
Dim As Child c2 = c1
Print

Sub s(Byval c As Child)
End Sub
Print "'Byval As Child' from Child:"
s(c1)
Print

Function f1() As Child
    Function = c1
End function
Print "'Function = c1' to Child:"
f1()
Print

Function f2() As Child
    Return c1
End function
Print "'Return c1' to Child:"
f2()
Print

Sleep

With explicit Base destructor:

Code: Select all

#define Parent_dtor

Code: Select all

   Constructor Parent()

'Dim As Child c2 = c1':
   Constructor Parent()
   Operator Parent.Let(Byref As Parent)

'Byval As Child' from Child:
   Constructor Parent()
   Operator Parent.Let(Byref As Parent)

'Function = c1' to Child:
   Constructor Parent()
   Operator Parent.Let(Byref As Parent)

'Return c1' to Child:
   Constructor Parent()
   Operator Parent.Let(Byref As Parent)

Without explicit Base destructor:

Code: Select all

'#define Parent_dtor

Code: Select all

   Constructor Parent()

'Dim As Child c2 = c1':
   Constructor Parent()
   Operator Parent.Let(Byref As Parent)

'Byval As Child' from Child:

'Function = c1' to Child:
   Constructor Parent()
   Operator Parent.Let(Byref As Parent)

'Return c1' to Child:
   Constructor Parent()
   Operator Parent.Let(Byref As Parent)
fxm
Moderator
Posts: 12081
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Conversion/Inheritance relating to UDTs via constructors and operators

Post by fxm »

Conversion/Inheritance

So for the two features which are conversion (see 3 posts before) and inheritance (post above) relating to UDTs, we can notice that the 'Byval ... As UDT' expression has a degraded performance compared to other 2 copy-construction expressions ('Dim As UDT ... = ...' and 'Return ...').

Test code combining conversion (v -> u) and inheritance (TU <- TW) to highlight the behavior of 'Byval ... As UDT', compared to 'Dim As UDT ... = ...' and 'Return ...':

Code: Select all

'' test cases selected:
#define dim_as_TU_from_TV
#define byval_as_TU_from_TV
#define return_from_TV_to_TU
#define dim_as_TW_from_TW
#define byval_as_TW_from_TW
#define return_from_TW_to_TW

'' constructors/operators defined:
#define TU_dtor
#define TV_cast_to_TU

Type _TV As TV

Type TU
    Dim As Integer I
    Declare Constructor()
    Declare Constructor(Byref v As _TV)
    Declare Operator Let(Byref u As TU)
    #ifdef TU_dtor
        Declare Destructor()
    #endif
End Type

Type TV
    Dim As Integer I
    #ifdef TV_cast_to_TU
        Declare Operator Cast() As TU
    #endif
End Type

Type TW Extends TU
End Type

Dim Shared As TU u0
Dim Shared As TV v
Dim Shared As TW w

Constructor TU()
    If (@This <> @u0) And (@This <> @w) Then
        Print "   Constructor TU()"
    End If
End Constructor

Constructor TU(Byref v As TV)
    Print "   Constructor TU(Byref As TV)"
End Constructor

Operator TU.Let(Byref u As TU)
    Print "   Operator TU.Let(Byref As TU)"
End Operator

#ifdef TV_cast_to_TU
    Operator TV.Cast() As TU
        Print "   Operator TV.Cast() As TU"
        Return u0
    End Operator
#endif

#ifdef TU_dtor
    Destructor TU()
    End Destructor
#endif

#ifdef dim_as_TU_from_TV
    Print "'Dim As TU u = v':"
    Dim As TU u = v
    Print
#endif

#ifdef byval_as_TU_from_TV
    Sub s1(Byval u As TU)
    End Sub
    Print "'Byval As TU' (from TV):"
    s1(v)
    Print
#endif

#ifdef return_from_TV_to_TU
    Function f1() As TU
        Return v
    End function
    Print "'Return v' to TU:"
    f1()
    Print
#endif

#ifdef dim_as_TW_from_TW
    Print "'Dim As TW w1 = w':"
    Dim As TW w1 = w
    Print
#endif

#ifdef byval_as_TW_from_TW
    Sub s2(Byval w As TW)
    End Sub
    Print "'Byval As TW' (from TW):"
    s2(w)
    Print
#endif

#ifdef return_from_TW_to_TW
    Function f2() As TW
        Return w
    End function
    Print "'Return w' to TW:"
    f2()
    Print
#endif

Sleep

1) Conversion (v -> u):

Code: Select all

'' test cases selected:
#define dim_as_TU_from_TV
#define byval_as_TU_from_TV
#define return_from_TV_to_TU
'#define dim_as_TW_from_TW
'#define byval_as_TW_from_TW
'#define return_from_TW_to_TW
Without cast operator in TV => OK:

Code: Select all

'' constructors/operators defined:
#define TU_dtor
'#define TV_cast_to_TU

Code: Select all

'Dim As TU u = v':
   Constructor TU(Byref As TV)

'Byval As TU' (from TV):
   Constructor TU(Byref As TV)

'Return v' to TU:
   Constructor TU(Byref As TV)
With cast operator in TV => NOK for 'Byval As TU' (from TV):
(cast operator instead of conversion-constructor)

Code: Select all

'' constructors/operators defined:
#define TU_dtor
#define TV_cast_to_TU

Code: Select all

'Dim As TU u = v':
   Constructor TU(Byref As TV)

'Byval As TU' (from TV):
   Operator TV.Cast() As TU

'Return v' to TU:
   Constructor TU(Byref As TV)

2) Inheritance (TU <- TW):

Code: Select all

'' test cases selected:
'#define dim_as_TU_from_TV
'#define byval_as_TU_from_TV
'#define return_from_TV_to_TU
#define dim_as_TW_from_TW
#define byval_as_TW_from_TW
#define return_from_TW_to_TW
With destructor in TU => OK:

Code: Select all

'' constructors/operators defined:
#define TU_dtor
#define TV_cast_to_TU

Code: Select all

'Dim As TW w1 = w':
   Constructor TU()
   Operator TU.Let(Byref As TU)

'Byval As TW' (from TW):
   Constructor TU()
   Operator TU.Let(Byref As TU)

'Return w' to TW:
   Constructor TU()
   Operator TU.Let(Byref As TU)
Without destructor in TU => NOK for 'Byval As TW' (from TW):
(interne construction + shallow copy, instead of explicit default-constructor + explicit copy-let operator)

Code: Select all

'' constructors/operators defined:
'#define TU_dtor
#define TV_cast_to_TU

Code: Select all

'Dim As TW w1 = w':
   Constructor TU()
   Operator TU.Let(Byref As TU)

'Byval As TW' (from TW):

'Return w' to TW:
   Constructor TU()
   Operator TU.Let(Byref As TU)

3) Conversion with Inheritance (w -> u with TU <- TW)
Same bad behavior of 'Byval As TU' (from TW) if there is no explicit copy-constructor defined in TU, but all the time whether there is an explicit destructor defined in TU or not:

Code: Select all

'' test cases selected:
#define dim_as_TU_from_TW
#define byval_as_TU_from_TW
#define return_from_TW_to_TU

'' constructors/operators defined:
'#define TU_ctor_from_TU
#define TU_dtor

Type TU
    Dim As Integer I
    Declare Constructor()
    #ifdef TU_ctor_from_TU
        Declare Constructor(Byref u As TU)
    #endif
    Declare Operator Let(Byref u As TU)
    #ifdef TU_dtor
        Declare Destructor()
    #endif
End Type

Type TW Extends TU
End Type

Dim Shared As TW w

Constructor TU()
    If @This <> @w Then
        Print "   Constructor TU()"
    End If
End Constructor

#ifdef TU_ctor_from_TU
    Constructor TU(Byref u As TU)
        Print "   Constructor(Byref u As TU)"
    End constructor
#endif

Operator TU.Let(Byref u As TU)
    Print "   Operator TU.Let(Byref As TU)"
End Operator

#ifdef TV_cast_to_TU
    Operator TV.Cast() As TU
        Print "   Operator TV.Cast() As TU"
        Return u0
    End Operator
#endif

#ifdef TU_dtor
    Destructor TU()
    End Destructor
#endif

#ifdef dim_as_TU_from_TW
    Print "'Dim As TU u = w':"
    Dim As TU u = w
    Print
#endif

#ifdef byval_as_TU_from_TW
    Sub s(Byval u As TU)
    End Sub
    Print "'Byval As TU' (from TW):"
    s(W)
    Print
#endif

#ifdef return_from_TW_to_TU
    Function f() As TU
        Return w
    End function
    Print "'Return w' to TU:"
    f()
    Print
#endif

Sleep

Code: Select all

'Dim As TU u = w':
   Constructor TU()
   Operator TU.Let(Byref As TU)

'Byval As TU' (from TW):

'Return w' to TU:
   Constructor TU()
   Operator TU.Let(Byref As TU)

In summary, in a lot of configurations, 'Byval ... As UDT' does not behave (degraded performance) like the other 2 copy-constructions 'Dim As UDT ... = ...' and 'Return ...'.
fxm
Moderator
Posts: 12081
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Conversion/Inheritance relating to UDTs via constructors and operators

Post by fxm »

Conversion/inheritance

A most complete code allowing to test all the conversion/inheritance cases seen in all the previous posts:

Code: Select all

'' This code allows to check the precedence of the constructors/operators (among the nine)
''    for each conversion/inheritance case (among the fifteen)
''    by commenting or not the #define lines only.

'' test cases selected:
#define dim_as_TU_from_TV
#define byval_as_TU_from_TV
#define equal_as_TU_from_TV
#define function_equal_from_TV_to_TU
#define return_from_TV_to_TU
#define dim_as_TW_from_TW
#define byval_as_TW_from_TW
#define equal_as_TW_from_TW
#define function_equal_from_TW_to_TW
#define return_from_TW_to_TW
#define dim_as_TU_from_TW
#define byval_as_TU_from_TW
#define equal_as_TU_from_TW
#define function_equal_from_TW_to_TU
#define return_from_TW_to_TU

'' constructors/operators defined:
#define TU_ctor_from_TU
#define TU_ctor_from_TV
#define TU_ctor_from_TX
#define TU_let_from_TU
#define TU_let_from_TV
#define TU_let_from_TX
#define TU_dtor
#define TV_cast_to_TU
#define TV_cast_to_TX

'' TX type defined:
'#define TX_as_integer
'#define TX_as_integer_ptr
#define TX_as_string
'#define TX_as_UDT_with_integer
'#define TX_as_UDT_with_string

#ifdef TX_as_integer
    Type TX As Integer
#endif

#ifdef TX_as_integer_ptr
    Type TX As Integer Ptr
#endif

#ifdef TX_as_string
    Type TX As String
#endif

#ifdef TX_as_UDT_with_integer
    Type TX
        Dim As Integer I
    End Type
#endif

#ifdef TX_as_UDT_with_string
    Type TX
        Dim As String s
    End Type
#endif

Type _TV As TV

Type TU
    Dim As Integer I
    Declare Constructor()
    #ifdef TU_ctor_from_TU
        Declare Constructor(Byref u As TU)
    #endif
    #ifdef TU_ctor_from_TV
        Declare Constructor(Byref v As _TV)
    #endif
    #ifdef TU_ctor_from_TX
        Declare Constructor(Byref s As TX)
    #endif
    #ifdef TU_let_from_TU
        Declare Operator Let(Byref u As TU)
    #endif
    #ifdef TU_let_from_TV
        Declare Operator Let(Byref v As _TV)
    #endif
    #ifdef TU_let_from_TX
        Declare Operator Let(Byref s As TX)
    #endif
    #ifdef TU_dtor
        Declare Destructor()
    #endif
End Type

Type TV
    Dim As Integer I
    #ifdef TV_cast_to_TU
        Declare Operator Cast() Byref As TU
    #endif
    #ifdef TV_cast_to_TX
        Declare Operator Cast() Byref As TX
    #endif
End Type

Type TW Extends TU
End Type

Dim Shared As TX x0
Dim Shared As TU u0
Dim Shared As TV v
Dim Shared As TW w0, w1

Constructor TU()
    If (@This <> @u0) And (@This <> @w0) AND (@This <> @w1) Then
        Print "   Constructor TU()"
    End If
End Constructor

#ifdef TU_ctor_from_TU
    Constructor TU(Byref u As TU)
        Print "   Constructor TU(Byref As TU)"
    End Constructor
#endif

#ifdef TU_ctor_from_TV
    Constructor TU(Byref v As TV)
        Print "   Constructor TU(Byref As TV)"
    End Constructor
#endif

#ifdef TU_ctor_from_TX
    Constructor TU(Byref s As TX)
        Print "   Constructor TU(Byref As TX)"
    End Constructor
#endif

#ifdef TU_let_from_TU
    Operator TU.Let(Byref u As TU)
        Print "   Operator TU.Let(Byref As TU)"
    End Operator
#endif

#ifdef TU_let_from_TV
    Operator TU.Let(Byref v As TV)
        Print "   Operator TU.Let(Byref As TV)"
    End Operator
#endif

#ifdef TU_let_from_TX
    Operator TU.Let(Byref s As TX)
        Print "   Operator TU.Let(Byref As TX)"
    End Operator
#endif

#ifdef TU_dtor
    Destructor TU()
    End destructor
#endif

#ifdef TV_cast_to_TU
    Operator TV.Cast() Byref As TU
        Print "   Operator TV.Cast() Byref As TU"
        Return u0
    End Operator
#endif

#ifdef TV_cast_to_TX
    Operator TV.Cast() Byref As TX
        Print "   Operator TV.Cast() Byref As TX"
        Return x0
    End Operator
#endif

#ifdef byval_as_TU_from_TV
    Sub s1(Byval u As TU)
    End Sub
#endif

#ifdef function_equal_from_TV_to_TU
    Function f11() As TU
        Function = v
    End function
#endif

#ifdef return_from_TV_to_TU
    Function f12() As TU
        Return v
    End function
#endif

#ifdef byval_as_TW_from_TW
    Sub s21(Byval w As TW)
    End Sub
#endif

#ifdef function_equal_from_TW_to_TW
    Function f21() As TW
        Function = w0
    End function
#endif

#ifdef return_from_TW_to_TW
    Function f22() As TW
        Return w0
    End function
#endif

#ifdef byval_as_TU_from_TW
    Sub s31(Byval u As TU)
    End Sub
#endif

#ifdef function_equal_from_TW_to_TU
    Function f31() As TU
        Function = w0
    End function
#endif

#ifdef return_from_TW_to_TU
    Function f32() As TU
        Return w0
    End function
#endif

Print "-------------------- Conversion (v -> u) ---------------------"
Print

Scope
    #ifdef dim_as_TU_from_TV
        Print "'Dim As TU u = v':"
        Dim As TU u = v
        Print
    #else
        Dim Byref As TU u = u0
    #endif

    #ifdef byval_as_TU_from_TV
        Print "'Byval As TU' (from TV):"
        s1(v)
        Print
    #endif

    #ifdef equal_as_TU_from_TV
        Print "'u = v':"
        u = v
        Print
    #endif

    #ifdef function_equal_from_TV_to_TU
        Print "'Function = v' to TU:"
        f11()
        Print
    #endif

    #ifdef return_from_TV_to_TU
        Print "'Return v' to TU:"
        f12()
        Print
    #endif
End Scope

Print "------------------- Inheritance (TU <- TW) -------------------"
Print

Scope
    #ifdef dim_as_TW_from_TW
        Print "'Dim As TW w = w0':"
        Dim As TW w = w0
        Print
    #else
        Dim Byref As TW w = w0
    #endif

    #ifdef byval_as_TW_from_TW
        Print "'Byval As TW' (from TW):"
        s21(w)
        Print
    #endif

    #ifdef equal_as_TW_from_TW
        Print "'w = w0':"
        w = w0
        Print
    #endif

    #ifdef function_equal_from_TW_to_TW
        Print "'Function = w' to TW:"
        f21()
        Print
    #endif

    #ifdef return_from_TW_to_TW
        Print "'Return w' to TW:"
        f22()
        Print
    #endif
End Scope

Print "----- Conversion with Inheritance (w -> u with TU <- TW) -----"
Print

Scope
    Dim Byref As TW w = w0
    #ifdef dim_as_TU_from_TW
        Print "'Dim As TU u = w':"
        Dim As TU u = w
        Print
    #endif

    #ifdef byval_as_TU_from_TW
        Print "'Byval As TU' (from TW):"
        s31(w)
        Print
    #endif

    #ifdef equal_as_TU_from_TW
        Print "'u = w':"
        u0 = w
        Print
    #endif

    #ifdef function_equal_from_TW_to_TU
        Print "'Function = w' to TU:"
        f31()
        Print
    #endif

    #ifdef return_from_TW_to_TU
        Print "'Return w' to TU:"
        f32()
        Print
    #endif
End Scope

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

Re: Conversion/Inheritance relating to UDTs via constructors and operators

Post by fxm »

Indirect Conversion

Conversion between two UDTs 'TU' and 'TV' : v -> u , but via a third-party type 'TX' : v -> x -> u

This kind of conversion (via a third-party type) uses a cast operator in the 'TV' type (v -> x), and a conversion-constructor or copy-assignment operator in the 'TU' type (x -> u).

All types are not illegible as third-party type for this indirect conversion:
- elementary built-in types: only pointer and string, the other numeric types do not work.
- UDT but only if there is no explicit copy-constructor or explicit copy-assignment operator (depending on the conversion expression) for the 'TU' type.

Simple example for copy-construction with indirect conversion:
'Dim As TU u = v'
performed via:
'Dim As TU u = x' with 'x = Cast(TX, v)'
so:
'TU.Ctor_from_TX( TV.Cast_to_TX( v ) )'
We can select the kind of the 'TX' type and if an explicit copy-constructor exists for the 'TU' type.
1) Example with string as third-party type:

Code: Select all

'' TX type defined:
'#define TX_as_integer
'#define TX_as_integer_ptr
#define TX_as_string
'#define TX_as_UDT_with_integer
'#define TX_as_UDT_with_string

'' constructors defined:
#define copy_constructor_TU

'----------------------------

#ifdef TX_as_integer
    Type TX As Integer
#endif

#ifdef TX_as_integer_ptr
    Type TX As Integer Ptr
#endif

#ifdef TX_as_string
    Type TX As String
#endif

#ifdef TX_as_UDT_with_integer
    Type TX
        Dim As Integer I
    End Type
#endif

#ifdef TX_as_UDT_with_string
    Type TX
        Dim As String s
    End Type
#endif

Type TU
    Dim As Integer I
    Declare Constructor()
    #ifdef copy_constructor_TU
        Declare Constructor(Byref u As TU)
    #endif
    Declare Constructor(Byref x As TX)
End Type

Constructor TU()
End constructor

#ifdef copy_constructor_TU
    Constructor TU(Byref u As TU)
        Print "   Constructor TU(Byref As TU)"
    End Constructor
#endif

Constructor TU(Byref x As TX)
    Print "   Constructor TU(Byref As TX)"
End Constructor

Type TV
    Dim As Integer I
    Declare Operator Cast() As TX
End Type

Operator TV.Cast() As TX
    Print "   Operator TV.Cast() As TX"
    Dim As TX x0
    Return x0
End Operator

Dim As TV v

Print "'Dim As TU u = v':"
Dim As TU u = v

Sleep

Code: Select all

'Dim As TU u = v':
   Operator TV.Cast() As TX
   Constructor TU(Byref As TX)
2) Example with UDT (with integer field) as third-party type and with explicit copy-constructor for 'TU' type:

Code: Select all

'' TX type defined:
'#define TX_as_integer
'#define TX_as_integer_ptr
'#define TX_as_string
#define TX_as_UDT_with_integer
'#define TX_as_UDT_with_string

'' constructors defined:
#define copy_constructor_TU

'----------------------------

#ifdef TX_as_integer
    Type TX As Integer
#endif

#ifdef TX_as_integer_ptr
    Type TX As Integer Ptr
#endif

#ifdef TX_as_string
    Type TX As String
#endif

#ifdef TX_as_UDT_with_integer
    Type TX
        Dim As Integer I
    End Type
#endif

#ifdef TX_as_UDT_with_string
    Type TX
        Dim As String s
    End Type
#endif

Type TU
    Dim As Integer I
    Declare Constructor()
    #ifdef copy_constructor_TU
        Declare Constructor(Byref u As TU)
    #endif
    Declare Constructor(Byref x As TX)
End Type

Constructor TU()
End constructor

#ifdef copy_constructor_TU
    Constructor TU(Byref u As TU)
        Print "   Constructor TU(Byref As TU)"
    End Constructor
#endif

Constructor TU(Byref x As TX)
    Print "   Constructor TU(Byref As TX)"
End Constructor

Type TV
    Dim As Integer I
    Declare Operator Cast() As TX
End Type

Operator TV.Cast() As TX
    Print "   Operator TV.Cast() As TX"
    Dim As TX x0
    Return x0
End Operator

Dim As TV v

Print "'Dim As TU u = v':"
Dim As TU u = v

Sleep
C:\.....\FBIDETEMP.bas(73) error 58: Type mismatch, at parameter 1 (u) of TU.constructor(as TU) in 'Dim As TU u = v'
3) Example with UDT (with integer field) as third-party type and without explicit copy-constructor for 'TU' type:

Code: Select all

'' TX type defined:
'#define TX_as_integer
'#define TX_as_integer_ptr
'#define TX_as_string
#define TX_as_UDT_with_integer
'#define TX_as_UDT_with_string

'' constructors defined:
'#define copy_constructor_TU

'----------------------------

#ifdef TX_as_integer
    Type TX As Integer
#endif

#ifdef TX_as_integer_ptr
    Type TX As Integer Ptr
#endif

#ifdef TX_as_string
    Type TX As String
#endif

#ifdef TX_as_UDT_with_integer
    Type TX
        Dim As Integer I
    End Type
#endif

#ifdef TX_as_UDT_with_string
    Type TX
        Dim As String s
    End Type
#endif

Type TU
    Dim As Integer I
    Declare Constructor()
    #ifdef copy_constructor_TU
        Declare Constructor(Byref u As TU)
    #endif
    Declare Constructor(Byref x As TX)
End Type

Constructor TU()
End constructor

#ifdef copy_constructor_TU
    Constructor TU(Byref u As TU)
        Print "   Constructor TU(Byref As TU)"
    End Constructor
#endif

Constructor TU(Byref x As TX)
    Print "   Constructor TU(Byref As TX)"
End Constructor

Type TV
    Dim As Integer I
    Declare Operator Cast() As TX
End Type

Operator TV.Cast() As TX
    Print "   Operator TV.Cast() As TX"
    Dim As TX x0
    Return x0
End Operator

Dim As TV v

Print "'Dim As TU u = v':"
Dim As TU u = v

Sleep

Code: Select all

'Dim As TU u = v':
   Operator TV.Cast() As TX
   Constructor TU(Byref As TX)
4) Example with UDT (with string field) as third-party type and with explicit copy-constructor for 'TU' type:

Code: Select all

'' TX type defined:
'#define TX_as_integer
'#define TX_as_integer_ptr
'#define TX_as_string
'#define TX_as_UDT_with_integer
#define TX_as_UDT_with_string

'' constructors defined:
#define copy_constructor_TU

'----------------------------

#ifdef TX_as_integer
    Type TX As Integer
#endif

#ifdef TX_as_integer_ptr
    Type TX As Integer Ptr
#endif

#ifdef TX_as_string
    Type TX As String
#endif

#ifdef TX_as_UDT_with_integer
    Type TX
        Dim As Integer I
    End Type
#endif

#ifdef TX_as_UDT_with_string
    Type TX
        Dim As String s
    End Type
#endif

Type TU
    Dim As Integer I
    Declare Constructor()
    #ifdef copy_constructor_TU
        Declare Constructor(Byref u As TU)
    #endif
    Declare Constructor(Byref x As TX)
End Type

Constructor TU()
End constructor

#ifdef copy_constructor_TU
    Constructor TU(Byref u As TU)
        Print "   Constructor TU(Byref As TU)"
    End Constructor
#endif

Constructor TU(Byref x As TX)
    Print "   Constructor TU(Byref As TX)"
End Constructor

Type TV
    Dim As Integer I
    Declare Operator Cast() As TX
End Type

Operator TV.Cast() As TX
    Print "   Operator TV.Cast() As TX"
    Dim As TX x0
    Return x0
End Operator

Dim As TV v

Print "'Dim As TU u = v':"
Dim As TU u = v

Sleep
C:\.....\FBIDETEMP.bas(73) error 98: Ambiguous call to overloaded function, TU.constructor() in 'Dim As TU u = v'
5) Example with UDT (with string field) as third-party type and without explicit copy-constructor for 'TU' type:

Code: Select all

'' TX type defined:
'#define TX_as_integer
'#define TX_as_integer_ptr
'#define TX_as_string
'#define TX_as_UDT_with_integer
#define TX_as_UDT_with_string

'' constructors defined:
'#define copy_constructor_TU

'----------------------------

#ifdef TX_as_integer
    Type TX As Integer
#endif

#ifdef TX_as_integer_ptr
    Type TX As Integer Ptr
#endif

#ifdef TX_as_string
    Type TX As String
#endif

#ifdef TX_as_UDT_with_integer
    Type TX
        Dim As Integer I
    End Type
#endif

#ifdef TX_as_UDT_with_string
    Type TX
        Dim As String s
    End Type
#endif

Type TU
    Dim As Integer I
    Declare Constructor()
    #ifdef copy_constructor_TU
        Declare Constructor(Byref u As TU)
    #endif
    Declare Constructor(Byref x As TX)
End Type

Constructor TU()
End constructor

#ifdef copy_constructor_TU
    Constructor TU(Byref u As TU)
        Print "   Constructor TU(Byref As TU)"
    End Constructor
#endif

Constructor TU(Byref x As TX)
    Print "   Constructor TU(Byref As TX)"
End Constructor

Type TV
    Dim As Integer I
    Declare Operator Cast() As TX
End Type

Operator TV.Cast() As TX
    Print "   Operator TV.Cast() As TX"
    Dim As TX x0
    Return x0
End Operator

Dim As TV v

Print "'Dim As TU u = v':"
Dim As TU u = v

Sleep

Code: Select all

'Dim As TU u = v':
   Operator TV.Cast() As TX
   Constructor TU(Byref As TX)

For copy-assignment with indirect conversion, the behavior is a bit more complex.
Usually:
'u = v'
performed via:
'u = x' with 'x = Cast(TX, v)'
so:
'TU.Let_from_TX( TV.Cast_to_TX ( v ) )'
But in the case where the third-party type is a UDT with an integer field (and not a string field) a second behavior is encountered if an explicit copy-assignment operator exists for the 'TU' type:
'u = v'
performed via:
'u = u0' with ('Dim As TU u0 = x' with 'x = Cast(TX, v)')
so:
'TU.Let_from_TU( TU.Ctor_from_TX( TV.Cast_to_TX( v ) ) )'
Code allowing to check all this, but parameterized here for the non-usual case

Code: Select all

'' TX type defined:
'#define TX_as_integer
'#define TX_as_integer_ptr
'#define TX_as_string
#define TX_as_UDT_with_integer
'#define TX_as_UDT_with_string

'' constructors defined:
'#define copy_constructor_TU
#define copy_assignment_TU

'----------------------------

#ifdef TX_as_integer
    Type TX As Integer
#endif

#ifdef TX_as_integer_ptr
    Type TX As Integer Ptr
#endif

#ifdef TX_as_string
    Type TX As String
#endif

#ifdef TX_as_UDT_with_integer
    Type TX
        Dim As Integer I
    End Type
#endif

#ifdef TX_as_UDT_with_string
    Type TX
        Dim As String s
    End Type
#endif

Type TU
    Dim As Integer I
    Declare Constructor()
    #ifdef copy_constructor_TU
        Declare Constructor(Byref u As TU)
    #endif
    #ifdef copy_assignment_TU
        Declare Operator Let(Byref u As TU)
    #endif
    Declare Constructor(Byref x As TX)
    Declare Operator Let(Byref x As TX)
End Type

Constructor TU()
End constructor

#ifdef copy_constructor_TU
    Constructor TU(Byref u As TU)
        Print "   Constructor TU(Byref As TU)"
    End Constructor
#endif

#ifdef copy_assignment_TU
    Operator TU.Let(Byref u As TU)
        Print "   Operator TU.Let(Byref As TU)"
    End Operator
#endif

Constructor TU(Byref x As TX)
    Print "   Constructor TU(Byref As TX)"
End Constructor

Operator TU.Let(Byref x As TX)
    Print "   Operator TU.Let(Byref As TX)"
End Operator

Type TV
    Dim As Integer I
    Declare Operator Cast() As TX
End Type

Operator TV.Cast() As TX
    Print "   Operator TV.Cast() As TX"
    Dim As TX x0
    Return x0
End Operator

Dim As TU u
Dim As TV v

Print "'u = v':"
u = v

Sleep

Code: Select all

'u = v':
   Operator TV.Cast() As TX
   Constructor TU(Byref As TX)
   Operator TU.Let(Byref As TU)
fxm
Moderator
Posts: 12081
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Conversion/Inheritance relating to UDTs via constructors and operators

Post by fxm »

  • .....
Conversions using User Data Type constructors and operators
  • For conversion between built-in types (among standard types like between numeric types as above or between string types), the compiler knows what to do without the need for instructions from user.
    This is called the implicit internal conversion (or coercion).

    When one of the two types is at least a UDT (User Defined Type), the user has to code some UDT procedures to define how do the conversion.
    Then, the conversion execution can be explicit if the user specifies what UDT procedure must be used, or implicit if the user leaves the choice to compiler.

    In the world of UDTs, conversions can be controlled by means of three member procedures:
    • - Single-argument constructor: allow conversion from a particular type to initialize an object.
      - Assignment operator: allow conversion from a particular type on assignment.
      - Type-cast operator: allow conversion to a particular type.
    For a construction with implicit initialization ('Dim As UDT u = v'), the compiler searches by priority:
    • - Firstly a matched constructor (for u type from v type).
      - Secondly a cast operator (for v type to special type (*)) and its matching conversion-constructor (for u type from special type (*)).
      - Thirdly finally a matched cast operator (for v type to u type).
      (a matched let operator (for u type from v type) is not searched by compiler on a construction with implicit initialization)
    For an implicit copy-construction ('Byval As u_type') when passing a v type parameter, the compiler searches by priority:
    • - Firstly a matched cast operator (for v type to u type).
      - Secondly a matched constructor (for u type from v type).
      - Thirdly finally a cast operator (for v type to special type (*)) and its matching conversion-constructor (for u type from special type (*)).
      (a matched let operator (for u type from v type) is not searched by compiler on a construction with implicit initialization)
    For an implicit assignment ('u = v'), or an implicit return from function by assigning ('Function = v') with function returning u type, the compiler searches by priority:
    • - Firstly a matched let operator (for u type from v type).
      - Secondly a cast operator (for v type to special type (*)) and its matching let operator (for u type from special type (*)).
      - Thirdly a matched cast operator (for v type to u type).
      - Fourthly finally a matched constructor (for u type from v type) and an explicit copy-let operator (u type).
    For an implicit return from function by exiting immediately ('Return v') with function returning u type, the compiler searches by priority:
    • - Firstly a matched constructor (for u type from v type).
      - Secondly a cast operator (for v type to special type (*)) and its matching conversion-constructor (for u type from special type (*)).
      If an explicit copy-constructor (u type) exists:
      - Thirdly a matched cast operator (for v type to u type).
      - Fourthly finally a matched let operator (for u type from v type).
      Else (an explicit copy-constructor (u type) does not exist):
      - Thirdly a matched let operator (for u type from v type).
      - Fourthly finally a matched cast operator (for v type to u type).
    special type (*) : pointer or string, or UDT if there is no explicit copy-constructor / explicit copy-assignment operator for u type
.....


@Jeff,
To summarize:

The previous posts of this thread allowed me to analyze the order of conversion priorities, for an instance 'u' of a TU type from an instance 'v' of a TV type, depending on the different constructors/operators available in the two types, and this for 5 basic expressions:
  • 1. 'Dim As TU u = v'
    2. 'Byval As TU' (from a TV instance)
    3. 'u = v'
    4. 'Function = v' (to a TU instance)
    5. 'Return v' (to a TU instance)
I found choosing the different conversion priorities (depending on the different constructors/operators available) mainly exhibits two weird cases and one troublesome case.


a) Weird case #1
  • In 4 out of 5 cases (1, 3, 4, 5), indirect conversion by a third-party TX type works for certain TX types, but even takes priority (priority 2) over direct conversion (priority 3 or 4), both using a Cast operator defined in the TV type:
    • explicit Cast(TX, v) + explicit TU.ctor/let(TX)
      takes priority over:
      explicit Cast(TU, v) + implicit TU.ctor/let(TU)
      and this for TX types like: pointer or string, or UDT if there is no explicit copy-constructor / explicit copy-assignment operator for TU type.
    Conclusion: Maybe do nothing ?

    Simplify example to highlight this behavior:
    - Priority of: explicit Cast(TX, v) + explicit TU.ctor/let(TX)

    Code: Select all

    '' constructors/operators defined:
    #define TV_cast_to_string
    
    
    Type TU
        Dim As Integer I
        Declare Constructor()
        Declare Constructor(Byref s As String)
        Declare Operator Let(Byref s As String)
    End Type
    
    Type TV
        Dim As Integer I
        Declare Operator Cast() Byref As TU
        #ifdef TV_cast_to_string
            Declare Operator Cast() As String
        #endif
    End Type
    
    Dim Shared As TU u0
    Dim Shared As TV v
    
    Constructor TU()
    End Constructor
    
    Constructor TU(Byref s As String)
        Print "   Constructor TU(Byref As String)"
    End Constructor
    
    Operator TU.Let(Byref s As String)
        Print "   Operator TU.Let(Byref As String)"
    End Operator
    
    Operator TV.Cast() Byref As TU
        Print "   Operator TV.Cast() As TU"
        Return u0
    End Operator
    
    #ifdef TV_cast_to_string
        Operator TV.Cast() As String
            Print "   Operator TV.Cast() As String"
            Return ""
        End Operator
    #endif
    
    
    Print "'Dim As TU u = v':"
    Dim As TU u = v
    Print
    
    Print "'u = v':"
    u0 = v
    Print
    
    Function f1() As TU
        Function = v
    End function
    Print "'Function = v' to TU:"
    f1()
    Print
    
    Function f2() As TU
        Return v
    End function
    Print "'Return v' to TU:"
    f2()
    Print
    
    Sleep
    

    Code: Select all

    'Dim As TU u = v':
       Operator TV.Cast() As String
       Constructor TU(Byref As String)
    
    'u = v':
       Operator TV.Cast() As String
       Operator TU.Let(Byref As String)
    
    'Function = v' to TU:
       Operator TV.Cast() As String
       Operator TU.Let(Byref As String)
    
    'Return v' to TU:
       Operator TV.Cast() As String
       Constructor TU(Byref As String)
    
    - Compared to: explicit Cast(TU, v) + implicit TU.ctor/let(TU)

    Code: Select all

    '' constructors/operators defined:
    '#define TV_cast_to_string
    
    
    Type TU
        Dim As Integer I
        Declare Constructor()
        Declare Constructor(Byref s As String)
        Declare Operator Let(Byref s As String)
    End Type
    
    Type TV
        Dim As Integer I
        Declare Operator Cast() Byref As TU
        #ifdef TV_cast_to_string
            Declare Operator Cast() As String
        #endif
    End Type
    
    Dim Shared As TU u0
    Dim Shared As TV v
    
    Constructor TU()
    End Constructor
    
    Constructor TU(Byref s As String)
        Print "   Constructor TU(Byref As String)"
    End Constructor
    
    Operator TU.Let(Byref s As String)
        Print "   Operator TU.Let(Byref As String)"
    End Operator
    
    Operator TV.Cast() Byref As TU
        Print "   Operator TV.Cast() As TU"
        Return u0
    End Operator
    
    #ifdef TV_cast_to_string
        Operator TV.Cast() As String
            Print "   Operator TV.Cast() As String"
            Return ""
        End Operator
    #endif
    
    
    Print "'Dim As TU u = v':"
    Dim As TU u = v
    Print
    
    Print "'u = v':"
    u0 = v
    Print
    
    Function f1() As TU
        Function = v
    End function
    Print "'Function = v' to TU:"
    f1()
    Print
    
    Function f2() As TU
        Return v
    End function
    Print "'Return v' to TU:"
    f2()
    Print
    
    Sleep
    

    Code: Select all

    'Dim As TU u = v':
       Operator TV.Cast() As TU
    
    'u = v':
       Operator TV.Cast() As TU
    
    'Function = v' to TU:
       Operator TV.Cast() As TU
    
    'Return v' to TU:
       Operator TV.Cast() As TU
    

b) Weird case #2
  • We can see above that:
    - Both the matched constructor (for u type from v type) and the matched let operator (for u type from v type) can be used (among the 5 possibilities) for the last three conversion cases,
    - but only the matched constructor (for u type from v type) can be used for the first two conversion cases, while we could also have had the matched let operator (for u type from v type) used (in a 4th possibility) for the first two conversion cases:
    • explicit TU.ctor(TV)
      is allowed, but not:
      explicit TU.let(TV) + explicit TU.ctor(TU)
      nor:
      explicit TU.let(TV) + explicit TU.ctor() + explicit TU.let(TU)
    Conclusion: Maybe do nothing ?

    Simplify example to highlight this behavior:
    - Conversion that works:

    Code: Select all

    '' constructor defined:
    #define TU_ctor_from_TV
    
    
    Type _TV As TV
    
    Type TU
        Dim As Integer I
        Declare Constructor()
        Declare Constructor(Byref u As TU)
        #ifdef TU_ctor_from_TV
            Declare Constructor(Byref v As _TV)
        #endif
        Declare Operator Let(Byref u As TU)
        Declare Operator Let(Byref v As _TV)
    End Type
    
    Type TV
        Dim As Integer I
    End Type
    
    Dim Shared As TV v
    
    Constructor TU()
        Print "   Constructor TU()"
    End Constructor
    
        Constructor TU(Byref u As TU)
            Print "   Constructor TU(Byref As TU)"
        End Constructor
    
    #ifdef TU_ctor_from_TV
        Constructor TU(Byref v As TV)
            Print "   Constructor TU(Byref As TV)"
        End Constructor
    #endif
    
    Operator TU.Let(Byref u As TU)
        Print "   Operator TU.Let(Byref As TU)"
    End Operator
    
    Operator TU.Let(Byref v As TV)
        Print "   Operator TU.Let(Byref As TV)"
    End Operator
    
    
    Print "'Dim As TU u = v':"
    Dim As TU u = v
    Print
    
    Sub s(Byval u As TU)
    End Sub
    Print "'Byval As TU' (from TV):"
    s(v)
    Print
    
    Sleep
    

    Code: Select all

    'Dim As TU u = v':
       Constructor TU(Byref As TV)
    
    'Byval As TU' (from TV):
       Constructor TU(Byref As TV)
    
    - Conversion that fails:

    Code: Select all

    '' constructor defined:
    '#define TU_ctor_from_TV
    
    
    Type _TV As TV
    
    Type TU
        Dim As Integer I
        Declare Constructor()
        Declare Constructor(Byref u As TU)
        #ifdef TU_ctor_from_TV
            Declare Constructor(Byref v As _TV)
        #endif
        Declare Operator Let(Byref u As TU)
        Declare Operator Let(Byref v As _TV)
    End Type
    
    Type TV
        Dim As Integer I
    End Type
    
    Dim Shared As TV v
    
    Constructor TU()
        Print "   Constructor TU()"
    End Constructor
    
        Constructor TU(Byref u As TU)
            Print "   Constructor TU(Byref As TU)"
        End Constructor
    
    #ifdef TU_ctor_from_TV
        Constructor TU(Byref v As TV)
            Print "   Constructor TU(Byref As TV)"
        End Constructor
    #endif
    
    Operator TU.Let(Byref u As TU)
        Print "   Operator TU.Let(Byref As TU)"
    End Operator
    
    Operator TU.Let(Byref v As TV)
        Print "   Operator TU.Let(Byref As TV)"
    End Operator
    
    
    Print "'Dim As TU u = v':"
    Dim As TU u = v
    Print
    
    Sub s(Byval u As TU)
    End Sub
    Print "'Byval As TU' (from TV):"
    s(v)
    Print
    
    Sleep
    
    Wished output for example:

    Code: Select all

    'Dim As TU u = v':
       Operator TU.Let(Byref As TV)
       Constructor TU(Byref As TU)
    
    'Byval As TU' (from TV):
       Operator TU.Let(Byref As TV)
       Constructor TU(Byref As TU)
    

c) Troublesome case
  • For the 2nd case 'Byval As TU' (from a TV instance)), the conversion using an operator Cast in the TV type has a higher priority (priority 1) than a direct conversion-construction in the TU type (priority 2):
    • explicit Cast(TU, v) + implicit TU.ctor(TU)
      takes priority over:
      explicit TU.ctor(TV)
    Conclusion: Maybe do something ?

    Very simplify example to highlight this behavior:
    - Priority 1:

    Code: Select all

    '' operator defined:
    #define TV_cast_to_TU
    
    
    Type _TV As TV
    
    Type TU
        Dim As Integer I
        Declare Constructor()
        Declare Constructor(Byref v As _TV)
    End Type
    
    Type TV
        Dim As Integer I
        #ifdef TV_cast_to_TU
            Declare Operator Cast() Byref As TU
        #endif
    End Type
    
    Dim Shared As TU u0
    Dim Shared As TV v
    
    Constructor TU()
        If @This <> @u0 Then
            Print "   Constructor TU()"
        End If
    End Constructor
    
    Constructor TU(Byref v As TV)
        Print "   Constructor TU(Byref As TV)"
    End Constructor
    
    #ifdef TV_cast_to_TU
        Operator TV.Cast() Byref As TU
            Print "   Operator TV.Cast() As TU"
            Return u0
        End Operator
    #endif
    
    
    Sub s(Byval u As TU)
    End Sub
    Print "'Byval As TU' (from TV):"
    s(v)
    Print
    
    Sleep
    

    Code: Select all

    'Byval As TU' (from TV):
       Operator TV.Cast() As TU
    
    Priority 2:

    Code: Select all

    '' operator defined:
    '#define TV_cast_to_TU
    
    
    Type _TV As TV
    
    Type TU
        Dim As Integer I
        Declare Constructor()
        Declare Constructor(Byref v As _TV)
    End Type
    
    Type TV
        Dim As Integer I
        #ifdef TV_cast_to_TU
            Declare Operator Cast() Byref As TU
        #endif
    End Type
    
    Dim Shared As TU u0
    Dim Shared As TV v
    
    Constructor TU()
    End Constructor
    
    Constructor TU(Byref v As TV)
        Print "   Constructor TU(Byref As TV)"
    End Constructor
    
    #ifdef TV_cast_to_TU
        Operator TV.Cast() Byref As TU
            Print "   Operator TV.Cast() As TU"
            Return u0
        End Operator
    #endif
    
    
    Sub s(Byval u As TU)
    End Sub
    Print "'Byval As TU' (from TV):"
    s(v)
    Print
    
    Sleep
    

    Code: Select all

    'Byval As TU' (from TV):
       Constructor TU(Byref As TV)
    
coderJeff
Site Admin
Posts: 4313
Joined: Nov 04, 2005 14:23
Location: Ontario, Canada
Contact:

Re: Conversion/Inheritance relating to UDTs via constructors and operators

Post by coderJeff »

@fxm, you certainly have a talent for finding issues that both should be addressed and are hard to fix! Very comprehensive analysis, good job.

For your last post
fxm wrote: Dec 03, 2022 14:42 a) Weird case #1
b) Weird case #2
c) Troublesome case
a) Weird case #1,
I think we should probably do nothing. The idea is that TU is something that can be initialized with a string, and TV is something that can be converted to a string. So we should like to be able to use TU in places that accept a string, and TV in places where we should provide a string. I think we are trying to find a balance between strict rules and flexible use. And this was driven by users, for example using TYPE to build custom string types. And even myself imagining that we might be able to build a dynamic wstring directly in fbc code that has all the behaviours of the string built-in type. But it could kind of go either way, and I don't think we make much effort on this one: either making it more restrictive and forcing explicit casts by the user to invoke constructors, or less restrictive and permit other built in types (like double, etc) to be used as a third-party conversions. Do nothing for now.

b) Weird case #2,
I don't know. It does not seem right that would should match LET statements on constructor assignments. Is this weird because of the differences with RETURN?

c) Troublesome case,
ya, this does seem like the constructor should be invoked instead of cast(). To pass byval we need a new instance of the type (this leads us to a different part of the compiler that where we were just working).

Working on c) might affect b), not sure. And it looks like should be able to change the order of some checks, but it is also a long complicated sequence of code in the fbc source. I think maybe make a new bug ticket for c) Troublesome case, as it will take some time before I can look in to the details.
fxm
Moderator
Posts: 12081
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Conversion/Inheritance relating to UDTs via constructors and operators

Post by fxm »

Thank you, I see that we are more or less of the same opinion.
Often the only purpose of my posts is to make an inventory and not necessarily to ask for corrections.
fxm
Moderator
Posts: 12081
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Conversion/Inheritance relating to UDTs via constructors and operators

Post by fxm »

coderJeff wrote: Dec 04, 2022 16:12 c) Troublesome case,
ya, this does seem like the constructor should be invoked instead of cast(). To pass byval we need a new instance of the type (this leads us to a different part of the compiler that where we were just working).

Working on c) might affect b), not sure. And it looks like should be able to change the order of some checks, but it is also a long complicated sequence of code in the fbc source. I think maybe make a new bug ticket for c) Troublesome case, as it will take some time before I can look in to the details.

About c) and 'Byval As UDT', I also saw other weird little things reported in previous posts:
[edit]

These two other weird little things are related to inheritance (Parent <- Child) and can be summarized in two cases:
  • 1. Construction of a Child instance from another Child instance.
    2. Construction of a Parent instance from a Child instance.
1) Construction of a Child instance from another Child instance
  • Code: Select all

    '' constructor/operator defined:
    #define Parent_ctor_from_Parent
    #define Parent_dtor
    
    Type Parent
        Dim As Integer I
        Declare Constructor()
        #ifdef Parent_ctor_from_Parent
            Declare Constructor(Byref p As Parent)
        #endif
        Declare Operator Let(Byref p As Parent)
        #ifdef Parent_dtor
            Declare Destructor()
        #endif
    End Type
    
    Type Child Extends Parent
    End Type
    
    Dim Shared As Child c1
    
    Constructor Parent()
        If @This <> @c1 Then
            Print "   Constructor Parent()"
        End if
    End Constructor
    
    #ifdef Parent_ctor_from_Parent
        Constructor Parent(Byref p As Parent)
            Print "   Constructor Parent(Byref As Parent)"
        End Constructor
    #endif
    
    Operator Parent.Let(Byref p As Parent)
        Print "   Operator Parent.Let(Byref As Parent)"
    End Operator
    
    #ifdef Parent_dtor
        Destructor Parent()
        End destructor
    #endif
    
    Print "'Dim As Child c2 = c1':"
    Dim As Child c2 = c1
    Print
    
    Sub s1c(Byval c As Child)
    End Sub
    Print "'Byval As Child' from Child:"
    s1c(c1)
    Print
    
    Function f1c() As Child
        Function = c1
    End function
    Print "'Function = c1' to Child:"
    f1c()
    Print
    
    Function f2c() As Child
        Return c1
    End function
    Print "'Return c1' to Child:"
    f2c()
    Print
    
    Sleep
    

    Code: Select all

    'Dim As Child c2 = c1':
       Constructor Parent()
       Operator Parent.Let(Byref As Parent)
    
    'Byval As Child' from Child:
       Constructor Parent()
       Operator Parent.Let(Byref As Parent)
    
    'Function = c1' to Child:
       Constructor Parent()
       Operator Parent.Let(Byref As Parent)
    
    'Return c1' to Child:
       Constructor Parent()
       Operator Parent.Let(Byref As Parent)
    
    Bad behavior of 'Byval As Child' (from Child) if there is no explicit destructor defined in Parent, whether there is an explicit copy-constructor defined in Parent or not:

    Code: Select all

    '' constructor/operator defined:
    #define Parent_ctor_from_Parent
    '#define Parent_dtor
    
    Type Parent
        Dim As Integer I
        Declare Constructor()
        #ifdef Parent_ctor_from_Parent
            Declare Constructor(Byref p As Parent)
        #endif
        Declare Operator Let(Byref p As Parent)
        #ifdef Parent_dtor
            Declare Destructor()
        #endif
    End Type
    
    Type Child Extends Parent
    End Type
    
    Dim Shared As Child c1
    
    Constructor Parent()
        If @This <> @c1 Then
            Print "   Constructor Parent()"
        End if
    End Constructor
    
    #ifdef Parent_ctor_from_Parent
        Constructor Parent(Byref p As Parent)
            Print "   Constructor Parent(Byref As Parent)"
        End Constructor
    #endif
    
    Operator Parent.Let(Byref p As Parent)
        Print "   Operator Parent.Let(Byref As Parent)"
    End Operator
    
    #ifdef Parent_dtor
        Destructor Parent()
        End destructor
    #endif
    
    Print "'Dim As Child c2 = c1':"
    Dim As Child c2 = c1
    Print
    
    Sub s1c(Byval c As Child)
    End Sub
    Print "'Byval As Child' from Child:"
    s1c(c1)
    Print
    
    Function f1c() As Child
        Function = c1
    End function
    Print "'Function = c1' to Child:"
    f1c()
    Print
    
    Function f2c() As Child
        Return c1
    End function
    Print "'Return c1' to Child:"
    f2c()
    Print
    
    Sleep
    

    Code: Select all

    'Dim As Child c2 = c1':
       Constructor Parent()
       Operator Parent.Let(Byref As Parent)
    
    'Byval As Child' from Child:
    
    'Function = c1' to Child:
       Constructor Parent()
       Operator Parent.Let(Byref As Parent)
    
    'Return c1' to Child:
       Constructor Parent()
       Operator Parent.Let(Byref As Parent)
    
    For 'Byval As Child' from Child: interne construction + shallow copy, instead of explicit default-constructor + explicit copy-let operator)
2) Construction of a Parent instance from a Child instance
  • Code: Select all

    '' constructor/operator defined:
    #define Parent_ctor_from_Parent
    #define Parent_dtor
    
    Type Parent
        Dim As Integer I
        Declare Constructor()
        #ifdef Parent_ctor_from_Parent
            Declare Constructor(Byref p As Parent)
        #endif
        Declare Operator Let(Byref p As Parent)
        #ifdef Parent_dtor
            Declare Destructor()
        #endif
    End Type
    
    Type Child Extends Parent
    End Type
    
    Dim Shared As Child c1
    
    Constructor Parent()
        If @This <> @c1 Then
            Print "   Constructor Parent()"
        End if
    End Constructor
    
    #ifdef Parent_ctor_from_Parent
        Constructor Parent(Byref p As Parent)
            Print "   Constructor Parent(Byref As Parent)"
        End Constructor
    #endif
    
    Operator Parent.Let(Byref p As Parent)
        Print "   Operator Parent.Let(Byref As Parent)"
    End Operator
    
    #ifdef Parent_dtor
        Destructor Parent()
        End destructor
    #endif
    
    Print "'Dim As Parent p = c1':"
    Dim As Parent P = c1
    Print
    
    Sub s1p(Byval p As Parent)
    End Sub
    Print "'Byval As Parent' from Child:"
    s1P(c1)
    Print
    
    Function f1p() As Parent
        Function = c1
    End function
    Print "'Function = c1' to Parent:"
    f1p()
    Print
    
    Function f2p() As Parent
        Return c1
    End function
    Print "'Return c1' to Parent:"
    f2p()
    Print
    
    Sleep
    

    Code: Select all

    'Dim As Parent p = c1':
       Constructor Parent(Byref As Parent)
    
    'Byval As Parent' from Child:
       Constructor Parent(Byref As Parent)
    
    'Function = c1' to Parent:
       Constructor Parent()
       Operator Parent.Let(Byref As Parent)
    
    'Return c1' to Parent:
       Constructor Parent(Byref As Parent)
    
    Bad behavior of 'Byval As Parent' (from Child) if there is no explicit copy-constructor defined in Parent, whether there is an explicit destructor defined in Parent or not:

    Code: Select all

    '' constructor/operator defined:
    '#define Parent_ctor_from_Parent
    #define Parent_dtor
    
    Type Parent
        Dim As Integer I
        Declare Constructor()
        #ifdef Parent_ctor_from_Parent
            Declare Constructor(Byref p As Parent)
        #endif
        Declare Operator Let(Byref p As Parent)
        #ifdef Parent_dtor
            Declare Destructor()
        #endif
    End Type
    
    Type Child Extends Parent
    End Type
    
    Dim Shared As Child c1
    
    Constructor Parent()
        If @This <> @c1 Then
            Print "   Constructor Parent()"
        End if
    End Constructor
    
    #ifdef Parent_ctor_from_Parent
        Constructor Parent(Byref p As Parent)
            Print "   Constructor Parent(Byref As Parent)"
        End Constructor
    #endif
    
    Operator Parent.Let(Byref p As Parent)
        Print "   Operator Parent.Let(Byref As Parent)"
    End Operator
    
    #ifdef Parent_dtor
        Destructor Parent()
        End destructor
    #endif
    
    Print "'Dim As Parent p = c1':"
    Dim As Parent P = c1
    Print
    
    Sub s1p(Byval p As Parent)
    End Sub
    Print "'Byval As Parent' from Child:"
    s1P(c1)
    Print
    
    Function f1p() As Parent
        Function = c1
    End function
    Print "'Function = c1' to Parent:"
    f1p()
    Print
    
    Function f2p() As Parent
        Return c1
    End function
    Print "'Return c1' to Parent:"
    f2p()
    Print
    
    Sleep
    

    Code: Select all

    'Dim As Parent p = c1':
       Constructor Parent()
       Operator Parent.Let(Byref As Parent)
    
    'Byval As Parent' from Child:
    
    'Function = c1' to Parent:
       Constructor Parent()
       Operator Parent.Let(Byref As Parent)
    
    'Return c1' to Parent:
       Constructor Parent()
       Operator Parent.Let(Byref As Parent)
    
    For 'Byval As Parent' from Child: interne construction + shallow copy, instead of explicit default-constructor + explicit copy-let operator
Last edited by fxm on Dec 06, 2022 21:36, edited 3 times in total.
Reason: Completed.
coderJeff
Site Admin
Posts: 4313
Joined: Nov 04, 2005 14:23
Location: Ontario, Canada
Contact:

Re: Conversion/Inheritance relating to UDTs via constructors and operators

Post by coderJeff »

fxm, sorry I have been working on a couple of issues with others lately.

With our latest features and issues we have been discussing you will need to (please) remind me what you think is next important item to make progress on. I may have lost track a little. :oops:
fxm
Moderator
Posts: 12081
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Conversion/Inheritance relating to UDTs via constructors and operators

Post by fxm »

From my point of view, the most important is to first consider the called 'c) Troublesome case' of conversion (from viewtopic.php?p=295865#p295865) without adding the inheritance context.

It is the only case of conversion (among the five below) where a direct conversion-constructor does not take priority compared to a Cast operator.
In the below quoted, it is the case in red:
  • .....
Conversions using User Data Type constructors and operators
  • For conversion between built-in types (among standard types like between numeric types as above or between string types), the compiler knows what to do without the need for instructions from user.
    This is called the implicit internal conversion (or coercion).

    When one of the two types is at least a UDT (User Defined Type), the user has to code some UDT procedures to define how do the conversion.
    Then, the conversion execution can be explicit if the user specifies what UDT procedure must be used, or implicit if the user leaves the choice to compiler.

    In the world of UDTs, conversions can be controlled by means of three member procedures:
    • - Single-argument constructor: allow conversion from a particular type to initialize an object.
      - Assignment operator: allow conversion from a particular type on assignment.
      - Type-cast operator: allow conversion to a particular type.
    For a construction with implicit initialization ('Dim As UDT u = v'), the compiler searches by priority:
    • - Firstly a matched constructor (for u type from v type).
      - Secondly a cast operator (for v type to special type (*)) and its matching conversion-constructor (for u type from special type (*)).
      - Thirdly finally a matched cast operator (for v type to u type).
      (a matched let operator (for u type from v type) is not searched by compiler on a construction with implicit initialization)
    For an implicit copy-construction ('Byval As u_type') when passing a v type parameter, the compiler searches by priority:
    • - Firstly a matched cast operator (for v type to u type).
      - Secondly a matched constructor (for u type from v type).
      - Thirdly finally a cast operator (for v type to special type (*)) and its matching conversion-constructor (for u type from special type (*)).
      (a matched let operator (for u type from v type) is not searched by compiler on a construction with implicit initialization)

    For an implicit assignment ('u = v'), or an implicit return from function by assigning ('Function = v') with function returning u type, the compiler searches by priority:
    • - Firstly a matched let operator (for u type from v type).
      - Secondly a cast operator (for v type to special type (*)) and its matching let operator (for u type from special type (*)).
      - Thirdly a matched cast operator (for v type to u type).
      - Fourthly finally a matched constructor (for u type from v type) and an explicit copy-let operator (u type).
    For an implicit return from function by exiting immediately ('Return v') with function returning u type, the compiler searches by priority:
    • - Firstly a matched constructor (for u type from v type).
      - Secondly a cast operator (for v type to special type (*)) and its matching conversion-constructor (for u type from special type (*)).
      If an explicit copy-constructor (u type) exists:
      - Thirdly a matched cast operator (for v type to u type).
      - Fourthly finally a matched let operator (for u type from v type).
      Else (an explicit copy-constructor (u type) does not exist):
      - Thirdly a matched let operator (for u type from v type).
      - Fourthly finally a matched cast operator (for v type to u type).

    special type (*) : pointer or string, or UDT if there is no explicit copy-constructor / explicit copy-assignment operator for u type

.....


I think that the two first priority levels must be swapped (like the other cases of conversion).

Simplest example to highlight this troublesome behavior:
- Priority 1:

Code: Select all

'' constructors/operators defined:
#define TU_ctor_from_TV
#define TU_ctor_from_String
#define TV_cast_to_String
#define TV_cast_to_TU

Type _TV As TV

Type TU
    Dim As Integer I
    Declare Constructor()
    #ifdef TU_ctor_from_TV
        Declare Constructor(Byref v As _TV)
    #endif
    #ifdef TU_ctor_from_String
        Declare Constructor(Byref s As String)
    #endif
End Type

Type TV
    Dim As Integer I
    #ifdef TV_cast_to_String
        Declare Operator Cast() As String
    #endif
    #ifdef TV_cast_to_TU
        Declare Operator Cast() As TU
    #endif
End Type

Constructor TU()
End Constructor

#ifdef TU_ctor_from_TV
    Constructor TU(Byref v As TV)
        Print "   Constructor TU(Byref As TV)"
    End Constructor
#endif

#ifdef TU_ctor_from_String
    Constructor TU(Byref s As String)
        Print "   Constructor TU(Byref As String)"
    End Constructor
#endif

#ifdef TV_cast_to_String
    Operator TV.Cast() As String
        Print "   Operator TV.Cast() As String"
        Return ""
    End Operator
#endif

#ifdef TV_cast_to_TU
    Operator TV.Cast() As TU
        Print "   Operator TV.Cast() As TU"
        Return TU()
    End Operator
#endif

Sub s1(Byval u As TU)
End Sub

Print "'Byval As TU' (from TV):"
s1(Type<TV>(0))
Print

Sleep

Code: Select all

'Byval As TU' (from TV):
   Operator TV.Cast() As TU
- Priority 2:

Code: Select all

'' constructors/operators defined:
#define TU_ctor_from_TV
#define TU_ctor_from_String
#define TV_cast_to_String
'#define TV_cast_to_TU

Type _TV As TV

Type TU
    Dim As Integer I
    Declare Constructor()
    #ifdef TU_ctor_from_TV
        Declare Constructor(Byref v As _TV)
    #endif
    #ifdef TU_ctor_from_String
        Declare Constructor(Byref s As String)
    #endif
End Type

Type TV
    Dim As Integer I
    #ifdef TV_cast_to_String
        Declare Operator Cast() As String
    #endif
    #ifdef TV_cast_to_TU
        Declare Operator Cast() As TU
    #endif
End Type

Constructor TU()
End Constructor

#ifdef TU_ctor_from_TV
    Constructor TU(Byref v As TV)
        Print "   Constructor TU(Byref As TV)"
    End Constructor
#endif

#ifdef TU_ctor_from_String
    Constructor TU(Byref s As String)
        Print "   Constructor TU(Byref As String)"
    End Constructor
#endif

#ifdef TV_cast_to_String
    Operator TV.Cast() As String
        Print "   Operator TV.Cast() As String"
        Return ""
    End Operator
#endif

#ifdef TV_cast_to_TU
    Operator TV.Cast() As TU
        Print "   Operator TV.Cast() As TU"
        Return TU()
    End Operator
#endif

Sub s1(Byval u As TU)
End Sub

Print "'Byval As TU' (from TV):"
s1(Type<TV>(0))
Print

Sleep

Code: Select all

'Byval As TU' (from TV):
   Constructor TU(Byref As TV)
- Priority 3:

Code: Select all

'' constructors/operators defined:
'#define TU_ctor_from_TV
#define TU_ctor_from_String
#define TV_cast_to_String
'#define TV_cast_to_TU

Type _TV As TV

Type TU
    Dim As Integer I
    Declare Constructor()
    #ifdef TU_ctor_from_TV
        Declare Constructor(Byref v As _TV)
    #endif
    #ifdef TU_ctor_from_String
        Declare Constructor(Byref s As String)
    #endif
End Type

Type TV
    Dim As Integer I
    #ifdef TV_cast_to_String
        Declare Operator Cast() As String
    #endif
    #ifdef TV_cast_to_TU
        Declare Operator Cast() As TU
    #endif
End Type

Constructor TU()
End Constructor

#ifdef TU_ctor_from_TV
    Constructor TU(Byref v As TV)
        Print "   Constructor TU(Byref As TV)"
    End Constructor
#endif

#ifdef TU_ctor_from_String
    Constructor TU(Byref s As String)
        Print "   Constructor TU(Byref As String)"
    End Constructor
#endif

#ifdef TV_cast_to_String
    Operator TV.Cast() As String
        Print "   Operator TV.Cast() As String"
        Return ""
    End Operator
#endif

#ifdef TV_cast_to_TU
    Operator TV.Cast() As TU
        Print "   Operator TV.Cast() As TU"
        Return TU()
    End Operator
#endif

Sub s1(Byval u As TU)
End Sub

Print "'Byval As TU' (from TV):"
s1(Type<TV>(0))
Print

Sleep

Code: Select all

'Byval As TU' (from TV):
   Operator TV.Cast() As String
   Constructor TU(Byref As String)
coderJeff
Site Admin
Posts: 4313
Joined: Nov 04, 2005 14:23
Location: Ontario, Canada
Contact:

Re: Conversion/Inheritance relating to UDTs via constructors and operators

Post by coderJeff »

fxm wrote: Dec 18, 2022 20:01 From my point of view, the most important is to first consider the called 'c) Troublesome case' of conversion (from viewtopic.php?p=295865#p295865) without adding the inheritance context.
Thank-you. I found where I left off from a few weeks ago. I had a change I had started working on. There is some existing logic in the compiler I need to work on understanding. It looks like this behaviour goes back several fbc versions.
coderJeff
Site Admin
Posts: 4313
Joined: Nov 04, 2005 14:23
Location: Ontario, Canada
Contact:

Re: Conversion/Inheritance relating to UDTs via constructors and operators

Post by coderJeff »

fxm, thank-you for writing the test cases.

I have converted these two to tests in the test-suite

Fully implemented:
viewtopic.php?p=295606#p295606

A few variations implemented:
viewtopic.php?p=295637#p295637

There is 1000's of combinations of types TU, TV, TX that could be implemented so I only added a few to start with. I think it would be easier to solve chess than exhaustively test every combination of types. lol.

I have an idea how to correct the troublesome case posted, but thought I should try and get some of these tests in to the test-suite to help check any regressions.
Post Reply