Callbacks possible?

Blitz3D Forums/Blitz3D Programming/Callbacks possible?

LamptonWorm(Posted 2013) [#1]
Hi all,

Quick question - is it possible to handle callbacks in B3D?

Specifically, using commands line "BASS_ChannelSetSync" with bass/bass-wrapper.

Cheers,
LW.


Yasha(Posted 2013) [#2]
Depends what you mean by "callback", the meaning seems to vary depending on which API is asking for one, although the short answer is "no" because Blitz3D doesn't supply any callback mechanism by default.

I couldn't speak for BASS, not having it (can you post the full declaration of that function, maybe its doc description if there is one?), but most libraries that expect a "callback" are asking for a function pointer, which Blitz3D doesn't provide as a built-in mechanism.

If that's what you need, you actually can take pointers to B3D functions using FastPointer, just ...be careful doing so, it's not as simple as doing it in e.g. C. The hack is naughty but it works.


LamptonWorm(Posted 2013) [#3]
Hi,

FastPointer...interesting :)

So, as an example from the BASS docs we have this command...
http://www.un4seen.com/doc/#bass/BASS_ChannelSetSync.html

One of the params is "SYNCPROC *proc" the docs of which are...
http://www.un4seen.com/doc/#bass/SYNCPROC.html

void CALLBACK SyncProc(
    HSYNC handle,
    DWORD channel,
    DWORD data,
    void *user
);


All I would need back from SyncProc is to know that it happened, true or false.

Hope this helps clarify what I'm trying to do.

Cheers,
LW.


Yasha(Posted 2013) [#4]
OK, doing this will take a little extra wrapping. The biggest obstacle is actually that FastPointer doesn't support multi-parameter functions.

What you will need to do is create a C callback-wrapper function with the type SyncProc, that packs its arguments into a single data structure (bank? Special-use type object? Something like that), and then call your real callback with that data structure - probably a bank - as its only argument. (If your callback is very simple it might not be worth writing it in B3D at all.)

You can write inline C code in B3D with the TCC library, which will save you creating an extra DLL for this purpose.

e.g. (EDIT: this example is wrong, see below for better code)



(...assumes all arguments can be cast to int safely. This may have many other problems, I haven't tested it since I don't have BASS. Don't call Print from the callback as BASS uses a separate thread apparently and it will break.)


LamptonWorm(Posted 2013) [#5]
Wow I had no idea about TCC, that is pretty cool. I'll have to get my experimenting hat on later and have a play. Thanks for the example code.

For these two lines, isn't 'CallBack' skipped over?

Local cbPtr = FunctionPointer() : Goto skip : CallBack(0)
.skip


Cheers,
LW.


Yasha(Posted 2013) [#6]
Yep. Don't modify that format (including that you should never make the argument anything other than zeroes): that is how you use FastPointer to get function pointers.

Explanation: Since B3D has no understanding of or mechanism for describing function pointers, there is no legal way to pass them as arguments (because that would imply already having taken their address). What the above code does is inspect the sequence of instructions after its own return point and harvest the next argument to an x86 CALL instruction! The Goto prevents the function from actually being called while we do this.

It's a really dirty, ugly, naughty hack (not least because it kinda relies on the B3D compiler being rather stupid), and whoever came up with it first is a genius.


LamptonWorm(Posted 2013) [#7]
Hi,

I'm trying this out, getting a MAV using BASS_ChannelSetSync atm on this line...

test=BASS_ChannelSetSync(Channel,BASS_SYNC_END,0,callBackWrapper,0)


Early days, and I'm poking around in the dark a little, any initial thoughts?

Edit, just noticed
Local callBackWrapper = TCC_GetSymbolWrap(s, "cb_wrap")


If I write 'callBackWrapper' it is set to -1, not sure that's expected.

Cheers,
LW.


LamptonWorm(Posted 2013) [#8]
Just guessing, but shouldn't "cp_wrap" be "_cb_wrap@16" ?

If so, I no longer get -1 if I change it, I get an address, and no MAV on BASS_ChannelSetSync, however when callBackWrapper is called by BASS, the application then crashes, on fastpointer.dll (c0000005).

(I'm using Win 7 64-bit btw)

Cheers!
LW.


Yasha(Posted 2013) [#9]
Yes, it should.

For some reason I added that line twice (?) and the second one was wrong but I was only looking at the first one (the local variable 'wrapper'). I have adjusted the example accordingly (as though it matters now). Sorry about that. Good catch.


LamptonWorm(Posted 2013) [#10]
Ah I was just editing my post when you replied :) no worries.

So, past that and onto the new problem..

"when callBackWrapper is called by BASS, the application then crashes, on fastpointer.dll (c0000005)."

(I'm using Win 7 64-bit btw)

Cheers,
LW.


Yasha(Posted 2013) [#11]
Yikes, it's been forever since I used the TCC wrapper and I'd basically forgotten how it works. OK, this code I have actually tested:



...where 'foo' refers to this test dll:




Turns out I'd just forgotten half of how to use the TCC wrapper. You don't pass argument values: TCC_CallCFunc takes a bank filled with values (so it gets extra convoluted when the values we want to pass are also banks... filled with values for a different function... oh my). You also don't seem to need the stdcall attribute on B3D function pointers... at this point I have no idea why.

Fun as it is to play with TCC, you might do better to put the wrapper function in externally-compiled code.


LamptonWorm(Posted 2013) [#12]
Hi, ah ok - thanks, I'll have another play tonight. As you say, it's fun for sure :) I should try the DLL route also, see how things pan out, learning all the way that's the main thing.

Cheers,
LW.


LamptonWorm(Posted 2013) [#13]
Hi,

Well, no joy here I'm afraid, with embedded or a separate DLL. I might need a fresh look at it, brain a bit fried :(

Cheers,
LW.


Yasha(Posted 2013) [#14]
My last suggestion is to ignore what I said above about needing the stdcall attribute for B3D function pointers, because I've gone back and verified and B3D does indeed use that convention internally (once again, I was being stupid when I said the above about not needing it). Other than that, I got nothing more.