So I have a C/C++ lib (actually a C++ lib with a C interface) which I want to use in a Swift package, I managed to wrap its API in an Objective-c++ class. The wrapper exposes for example a method as:
This might be difficult. If the callback has a field for an arbitrary data pointer, you might use something like this example to pass an instance of Swift class containing Swift closure. (It is ideal, to have something like destroy callback to release said Swift class.)
If you can get Swift closure to work, then you can add additional layer for async/await.
However, the API you have posted is insufficient even for the simple Seift closure.
To put it simply, you need to have the ability to âpairâ the non-blocking call and callback call.
Whether you can use arbitrary data pointer or number tag (or you can ensure that the callbacks are called in a particular order, like fifo for example) does not matter. But you need a guarantee that we can build on.
Note that I had to use global variable to remember the passed block (swift closure) to use it from within C callback. Should your C call & callback be using a slightly different signature:
So, in your first example, I did not know that an objc block is already available with await.. the second example seems more appropriate since a global var does not feel like a good solution. But the pointer userData how can you 'bridge' a Swift closure to having userData accepting it.. and then how would you use it in C land?
The example I've posted in my first post is doing exactly that. You can capture Swift closure in an instance of Swift class and then use Unmanaged to pass it around as an opaque pointer (or UnsafeRawPointer). I can rewrite it into a clearer example if you would like.
IMO means: if you can change the C++ library, you need to introduce an userData argument which does precisely what I wrote above: pass around an opaque pointer pointing to an instance of Swift class.
If you can not change the C++ API and the C++ API does not provide it already, then you're in no luck and you need to find other solution.
C function pointer is a pointer-wide variable, that contains pointer to executable memory, where the function is located.
Swift closure (in most cases) two pointer-wide variable. First pointer is a function pointer. Second pointer is (in most cases) reference-counted heap-allocated box, where the arguments (capture list) for the function pointer are stored.
If you can't change C library API - you are stuck with global variable approach. And if you need to support, say, up to 10 concurrent "auth" calls you'd need 10 different callbacks each referencing their own global variable, which sucks.
OTOH, if you can change the C library and add userData parameter in that call / callback - all good, use smth like this (showing the relevant pieces, everything else is as before):
Maybe using ASan? Or maybe the block youâre testing with doesnât actually capture anything and therefore didnât end up using a dynamic allocation.
Yep, thatâs what Iâd use! Note that this does bake in the assumption that the callback is called exactly once; if itâs never called, the block will be leaked, and if itâs called multiple times, itâs a use-after-free. In that case youâll have to use some other mechanism to manage the lifetime of the block.
ok, so one of the C API call has actually a callback which can be called as long as the callback lives.. a sort of observable. I guess then you need something else like you mention?
Then it should be +1 (CFBridgingRetain) on "enter", then a sequence of +0 (__bridge) callouts, and finally -1 (CFBridgingRelease) to release the block at the end.
So with the multiple calls you would need to know when 'listener' stops listening and so CFBridgingRelease is needed...
And so do I understand correctly that the block completion handlers are already usable with 'await' in Swift? I need the wrapping Swift API also to confirm to async/await.
I also saw examples where the closures were wrapped in in a class and then passed over in the closure with 'Unmanaged', however this seems cleaner but you need to change the C api to use the 'in between closure' right?
yes, async version is generated automatically from a function with the trailing completion handler parameter.
you can do this but as I understand this happens automatically (during conversion from swift closure to obj-c block) and it'd prohibit (2) while also making the caller side more complicated than necessary.