-
Notifications
You must be signed in to change notification settings - Fork 10.6k
[cxx-interop] Add CxxConvertibleToCollection protocol
#62243
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,70 @@ | ||
| //===----------------------------------------------------------------------===// | ||
| // | ||
| // This source file is part of the Swift.org open source project | ||
| // | ||
| // Copyright (c) 2014 - 2022 Apple Inc. and the Swift project authors | ||
| // Licensed under Apache License v2.0 with Runtime Library Exception | ||
| // | ||
| // See https://swift.org/LICENSE.txt for license information | ||
| // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
|
|
||
| /// A C++ type that can be converted to a Swift collection. | ||
| public protocol CxxConvertibleToCollection { | ||
| associatedtype RawIterator: UnsafeCxxInputIterator | ||
|
|
||
| /// Do not implement this function manually in Swift. | ||
| mutating func __beginUnsafe() -> RawIterator | ||
|
|
||
| /// Do not implement this function manually in Swift. | ||
| mutating func __endUnsafe() -> RawIterator | ||
| } | ||
|
|
||
| @inlinable @inline(__always) | ||
| internal func forEachElement<C: CxxConvertibleToCollection>( | ||
| of c: C, | ||
| body: (C.RawIterator.Pointee) -> Void | ||
| ) { | ||
| var mutableC = c | ||
| withExtendedLifetime(mutableC) { | ||
| var rawIterator = mutableC.__beginUnsafe() | ||
| let endIterator = mutableC.__endUnsafe() | ||
| while rawIterator != endIterator { | ||
| body(rawIterator.pointee) | ||
| rawIterator = rawIterator.successor() | ||
| } | ||
| } | ||
| } | ||
|
|
||
| extension Array { | ||
| /// Creates an array containing the elements of a C++ collection. | ||
| /// | ||
| /// This initializer copies each element of the C++ collection to a new Swift | ||
| /// array. | ||
| /// | ||
| /// - Complexity: O(*n*), where *n* is the number of elements in the C++ | ||
| /// collection. | ||
| public init<C: CxxConvertibleToCollection>(_ c: C) | ||
| where C.RawIterator.Pointee == Element { | ||
|
|
||
| self.init() | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we reserve N elements here so we don't have to potentially allocate each time? (Can also be follow up. And it would be cool if we had some benchmarks to measure if there's actually an improvement from that change/others.)
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
We could do that for C++ RAC, however, C++ RAC are auto-conformed to
This won't be an improvement unfortunately, we're just making the copy explicit. It's a good idea to add a benchmark though 👍 |
||
| forEachElement(of: c) { self.append($0) } | ||
| } | ||
| } | ||
|
|
||
| extension Set { | ||
| /// Creates an set containing the elements of a C++ collection. | ||
| /// | ||
| /// This initializer copies each element of the C++ collection to a new Swift | ||
| /// set. | ||
| /// | ||
| /// - Complexity: O(*n*), where *n* is the number of elements in the C++ | ||
| /// collection. | ||
| public init<C: CxxConvertibleToCollection>(_ c: C) | ||
| where C.RawIterator.Pointee == Element { | ||
|
|
||
| self.init() | ||
| forEachElement(of: c) { self.insert($0) } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,40 @@ | ||
| // RUN: %target-run-simple-swift(-I %S/Inputs -Xfrontend -enable-experimental-cxx-interop) | ||
| // | ||
| // REQUIRES: executable_test | ||
| // REQUIRES: OS=macosx || OS=linux-gnu | ||
|
|
||
| import StdlibUnittest | ||
| import CustomSequence | ||
| import Cxx | ||
|
|
||
| var CxxSequenceTestSuite = TestSuite("CxxConvertibleToCollection") | ||
|
|
||
| extension SimpleSequence: CxxConvertibleToCollection {} | ||
|
|
||
| CxxSequenceTestSuite.test("SimpleSequence to Swift.Array") { | ||
| let seq = SimpleSequence() | ||
| let array = Array(seq) | ||
| expectEqual([1, 2, 3, 4] as [Int32], array) | ||
| } | ||
|
|
||
| CxxSequenceTestSuite.test("SimpleSequence to Swift.Set") { | ||
| let seq = SimpleSequence() | ||
| let set = Set(seq) | ||
| expectEqual(Set([1, 2, 3, 4] as [Int32]), set) | ||
| } | ||
|
|
||
| extension SimpleEmptySequence: CxxConvertibleToCollection {} | ||
|
|
||
| CxxSequenceTestSuite.test("SimpleEmptySequence to Swift.Array") { | ||
| let seq = SimpleEmptySequence() | ||
| let array = Array(seq) | ||
| expectTrue(array.isEmpty) | ||
| } | ||
|
|
||
| CxxSequenceTestSuite.test("SimpleEmptySequence to Swift.Set") { | ||
| let seq = SimpleEmptySequence() | ||
| let set = Set(seq) | ||
| expectTrue(set.isEmpty) | ||
| } | ||
|
|
||
| runAllTests() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be really cool if we could provide optimized implementations for contiguous collections (C++ provides this information in iterator traits). This could just be a reserve + memcpy. I don't know if LLVM is smart enough to do that on its own (probably not).
That can be a follow up patch, though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great point! I'll do that in a separate patch.