Sub Pointer

Data type that stores a pointer to a Sub procedure

Syntax
   Dim variable As Sub [cdecl|pascal|stdcall] [( [parameter_list] )] [= 
   initializer]

Parameters
      parameter_list: parameter[, parameter[, ...]]
      parameter: [ByRef|ByVal] identifier [As type] [= default_value]
         identifier: the name of the variable referenced in the subroutine
         type: the type of variable
         default_value: the value of the argument if none is specified in 
         the call
      intializer: address of a subroutine to set as the intial value

Description
   A Sub pointer is a procedure pointer that stores the memory location of 
   compiled code.  If no intializer is given the default initial value is 
   zero (0).

   The memory address for the Sub procedure can be assigned to the variable 
   by taking the address of a subroutine with ProcPtr or 
   Operator @ (Address Of).

   The procedure must match the same Sub declaration as the declared Sub 
   pointer.

   To call the subroutine assigned, use the variable name as if it were a 
   normal declared Sub, always with parentheses around the parameter list 
   even empty (without parentheses, only the pointer value, ie the address 
   of the subroutine, would be accessed).

   One of the primary uses for Sub pointers is to create callback 
   procedures:
      - A callback Sub is a Sub that is passed through an argument (a Sub 
      pointer) to another procedure which is expected to call back 
      (execute) the "argument" at a convenient time.
      - If the callback Sub is completely executed before the invocation 
      returns to the caller code, then the callback process is said to be 
      "synchronous".
      - If the invocation immediately returns to the caller code, and the 
      callback Sub and the caller's next code are running in parallel, then 
      the callback process is said to be "asynchronous".

Example
      Sub Hello()
         Print "Hello"
      End Sub

      Sub Goodbye()
         Print "Goodbye"
      End Sub

      Dim x As Sub() = ProcPtr( Hello )

      x()

      x = @Goodbye  '' or procptr(Goodbye)

      x()

   Sub s0 ()
     Print "'s0 ()'"
   End Sub

   Sub s1 (ByVal I As Integer)
     Print "'s1 (Byval As Integer)'", I
   End Sub

   Sub s2 (ByRef S As String, ByVal D As Double)
     Print "'s2 (Byref As String, Byval As Double)'", S, D
   End Sub

   Dim s0_ptr As Sub () = @s0
   Dim s1_ptr As Sub (ByVal I As Integer) = @s1
   Dim s2_ptr As Sub (ByRef S As String, ByVal D As Double) = @s2

   s0_ptr()
   s1_ptr(3)
   s2_ptr("PI", 3.14)

   ' Example of advanced callback Sub mechanism (asynchronous) to implement a key pressed event:
   ' (the user callback Sub address can be modified while the event thread is running)
   '   - An asynchronous thread tests the keyboard in a loop, and calls a user callback Sub each time a key is pressed.
   '   - An UDT groups the common variables used (callback Sub pointer, character of key pressed, thread end flag),
   '       and the static thread Sub plus the thread handle.
   '   - An UDT instance pointer is passed to the thread, which then transmits it to the callback Sub each time.
   '   - The callback Sub prints the character of the key pressed character,
   '       but if the key pressed is <escape> it orders the thread to finish.
   '   - As the user callback pointer is a member field of the UDT, it can be modified while the thread is running.

   '' UDT for thread environment
     Type threadUDT
      Dim As Sub (ByVal As ThreadUDT Ptr) callback             '' callback Sub pointer
      Dim As Integer threadEnd                                 '' thread end flag
      Dim As String s                                          '' character of the key pressed
      Declare Static Sub threadInkey (ByVal p As Any Ptr)      '' static thread Sub
      Dim As Any Ptr threadHandle                              '' handle to the thread
     End Type

   '' thread Sub definition
     Sub threadUDT.threadInkey (ByVal p As Any Ptr)
      Dim As threadUDT Ptr pt = p                              '' convert the any ptr to a threadUDT pointer
      Do
        pt->s = Inkey
        If pt->s <> "" AndAlso pt->callback > 0 Then           '' test condition key pressed & callback Sub defined
         pt->callback(p)
        End If
        Sleep 50, 1
      Loop Until pt->threadEnd                                 '' test condition to finish thread
     End Sub

   '' user callback Sub definition
     Sub printInkey (ByVal pt As threadUDT Ptr)
      If Asc(pt->s) = 27 Then                                  '' test condition key pressed = <escape>
        pt->threadEnd = -1                                     '' order thread to finish
        Print
      Else
        Print pt->s;
      End If
     End Sub

   '' user main code
     Dim As ThreadUDT t                                         '' create an instance of threadUDT
     t.threadHandle = ThreadCreate(@threadUDT.threadInkey, @t)  '' launch the thread, passing the instance address
     t.callback = @printInkey                                   '' initialize the callback Sub pointer
     ThreadWait(t.threadHandle)                                 '' wait for the thread finish

Differences from QB
   * New to FreeBASIC

See also
   * Sub
   * ProcPtr
   * Operator @ (Address Of)

