The QuickBASIC language does not have a built-in feature to call a procedure through a pointer (or at least not one intended to be used that way) - that is, a SUB or FUNCTION cannot be given another SUB or FUNCTION to call. If the same piece of code is to be used to perform different tasks, the only way to do so is to use e.g. SELECT CASE at the point of the call.
Solutions
UEVENT
The UEVENT feature, which is an overlooked part of the language (I have never seen it used), can be [ab]used as a callback mechanism:
DECLARE SUB SetUEvent () 'part of QB.LIB/QB.QLB; run QB with the /L option
'To set the callback:
ON UEVENT GOSUB Callback 'Set the label to be called by SetUEvent
UEVENT ON 'Enable UEVENT processing
'To call the callback:
SetUEvent
Dummy: 'Ensure UEVENT processing if program is compiled without /V option
END
Callback:
PRINT "Hello world"
RETURN
No third-party code necessary (although a library - the QB.LIB/QB.QLB that comes with QuickBASIC - still has to be used).
Works in the interpreter/IDE as well as in compiled code.
Calls go through GOSUB, so it's like having closures.
Disadvantages
Only one callback can be set at a time.
SHARED variables must be used to pass data between the caller and the callback.
When using the "Make EXE" option in the IDE, the program will be compiled with the /V option, which increases the size of the executable significantly. To compile without this option, you must run the compiler and linker manually. How this is done can be seen by quitting the IDE immediately after compiling; the commands run by the IDE will be visible. When the /V option is not used (only /W), event processing is only done when a label is reached as opposed to after each statement. That is the purpose of the Dummy: line in the above example. See also Q45948.
While SetUEvent can (and is intended) to be called from non-BASIC languages, it only sets a flag that tells QuickBASIC that an event is waiting to be processed. There is no documented way to force the event to be processed at a certain point.
There is a bug when ON UEVENT and ON TIMER are used in the same program; see Q46182 and The UEVENT Bug by Ray Crumrine.
My PROCPTR library
In 2013, having known about UEVENT but not knowing how simple it is to use, I wrote a library (named PROCPTR) as a proof of concept. You can download it along with an example program.
Simple to use and understand. You can use it as a base for a better library.
Disadvantages
Only works in compiled code.
Can only call SUBs. (Should be easy to add support for FUNCTIONs.)
Cannot pass parameters; SHARED variables must be used. (Should be easy to fix.)
It assumes that the caller and callback reside in the same segment. This is true for BASIC code, but not for library code. (Should be easy to fix.)
The method used to obtain procedure pointers is somewhat awkward.
I do not intend to do any more work on it.
BoostQB
BoostQB (or boostqb for the nihilistic) is a library that includes, among other things, the most polished implementation of procedure pointers. I didn't know about it until I started writing this page.
Advantages
Supports an arbitrary number of callbacks.
Can call any SUB or FUNCTION.
Can pass and return values.
The callback does not need any special prologue.
Can be used to mix BASIC and library code.
Can also obtain addresses of labels to perform an indirect GOSUB. However, it is not documented whether it works from another procedure like UEVENT does.