sub/function as datatype

Forum for discussion about the documentation project.
Tourist Trap
Posts: 2958
Joined: Jun 02, 2015 16:24

Re: sub/function as datatype

Post by Tourist Trap »

fxm wrote:I also wrote in 2011 such an example:
[url=viewtop
ic.php?f=7&t=18246]UDT Screen_Event_Thread (methods and event pointers)[/url]
I missed the mechanics the last time I read this thread. This is a very interesting example. Those days I have to use vba more than fb due to the context of excel, and when it comes to events in class modules there, there is something like that to be done. I still don't master it at any level but now I see a bridge between fb and vba from the user defined events point of view.
About callbacks, it is still a very important step in learning, in a more general context, with maybe applications beyond events handlers (in agent communication modeling for instance).
fxm
Moderator
Posts: 12081
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: sub/function as datatype

Post by fxm »

KeyPgFunctionPtr → fxm [Added sentence about use of procedure pointers to create callback procedures]
KeyPgSubPtr → fxm [Added sentence about use of procedure pointers to create callback procedures]
fxm
Moderator
Posts: 12081
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: sub/function as datatype

Post by fxm »

Example of advanced* callback Sub mechanism to implement a key pressed event, proposed for the documentation page: SUB Pointer
(*: the user callback Sub address can be modified while the event thread is running)

Code: Select all

' Example of advanced callback Sub mechanism 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
    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
fxm
Moderator
Posts: 12081
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: sub/function as datatype

Post by fxm »

More simple and compact.
Example of basic* callback Function mechanism to implement a key pressed event, proposed for the documentation page: FUNCTION Pointer
(*: the user callback Function address cannot be modified while the event thread is running)

Code: Select all

' Example of basic callback Function mechanism to implement a key pressed event:
' (the user callback Function address cannot be modified while the event thread is running)
'   - An asynchronous thread tests the keyboard in a loop, and calls a user callback Function each time a key is pressed.
'   - The callback Function address is passed to the thread.
'   - The callback Function prints the character of the key pressed,
'       but if the key pressed is <escape> it orders the thread to finish by using the function return value.
'   - As the user callback address is passed to the thread as argument, it cannot be modified while the thread is running.


'' thread Sub definition
  Sub threadInkey (Byval p As Any Ptr)
    If p > 0 Then                                                '' test condition callback Function defined
      Dim As Function (Byref As String) As Integer callback = p  '' convert the any ptr to a callback Function pointer
      Do
        Dim As String s = Inkey
        If s <> "" Then                                          '' test condition key pressed
          If callback(s) Then                                    '' test condition to finish thread
            Exit Do
          End If
        End If
        Sleep 50
      Loop
    End If
  End Sub

'' user callback Function definition
  Function printInkey (Byref s As String) As Integer
    If Asc(s) = 27 Then                                        '' test condition key pressed = <escape>
      Print
      Return -1                                                '' order thread to finish
    Else
      Print s;
      Return 0                                                 '' order thread to continue
    End If
  End Function

'' user main code
  Dim As Any Ptr p = Threadcreate(@threadInkey, @printInkey)   '' launch the thread, passing the callback Function address
  Threadwait(p)                                                '' wait for the thread finish
fxm
Moderator
Posts: 12081
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: sub/function as datatype

Post by fxm »

@coderJeff,
While preparing the addition in wiki of my 2 examples proposed above, a question comes to me about your two already existing examples:
Your two examples in pages KeyPgSubPtr and KeyPgFunctionPtr have the same filename "examples/manual/datatype/funcptr.bas". Is this normal?
coderJeff
Site Admin
Posts: 4313
Joined: Nov 04, 2005 14:23
Location: Ontario, Canada
Contact:

Re: sub/function as datatype

Post by coderJeff »

Sorry, the duplicate filename is my copy/paste mistake. It should be unique. And, if there are multiple examples on a wiki page, give each one a unique name.

Each example in the wiki that has a filename tag, can get extracted and compile tested automatically by the wiki/doc checking tools.

Yes, please change examples to whatever you or users think is best.

EDIT: looked through my old codes, 2 things I most often use function pointers:
1) Call backs, for example, print logging, comparison function (for sorting using generic algorithm), enumerations (my own or like in WINAPI)
2) Interface: a TYPE with several function pointer methods that are initialized when the CONSTRUCTOR is called. For example, a stream-like interface that might work with a file or memory, gets constructed with members pointing to different methods.
Tourist Trap
Posts: 2958
Joined: Jun 02, 2015 16:24

Re: sub/function as datatype

Post by Tourist Trap »

fxm wrote:KeyPgFunctionPtr → fxm [Added sentence about use of procedure pointers to create callback procedures]
KeyPgSubPtr → fxm [Added sentence about use of procedure pointers to create callback procedures]
Thanks for the effort.

I have a question here that I failed many times to answer myself. Suppose we do:

Code: Select all

sub aaa(byval x as integer)
   ? "aaa", x
end sub

var uuu = procPtr(aaa)
'dim as any ptr uuu = procPtr(aaa)

uuu(1)

This works. But if I replace VAR by DIM AS ANY PTR it won't work any more. (err71)
I can get it work if I cast uuu as a SUB(byval as integer) variable type. But I have to know this signature by advance.

My concern here is, what if I want to store in some array of any ptr a list of procedure pointers?
I'm ok with casting the array value at the right index with the right type. But I dont want to know this type by advance, otherwise the use of an array makes no sense in my opinion.
So can I retrieve the right type from the procedure pointer? For instance:

Code: Select all

cast( pointer_to_proc[0], pointer_to_proc)(1)
Of course the example here is dummy, but is there some similar way to retrieve the procedure signature so that we can do a correct cast at any time, and then use arrays of procedure pointers easily?
fxm
Moderator
Posts: 12081
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: sub/function as datatype

Post by fxm »

For example with an Any Ptr array:

Code: Select all

Sub s0(Byval I As Integer)
  Print "s0(Byval As Integer)", I
End Sub

Sub s1(Byref S As String, Byval D As Double)
  Print "s1(Byref As String, Byval As Double)", S, D
End Sub

Dim As Any Ptr parray(...) = {@s0, @s1}

Cast(Typeof(@s0), parray(0))(3)
Cast(Typeof(@s1), parray(1))("PI", 3.14)
Similar example using an Any Ptr buffer:

Code: Select all

Sub s0(Byval I As Integer)
  Print "s0(Byval As Integer)", I
End Sub

Sub s1(Byref S As String, Byval D As Double)
  Print "s1(Byref As String, Byval As Double)", S, D
End Sub

Dim As Any Ptr Ptr pbuffer = Callocate(2, Sizeof(Any Ptr))
pbuffer[0] = @s0
pbuffer[1] = @s1

Cast(Typeof(@s0), pbuffer[0])(3)
Cast(Typeof(@s1), pbuffer[1])("PI", 3.14)

Deallocate(pbuffer)
fxm
Moderator
Posts: 12081
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: sub/function as datatype

Post by fxm »

KeyPgSubPtr → fxm [Added an example of callback mechanism using a Sub pointer]
KeyPgFunctionPtr → fxm [Added an example of callback mechanism using a Function pointer]
grindstone
Posts: 862
Joined: May 05, 2015 5:35
Location: Germany

Re: sub/function as datatype

Post by grindstone »

@fxm: Please excuse my criticism, but IMHO the examples are much too complicated for only to explain the operating principle of a callback pointer. I'm afraid especially a beginner will be confused rather than enlightened.

I guess the example you posted above (if slightly modified) would do the job much better:

Code: Select all

Sub s0(Byval I As Integer)
  Print "s0(Byval As Integer)", I
End Sub

Sub s1(Byref S As String, Byval D As Double)
  Print "s1(Byref As String, Byval As Double)", S, D
End Sub

Dim s0_ptr As Sub (Byval I As Integer)
Dim s1_ptr As Sub (Byref S As String, Byval D As Double)

Dim As Any Ptr parray(...) = {@s0, @s1}

s0_ptr = parray(0)
s0_ptr(3)

s1_ptr = parray(1)
s1_ptr("PI", 3.14)
fxm
Moderator
Posts: 12081
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: sub/function as datatype

Post by fxm »

In your above example, 'parray()' is useless, so that can be reduced to:

Code: Select all

Sub s0(Byval I As Integer)
  Print "s0(Byval As Integer)", I
End Sub

Sub s1(Byref S As String, Byval D As Double)
  Print "s1(Byref As String, Byval As Double)", S, D
End Sub

Dim s0_ptr As Sub (Byval I As Integer) = @s0
Dim s1_ptr As Sub (Byref S As String, Byval D As Double) = @s1

s0_ptr(3)
s1_ptr("PI", 3.14)
grindstone
Posts: 862
Joined: May 05, 2015 5:35
Location: Germany

Re: sub/function as datatype

Post by grindstone »

fxm wrote:In your above example, 'parray()' is useless
Correct. :-)
fxm
Moderator
Posts: 12081
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: sub/function as datatype

Post by fxm »

I think that whoever wants to use procedure pointers is not a beginner but rather a "junior" coder at least.
In addition to a purely syntactic example like yours above, I also wanted to demonstrate the real utility of using such procedure pointers.

But nothing prevents to add an intermediate example that only better highlights the right use of the syntax as following:

Code: Select all

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)
grindstone
Posts: 862
Joined: May 05, 2015 5:35
Location: Germany

Re: sub/function as datatype

Post by grindstone »

I fully agree, this pleases all. :-)
fxm
Moderator
Posts: 12081
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: sub/function as datatype

Post by fxm »

KeyPgSubPtr → fxm [Added an intermediate example]
Post Reply