The current prototype implementation of Swift-to-C++ ignores argument labels when generating C++ functions and methods that represent the native Swift functions. This approach has worked so far, but it's not without its flaws. The primary issue is that functions overloaded by argument labels don't map well to C++. For example, there is no way to distinguish these two methods from the Array
Swift type in C++:
index(after:)
index(before:)
Mapping them to C++ index
methods would produce compiler errors in the generated header on the C++ side.
I've been thinking about some potential solutions. The Swift compiler could:
- Detect conflicting overloads like the ones shown above and not emit them in the generated C++ header. The user would need to explicitly specify their name in C++ using
@expose
attribute. - Incorporate some argument labels into the name of the C++ function , if there is a naming conflict in the overloaded set. For example, this overload set:
index(_ , offsetBy:)
index(after:)
index(before:)
Could be mapped to these C++ functions, renaming the last two overloads to prevent a conflict:
index( )
indexAfter()
indexBefore()
However, both of these approaches have issues when it comes to source compatibility for C++ users. Future changes to such a Swift API that remove, rename or add argument labels and/or overloads can cause previously exposed Swift functions to be renamed in the C++ header. That would break C++ sources for users who call these APIs from C++. For example, if we add another overload with two arguments:
index(_ , offsetBy:)
index(_ , subtractedBy:)
That would cause the existing index
overload to be renamed to indexOffsetBy
, breaking source compatibility for C++ clients. I don't think that this would lead to a great user experience, therefore I think that always incorporating argument labels might be best.
Always incorporate argument labels
I tried incorporating Swift argument labels into the names of C++ function/methods that represent Swift functions. So far this approach has worked quite well on the set of existing APIs that we expose into C++ from the Swift standard library.
For example, these methods in Swift's Array
type:
index(after:)
index(before:)
insert(at:)
index(_,offsetBy:,limitedBy:)
distance(from:,to:)
Can now map to C++ without any ambiguities:
indexAfter()
indexBefore()
insertAt()
indexOffsetByLimitedBy()
distanceFromTo()
This approach does not have the downside of inadvertent source breaks on the C++ side whenever a new overload that uses different argument labels is added to the overload set with the same base name. Because of that, I think that this approach is the best option that I've explored so far. Swift APIs are expected to evolve over time so the C++ clients need to be resilient to trivial API additions like the ones mentioned in this post that don't break Swift code.
Right now this approach generates the C++ name by adding each argument label with capitalized first character to the base name of the function. In the future we can improve the heuristics, for example certain argument labels do not need to be capitalized potentially.
I'm planning to make this change in the prototype implementation of Swift-to-C++ interop in the compiler and pitch this approach to evolution in the future. Let me know if you have any feedback and/or concerns about this approach.