QB64-Phoenix-Edition / QB64pe

The QB64 Phoenix Edition Repository
https://qb64phoenix.com
Other
119 stars 26 forks source link

_Offset() should be able to return function/sub pointers #48

Open a740g opened 2 years ago

a740g commented 2 years ago

Some Win32 API need function pointers for callbacks. I am quite certain that we have other OS API requiring the same.

Not sure how feasible this is... but can we get _Offset() to return function pointers to Subs and Functions?

We can then use it like the example below.

mmr = waveOutOpen(hwaveout, WAVE_MAPPER, wfe, _Offset(WaveProc), 0, CALLBACK_FUNCTION)
merr = midiStreamOpen(hMidiStream, MidiDevice, 1, _Offset(MidiProc), 0, CALLBACK_FUNCTION)

Sub WaveProc (hwo As _Unsigned Long, uMsg As _Unsigned Long, dwInstance As _Unsigned Long, dwParam1 As _Unsigned Long, dwParam2 As _Unsigned Long)
    If uMsg = WOM_DONE Then buffersout = buffersout - 1
End Sub

Sub MidiProc (hMidi As _Unsigned Long, uMsg As _Unsigned Long, dwInstance As _Unsigned Long, dwParam1 As _Unsigned Long, dwParam2 As _Unsigned Long)
    Select Case uMsg
        Case MOM_DONE
            ...
        Case Else
            ...
    End Select
End Sub
mkilgore commented 2 years ago

This is something I've wanted and worked around myself in the past. Function pointers are really nice for creating more generic structures, since you can make TYPEs that have function pointers in them and then pass them to SUBs/FUNCTIONs that can be more generic and use the callbacks for necessary functionality. That said I think there's a few separate issues here that would all need to be solved:

First is just providing the _Offset(func) syntax itself and returning a pointer to a function. This is definitely possible as I have done it in QB64 the past, though it admittedly gets messy as function pointers are not really supposed to be stored in generic void * or uintptr_t values. But it is a detail that can be fudged without too much concern even if not ideal.

The second is allowing DECLARE LIBRARY entries to include function pointers in their parameters. I don't think this is as simple as just using _OFFSET for them as the type would not match in the resulting C++. Like mentioned earlier maybe that can be fudged as well and we just ignore types completely and hope they're right, but IMO that's probably a bad solution just because people are going to get the types wrong and the errors would be very unpredictable. It would be very nice if at least DECLARE LIBRARY allowed some kind of syntax for real function pointers, Ex. _SUB(type1, type2, type3), _FUNCTION(type1, type2, type3). I'm not sure that's the ideal syntax but I think the idea is clear.

The third (and probably the biggest issue for your usage) is that it's currently not possible for you to declare a function with the correct parameters for the callback you're trying to use. All of the parameters in QB64 are passed by reference, and BYVAL is not allowed for regular SUBs/FUNCTIONs, meaning that every parameter to your SUBs/FUNCTIONs are effectively pointers and you have no way to control that. For example your uint uMsg parameter is really a uint *uMsg parameter, which obviously will not work as intended. There are a few potential ways we can solve this but I don't think they're trivial to implement Ex. Allow BYVAL in regular SUBs/FUNCTIONs, automatically fix up BYVAL and pointer parameters based on the function pointer type, etc...

So I think this feature is worth implementing, but it's also unfortunately not a simple one to add.

a740g commented 2 years ago

Got it!

Never thought of the third point really. Yeah. That is going to be a problem. How complicated is it to implement ByVal in function and sub parameters? I remember VBDOS 1.0 had it. PDS too I think.

Probably we should be looking at VBDOS 1.0 language compatibility (minus the "Ruby" TUI and form stuff). Just thinking out loud. 🙂

mkilgore commented 2 years ago

How complicated is it to implement ByVal in function and sub parameters?

I don't really know to be honest, I haven't ever looked into it. I assume there's some kind of problem since I think it has been talked about in the past and not done for whatever reason. It's definitely a feature I'd like though, since in a lot of situations you're actively avoiding accidentally modifying the by-reference parameters.