Pointers to Procedures

Pointers that point to procedures

   Just as pointers can be made to point to an Integer or Single type, 
   pointers can also point to procedures, that is, they can store the 
   address of a procedure.

Declaration
   To declare a pointer to procedure, use the Sub or Function keywords, 
   followed by any parameters and return value type:
   ' declares a pointer to sub procedure that takes no arguments
   Dim pointerToProcedure As Sub
         

   Procedure pointers store procedure addresses, which are retrieved using 
   Operator @ (Address of) or the Procptr Operator:
   '' pfunc.bi

   Function Add (a As Integer, b As Integer) As Integer
      Return a + b
   End Function

   Dim pFunc As Function (As Integer, As Integer) As Integer = @Add
         

Calling a procedure pointer
   The interesting thing about procedure pointers is that they can be 
   called just like a procedure:
   '' .. Add and pFunc as before ..
   #include Once "pfunc.bi"

   Print "3 + 4 = " & pFunc(3, 4)
         

   For a calling example of subroutine pointer, see the 
   Operator @ (Address Of) page.

   Note: When calling a procedure through a procedure pointer, parentheses 
   surrounding the argument list (even empty) are mandatory to resolve 
   ambiguity with a simple access to the pointer value.

Passing procedure pointers to procedures
   Passing procedure pointers to other procedures is similar as well:
   '' .. Add and pFunc as before ..
   #include Once "pfunc.bi"

   Function DoOperation (a As Integer, b As Integer, operation As Function (As Integer, As Integer) As Integer) As Integer
      Return operation(a, b)
   End Function

   Print "3 + 4 = " & DoOperation(3, 4, @Add)
         

   Because procedure pointer declarations can be lengthy, it often helps to 
   create a type alias for the procedure pointer, in an effort to make 
   clearer code:
   '' .. Add and pFunc as before ..
   #include Once "pfunc.bi"

   Type operation As Function (As Integer, As Integer) As Integer

   Function DoOperation (a As Integer, b As Integer, op As operation) As Integer
      Return op(a, b)
   End Function

   Print "3 + 4 = " & DoOperation(3, 4, @Add)
         

Pointers to procedure pointers
   Because the syntax of a procedure pointer does not allow declaration of 
   a pointer to procedure pointer when the procedure is a function (because 
   ptr applies on return type and not on procedure), a type alias is used. 
   Notice how it is necessary to surround a dereferenced pointer to 
   procedure pointer by parenthesis when calling the procedure. This is 
   because the function-call operator '()' has higher precedence than 
   Operator * (Value of):
   Function Halve (ByVal i As Integer) As Integer
      Return i / 2
   End Function

   Function Triple (ByVal i As Integer) As Integer
      Return i * 3
   End Function

   Type operation As Function (ByVal As Integer) As Integer

   ' an array of procedure pointers, NULL indicates the
   ' end of the array
   Dim operations(20) As operation = _
   { @Halve, @Triple, 0 }

   Dim i As Integer = 280

   ' apply all of the operations to a variable by iterating through the array
   ' with a pointer to procedure pointer
   Dim op As operation Ptr = @operations(0)
   While (*op <> 0)
      ' call the procedure that is pointed to, note the extra parenthesis
      i = (*op)(i)
      op += 1
   Wend

   Print "Value of 'i' after all operations performed: " & i
         

Pointers to member procedures
   Method pointers are not implemented yet, but it is possible to 
   work-around that by using a static wrapper:
   /''
    ' This example shows how you can simulate getting a class method pointer, 
    ' until support is properly implemented in the compiler.
    '
    ' When this is supported, you will only need to remove the static wrapper
    ' function presented here, to maintain compatibility. 
    '/

   Type T
      Declare Function test(ByVal number As Integer) As Integer
      Declare Static Function test(ByRef This As T, ByVal number As Integer) As Integer
      Dim As Integer i = 420
   End Type

   Function T.test(ByVal number As Integer) As Integer
      Return i + number
   End Function

   Function T.test(ByRef This As T, ByVal number As Integer) As Integer
      Return this.test(number)
   End Function

   Dim p As Function(ByRef As T, ByVal As Integer) As Integer
   p = @T.test

   Dim As T obj

   Print p(obj, 69) '' prints 489
      

Typing rule for procedure pointer declaration
   The procedure pointer declaration allows to assign to the pointer:
      * not only a procedure with the same parameters types, and if any, 
        the same result type,
      * but also a procedure with contravariant parameters (by reference 
        or by pointer) or/and a covariant result (by reference or by 
        pointer).

   Assigning to a function pointer a fonction with a contravariant 
   parameter and a covariant result, both by pointer:		
   'Example of assigning to a function pointer a function with:
   '   - a contravariant parameter by pointer,
   '   - and a covariant result by pointer.

   Type A
      Dim As Integer I
      Declare Constructor ()
      Declare Destructor ()
   End Type
   Constructor A ()
      Print "    A instance constructed", @This
   End Constructor
   Destructor A ()
      Print "    A instance destroyed", @This
   End Destructor

   Type B Extends A
      Dim As Integer J
      Declare Constructor ()
      Declare Constructor (ByRef a0 As A)
      Declare Destructor ()
   End Type
   Constructor B ()
      Print "    B instance constructed", @This
   End Constructor
   Constructor B (ByRef a0 As A)
      Cast(A, This) = a0
      Print "    B instance constructed", @This
   End Constructor
   Destructor B ()
      Print "    B instance destroyed", @This
   End Destructor

   Function f (ByVal pa0 As A Ptr) As B Ptr
      Return New B(*pa0)
   End Function

   Scope
      Dim As Function (ByVal As B Ptr) As A Ptr pf = @f
      Print "'Scope : Dim As B b0':"
      Dim As B b0
      Print
      Print "'Dim As A Ptr pab = pf(@b0)':"
      Dim As A Ptr pab = pf(@b0)
      Print
      Print "'Delete CPtr(B Ptr, pab)':"
      Delete CPtr(B Ptr, pab)
      Print
      Print "'End Scope':"
   End Scope

   Sleep
         

   Assigning to a function pointer a function with a contravariant 
   parameter and a covariant result, both by reference:
   'Example of assigning to a function pointer a function with:
   '   - a contravariant parameter by reference,
   '   - and a covariant result by reference.

   Type A Extends Object
      Dim As Integer I
      Declare Constructor ()
      Declare Virtual Destructor ()
   End Type
   Constructor A ()
      Print "    A instance constructed", @This
   End Constructor
   Destructor A ()
      Print "    A instance destroyed", @This
   End Destructor

   Type B Extends A
      Dim As Integer J
      Declare Constructor ()
      Declare Constructor (ByRef a0 As A)
      Declare Virtual Destructor ()
   End Type
   Constructor B ()
      Print "    B instance constructed", @This
   End Constructor
   Constructor B (ByRef a0 As A)
      Cast(A, This) = a0
      Print "    B instance constructed", @This
   End Constructor
   Destructor B ()
      Print "    B instance destroyed", @This
   End Destructor

   Function f (ByRef a0 As A) ByRef As B
      Return *New B(a0)
   End Function

   Scope
      Dim As Function (ByRef As B) ByRef As A pf = @f
      Print "'Scope : Dim As B b0':"
      Dim As B b0
      Print
      Print "'Dim Byref As A rab = pf(b0)':"
      Dim ByRef As A rab = pf(b0)
      Print
      Print "'Delete @rab':"
      Delete @rab
      Print
      Print "'End Scope':"
   End Scope

   Sleep
         

See also
   * Sub
   * Function
   * Pointer
   * Operator @ (Address Of)
   * Procptr Operator

