Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions stdlib/public/Cxx/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
add_swift_target_library(swiftCxx ${SWIFT_STDLIB_LIBRARY_BUILD_TYPES} IS_SDK_OVERLAY
CxxConvertibleToCollection.swift
CxxRandomAccessCollection.swift
CxxSequence.swift

Expand Down
70 changes: 70 additions & 0 deletions stdlib/public/Cxx/CxxConvertibleToCollection.swift
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 {
Copy link
Contributor

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.

Copy link
Contributor Author

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.

/// 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()
Copy link
Contributor

Choose a reason for hiding this comment

The 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.)

Copy link
Contributor Author

Choose a reason for hiding this comment

The 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?

We could do that for C++ RAC, however, C++ RAC are auto-conformed to Swift.RandomAccessCollection which brings an equivalent initializer. For non-random-access collections, we don't really know what N is until we actually iterate over the collection. For some collections iterating twice might produce different results so we can't do that I think.

it would be cool if we had some benchmarks to measure if there's actually an improvement from that change/others

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()