Using References

The syntax for declaring the References and Using them instead of pointers.

Preamble:

   The different syntaxes used to declare a reference all use the 'Byref' 
   keyword.
   Since a pointer is a variable, it is possible to modify its contents, 
   and the same pointer can allow successive access to different variables. 
   The association between a reference and the object that it designates 
   is, however, fixed when it is declared.

   The 'Byref' keyword indicates a variable that is declared by reference. 
   It is used in three different contexts:
      - In a procedure signature, to pass an argument by reference (byref 
      parameter).
      - In a function signature, to return a variable to the caller by 
      reference (byref return).
      - In the body of the code, to define a reference variable (byref 
      variable).

   Table of Contents
1. Passing parameter by reference to procedure (byref parameter)
2. Returning variable by reference from function (byref return)
3. Defining reference variable in code (byref variable)
4. References versus pointers by comparative examples
5. Hacking on usage of references with the additional syntaxes allowed by FreeBASIC

1. Passing parameter by reference to procedure (byref parameter)
   * Syntax of declaration:
      {Sub|Function} procedure_name (ByRef parameter As [Const] datatype, 
      ...

      When used in the parameter list of a procedure, the Byref keyword 
      indicates that an argument is passed by reference, not by value. The 
      consequence is that any modification made to the argument in the 
      called procedure is reflected in the body of the call.
      If the procedure does not need or must not to modify the transmitted 
      object, the Const qualifier can be used in the declaration (before 
      the declaration of the datatype) so that the compiler checks in the 
      body of the procedure that the passed object is not modified in any 
      place (otherwise, a compiler error message is issued).

   * Full syntax example for passing a parameter by reference:
   Declare Sub passbyref (ByRef ref As Double, ByVal value As Double)  '' declaration for passing by reference

   Dim As Double X = 0
   Print X
   passbyref(X, 1.23)
   Print X

   Sleep

   Sub passbyref (ByRef ref As Double, ByVal value As Double)  '' declaration for passing by reference
      ref = value
   End Sub
         
Output:

    0
    1.23
   			

   Note: A pointer can be passed directly (without first dereferencing it) 
   to Byref procedure parameter if in argument term the Byval keyword is 
   specified in front of the pointer name.

Back to top

2. Returning variable by reference from function (byref return)
   * Syntax of declaration:
      Function function_name (...) ByRef As [Const] datatype

      When used in the return type of a function, the Byref keyword 
      indicates that the variable is returned by reference, not by value. 
      The consequence is that the caller can modify the variable returned 
      by the function and the modification is reflected in the state of the 
      variable that the function processes.
      If the caller does not need or must not to modify the transmitted 
      object, the Const qualifier can be used in the declaration (before 
      the declaration of the datatype) so that the compiler checks in the 
      body of the caller that the returned object is not modified in any 
      place (otherwise, a compiler error message is issued).

      Operators (member or global), when used as functions, have also the 
      capability to return results by reference, by using the similar 
      syntax.

   * Full syntax example for returning a variable by reference:
   Declare Function returnbyref () ByRef As Double  '' declaration for returning by reference

   Print returnbyref()
   returnbyref() = 4.56
   Print returnbyref()

   Sleep

   Function returnbyref () ByRef As Double  '' declaration for returning by reference
      Static As Double X = 0
      Return X
   End Function
         
Output:

    0
    4.56
   		
As for the arguments list, it should always be surrounded with parentheses 
even if empty.

      Specific syntax:
         On the left-hand side of an assignment expression using the '=' 
         symbol, the result of the function (returned by reference) must be 
         enclosed in parentheses when the function calls one single 
         argument, in order to solve the parsing ambiguity.
         From fbc version 0.90, '=>' can be used for assignments, in place 
         of '=', same as for initializers, allowing to avoid parsing 
         ambiguity (without parentheses):
   Declare Function transitbyref( ByRef _s As String ) ByRef As String

   Dim As String s

   s = "abcd"
   Print s

   '' the enclosing parentheses are required here.
   ( transitbyref( s ) ) = transitbyref( s ) & "efgh"
   Print s

   '' the enclosing parentheses are not required here.
   transitbyref( s ) => transitbyref( s ) & "ijkl"
   Print s

   Sleep

   Function transitbyref( ByRef _s As String ) ByRef As String
      '' This var-len string will transit by reference (input and output), no copy will be created.
      Return _s
   End Function
               
Output:

   abcd
   abcdefgh
   abcdefghijkl
   					

   Note: A pointer can be returned directly (without first dereferencing 
   it) for a Byref function return if in identifier= or Function= or Return 
   statement the Byval keyword is specified in front of the pointer name.

Back to top

3. Defining reference variable in code (byref variable)
   * Syntax of declaration:
         {Dim|Static} [Shared] ByRef As [Const] datatype ref = variable
      or
         [Static] Var [Shared] ByRef ref = variable

      Unlike pointers, the reference variable must be assigned as soon as 
      the declaration using an initializer.
      datatype must be the same type as that of the variable, or a 
      compatible type (for example one from the types of its Bases in case 
      of inheritance):
         - Only when the two types are identical (or using the second 
         syntax with Var), a reference variable can be considered as an 
         alias of the variable. One can do the same operations through such 
         a reference variable as one can do with the original variable.
         - Otherwise (types compatible but not identical), one can not do 
         all same operations than with the original variable:
            For example, a base type reference variable referring to a 
            derived type object allows to activate polymorphism when a 
            virtual method is called on it, similarly to a base type 
            pointer referring to a derived type object. One can do the same 
            operations through such a reference variable as one can do with 
            a dereferenced pointer of same type (but for both not the same 
            operations as using directly the derived type instance).

      If the code does not need or must not to modify the referred object, 
      the Const qualifier can be used in the declaration (before the 
      declaration of the data_type in the first syntax) so that the 
      compiler checks in the code that the object is not modified, through 
      the reference variable, in any place (otherwise, a compiler error 
      message is issued).

      There is no interaction between the life of a reference and the life 
      of the object who is referred (similarly to a pointer: destroy an 
      object does not destroy its pointer(s)).
      Once created, each one lives his life independently.

   * Full syntax example for defining a reference variable in code:
   Dim As Double X = 0

   Dim ByRef As Double refX = X  '' declaration for defining a reference
   Print X
   refX = 7.89
   Print X

   Sleep
         
Output:

    0
    7.89

Back to top

4. References versus pointers by comparative examples
   * Function returning the greater variable between two integer 
     variables:
      - Using pointers (by passing/returning pointer variables):
   Function maxPtr (ByVal p1 As Integer Ptr, ByVal p2 As Integer Ptr) As Integer Ptr
      If *p1 > *p2 Then
         Return p1
      Else
         Return p2
      End If
   End Function

   Dim As Integer i1 = 1, i2 = 2
   Print i1, i2
   *maxPtr(@i1, @i2) = 3
   Print i1, i2

   Sleep
            
Output:

    1             2
    1             3
   				

      - Using references (by passing/returning reference variables):
   Function maxRef (ByRef r1 As Integer, ByRef r2 As Integer) ByRef As Integer
      If r1 > r2 Then
         Return r1
      Else
         Return r2
     End If
   End Function

   Dim As Integer i1 = 1, i2 = 2
   Print i1, i2
   maxRef(i1, i2) = 3
   Print i1, i2

   Sleep
            
Output:

    1             2
    1             3
   				

               * Inheritance structure with overriding subroutine and 
                 overriding function with covariant return:
      - Using pointers to objects:
   Type myBase Extends Object
      Declare Virtual Function clone () As myBase Ptr
      Declare Virtual Sub Destroy ()
   End Type

   Function myBase.clone () As myBase Ptr
      Dim As myBase Ptr pp = New myBase(This)
      Print "myBase.clone() As myBase Ptr", pp
      Function = pp
   End Function

   Sub myBase.Destroy ()
      Print "myBase.Destroy()", , @This
      Delete @This
   End Sub

   Type myDerived Extends myBase
      Declare Function clone () As myDerived Ptr Override  '' overriding member function with covariant return
      Declare Sub Destroy () Override                      '' overriding member subroutine
   End Type

   Function myDerived.clone () As myDerived Ptr      '' overriding member function with covariant return
      Dim As myDerived Ptr pc = New myDerived(This)
      Print "myDerived.clone() As myDerived Ptr", pc
      Function = pc
   End Function

   Sub myDerived.Destroy ()                '' overriding member subroutine
      Print "myDerived.Destroy()", , @This
      Delete @This
   End Sub

   Dim As myDerived c

   Dim As myBase Ptr ppc = @c                '' base type pointer to derived object c
   Dim As myDerived Ptr pcc = @c             '' derived type pointer to derived object c

   Dim As myBase Ptr ppc1 = ppc->clone()     '' base type pointer to clone of object c
   '                                              (through its base type pointer and polymorphism)
   Dim As myDerived Ptr pcc1 = pcc->clone()  '' derived type pointer to derived object c
   '                                              (through its derived type pointer and covariance of return value)
   Print
   ppc1->Destroy()                           '' using base type pointer and polymorphism
   pcc1->Destroy()                           '' using derived type pointer

   Sleep
            
Output example:

   myDerived.clone() As myDerived Ptr        4663904
   myDerived.clone() As myDerived Ptr        4663952

   myDerived.Destroy()                       4663904
   myDerived.Destroy()                       4663952
   				

      - Using references to objects:
   Type myBase Extends Object
      Declare Virtual Function clone () ByRef As myBase
      Declare Virtual Sub Destroy ()
   End Type

   Function myBase.clone () ByRef As myBase
      Dim As myBase Ptr pp = New myBase(This)
      Print "myBase.clone() Byref As myBase", pp
      Function = *pp
   End Function

   Sub myBase.Destroy ()
      Print "myBase.Destroy()", , @This
      Delete @This
   End Sub

   Type myDerived Extends myBase
      Declare Function clone () ByRef As myDerived Override  '' overriding member function with covariant return
      Declare Sub Destroy () Override                        '' overriding member subroutine
   End Type

   Function myDerived.clone () ByRef As myDerived      '' overriding member function with covariant return
      Dim As myDerived Ptr pc = New myDerived(This)
      Print "myDerived.clone() Byref As myDerived", pc
      Function = *pc
   End Function

   Sub myDerived.Destroy ()                '' overriding member subroutine
      Print "myDerived.Destroy()", , @This
      Delete @This
   End Sub

   Dim As myDerived c

   Dim ByRef As myBase rpc = c                '' base type reference to derived object c
   Dim ByRef As myDerived rcc = c             '' derived type reference to derived object c

   Dim ByRef As myBase rpc1 = rpc.clone()     '' base type reference to clone of object c
   '                                               (through its base type reference and polymorphism)
   Dim ByRef As myDerived rcc1 = rcc.clone()  '' derived type reference to derived object c
   '                                               (through its derived type reference and covariance of return value)
   Print
   rpc1.Destroy()                             '' using base typpe reference and polymorphism
   rcc1.Destroy()                             '' using derived type reference

   Sleep
            
Output example:

   myDerived.clone() ByRef As myDerived      9775712
   myDerived.clone() ByRef As myDerived      9775760

   myDerived.Destroy()                       9775712
   myDerived.Destroy()                       9775760

Back to top

5. Hacking on usage of references with the additional syntaxes allowed by 
FreeBASIC
   In FB, a reference is implemented under the hood through an internal 
   pointer which holds the address of the variable.

   The access to this internal pointer is presently allowed for user, in 
   read, and also in write for a reference variable (unlike many other 
   languages):
      - Therefore, the address of the referred variable (the value of the 
      internal pointer) can be get by using the '@' operator applied on the 
      reference variable symbol name:
         variable_address = @ref
      - And even, a reference variable can be reassigned (by modifying the 
      value of the internal pointer) to refer to another variable (of 
      compatible type) by doing:
         @ref = @other_variable
      - The address of the internal pointer of a reference variable can 
      even be obtained:
         internal_pointer_address = @@ref
      Note:
         - A reference variable can also be re-initialized to a "null" 
         reference:
            @ref = 0
         - A reference variable can even be directly declared as a "null" 
         reference:
            Dim ByRef As datatype ref = *CPtr(datatype Ptr, 0)

   Thus, by always using the same reference symbol name, one can mix the 
   pure syntax on the reference with the syntax on its internal pointer.

   * Example of hacking on reference symbol name:
   Declare Function resizeZstring (ByRef refZstring As ZString, ByVal length As Integer) ByRef As ZString
   Declare Sub prntZstring (ByRef refZstring As ZString)

   Dim ByRef As ZString refZ = *CPtr(ZString Ptr, 0)  '' "null" reference declaration

   Const cz1 = "FB"
   @refZ = @(resizeZstring(refZ, Len(cz1)))           '' reference (re-)inititialization
   refZ = cz1
   prntZstring(refZ)

   Const cz2 = "FreeBASIC"
   @refZ = @(resizeZstring(refZ, Len(cz2)))           '' reference re-inititialization
   refZ = cz2
   prntZstring(refZ)

   Const cz3 = "FreeBASIC 1.06.0"
   @refZ = @(resizeZstring(refZ, Len(cz3)))           '' reference re-inititialization
   refZ = cz3
   prntZstring(refZ)

   Const cz4 = ""
   @refZ = @(resizeZstring(refZ, Len(cz4)))           '' reference re-inititialization to "null" reference
   refZ = cz4
   prntZstring(refZ)

   Sleep

   Function resizeZstring (ByRef refZstring As ZString, ByVal length As Integer) ByRef As ZString
      If length > 0 Then
         If @refZstring = 0 Then
            Print "Zstring memory buffer allocation"
         Else
            Print "Zstring memory buffer re-allocation"
         End If
         length += 1
      Else
         Print "Zstring memory buffer de-allocation"
      End If
   '	Return *Cptr(Zstring Ptr, Reallocate(@refZstring, length * Sizeof(Zstring)))
   '	'' Using the "Return Byval ..." syntax allows to avoid casting + dereferencing as above
      Return ByVal Reallocate(@refZstring, length * SizeOf(ZString))
   End Function

   Sub prntZstring (ByRef refZstring As ZString)
      Print "  " & @refZstring, "'" & refZstring & "'"
      Print
   End Sub
         
Output example:

   ZString memory buffer allocation
     9513600     'FB'

   ZString memory buffer re-allocation
     9513600     'FreeBASIC'

   ZString memory buffer re-allocation
     9513600     'FreeBASIC 1.06.0'

   ZString memory buffer de-allocation
     0           ''

Back to top

See also
   * Byref (Parameters), Byref (Function Results), Byref (Variables)
   * Operator @ (Address Of), Operator * (Value Of)
   * From Pointers to References

