Hello @egor.zhdan and the C++ Interop Team. I'm trying to make a complete list of C++ standard library types that could be extended to seamlessly work with Swift and I've noticed that some types (like Optional/std:optional) could theoretically be included, however, since Swift doesn't have any protocols to described them, only concrete types, doing so is not straightforward.
I would imagine requiring something like .asOptional(), which in turn might not be possible or sub-optimal in some cases.
What's a good workaround for types in this situation? Should we only focus on types for which we can provide protocol conformances (like the collections in the examples)?
You're right, Swift doesn't have an ExpressibleByOptional type, it only has ExpressibleByNilLiteral.
Unfortunately there doesn't seem to be a way to avoid the overhead of copying the wrapped object when bridging std::optional to Swift.Optional and vice versa: the types have different memory layout, which means we can't bridge them trivially by doing something like reinterpret_cast.
I think requiring the user to explicitly spell out the type conversion is fine in this case. I would say a more Swift-y API would be having an initializer for std::optional that takes a Swift.Optional, and vice versa. The initializers can be added within extensions for these two types, so there is no need to e.g. change the Swift.Optional implementation in the Swift stdlib sources.
Historically Swift used toX() functions for type conversions, e.g. "123".toInt() to convert a Swift.String to Swift.Int, however, that convention was changed circa Swift 3, and now the Swift stdlib types have initializers for this kind of type conversion.
In general, types like std::optional that can't be seamlessly bridged to Swift could still be made more convenient to use from Swift by providing an overlay. Sometimes we can't avoid forcing the user to write some bits of extra conversion code, like explicitly creating a std::optional. I think this is expected to some extent given the difference between the two languages.
Thank you for your detailed reply @egor.zhdan! Indeed, using initializers feels more like Swift. (also, I like the aesthetics of it better in general)
How about the types that throw? I know Swift is not using the same exception model and is not able to catch or interface in any way with C++ errors. We could write non-throwing wrappers, but I think that would add a bit of effort to it. What would be a good workaround/solution here?