I'm trying to call a function that expects a std::function in C++, and I'd like to provide it a Swift closure.
Autocomplete seems to recognize what I'm doing and offers to cover the std::function argument into a trailing closure. But I can't find any configuration that actually compiles.
In the trailing closure case, I get:
Trailing closure passed to parameter of type 'XXX' (aka 'std.__1.function<((XXX, XXX) -> XXX)>') that does not accept a closure
I've also noticed despite the autocomplete working, in the C++/Swift interop guide std::function is listed as not supported.
Is that doc up to date? Is there a way to provide a closure to std::function?
I'm wondering the same thing. I expected an initializer that accepts a Swift closure or something similar. The only completion suggestion in Xcode is an initializer without parameters, which compiles but doesn't really help.
Since Swift lambdas and C++ std::functions are fundamentally different, the best way I have found so far to bridge between is to use C-style functions that carry their closure (context) as void*.
So;
Create a class in Swift that wraps the closure. Give it a call(..) func which internally calls the closure
Create a new instance of that Swift class with your desired Swift closure
Convert that instance to an Unmanaged & toOpaque() (with a retain)
Create a static C-style Swift function that takes an opaque pointer as a first arg, and optionally any other args as well - call it callClosure or something
Create a second C-style Swift function that takes an opaque pointer and unwraps it using Unmanaged with a retained take and release. Call it destroyClosure or something
Finally, create a C++ function that takes three args: void* context, void(*callClosure)(void* /* context */, and void(*destroy)(void* /* context */. This will create a shared_ptr of the context so it's ref-counted (and destroy() is the custom deleter), and then return an std::function in which you call the callClosure() func with context as it's first argument.
Because it's a C-style function pointer, you can't seem to use borrowing or inout as far as I can see. So everything is copied. This is also annoying if you do shared_ptr.pointee as this copies the pointee everytime.
I ended up adding a simple C++ function that accepts a Clang block and captures and evaluates the block from within the std::function. I'm not sure how well it works with more complex types, but it seems to do the job in my case.
Clang already has a conversion from blocks to std::function (they are “callable” in the C++ sense, after all), so you may be able to remove a level of wrapping here. But the thread’s larger point about providing nice interop should stand.
C/Objective-C blocks won't support C++ types as arguments or return values afaik. You can only use C or Objective-C types. (didn't double check this tho!)
A C/Objective-C block cannot be automatically converted to an std::function in my tests. I always need to capture it and call it. But I might have done something wrong (it was a top-level inline function that did this for a void() block)
This actually seems to work for me, too. So all I need now is a very thin function that takes a block and forwards it to the original function that takes a std::function. Not too bad.