diff --git a/stdlib/public/Cxx/CMakeLists.txt b/stdlib/public/Cxx/CMakeLists.txt index 4f773bc1deb45..3907a8f7eaafa 100644 --- a/stdlib/public/Cxx/CMakeLists.txt +++ b/stdlib/public/Cxx/CMakeLists.txt @@ -1,4 +1,5 @@ add_swift_target_library(swiftCxx ${SWIFT_STDLIB_LIBRARY_BUILD_TYPES} IS_SDK_OVERLAY + CxxConvertibleToCollection.swift CxxRandomAccessCollection.swift CxxSequence.swift diff --git a/stdlib/public/Cxx/CxxConvertibleToCollection.swift b/stdlib/public/Cxx/CxxConvertibleToCollection.swift new file mode 100644 index 0000000000000..a43eb3079d35c --- /dev/null +++ b/stdlib/public/Cxx/CxxConvertibleToCollection.swift @@ -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( + 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: C) + where C.RawIterator.Pointee == Element { + + self.init() + 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: C) + where C.RawIterator.Pointee == Element { + + self.init() + forEachElement(of: c) { self.insert($0) } + } +} diff --git a/test/Interop/Cxx/stdlib/overlay/custom-convertible-to-collection.swift b/test/Interop/Cxx/stdlib/overlay/custom-convertible-to-collection.swift new file mode 100644 index 0000000000000..449699dfaeb06 --- /dev/null +++ b/test/Interop/Cxx/stdlib/overlay/custom-convertible-to-collection.swift @@ -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()