Skip to content

Commit fcbfc49

Browse files
committed
[cxx-interop] Allow removing elements from std::vector
This adds `func remove(at index: Int)` to all instantiations of `std::vector` via an extension for `protocol CxxVector`. The original C++ method `std::vector::erase` is not visible in Swift because all of its overloads return unsafe iterators. rdar://113704853
1 parent 8879ae5 commit fcbfc49

File tree

3 files changed

+76
-4
lines changed

3 files changed

+76
-4
lines changed

lib/ClangImporter/ClangDerivedConformances.cpp

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1003,26 +1003,38 @@ void swift::conformToCxxVectorIfNeeded(ClangImporter::Implementation &impl,
10031003
decl, ctx.getIdentifier("value_type"));
10041004
auto iterType = lookupDirectSingleWithoutExtensions<TypeAliasDecl>(
10051005
decl, ctx.getIdentifier("const_iterator"));
1006-
if (!valueType || !iterType)
1006+
auto mutableIterType = lookupDirectSingleWithoutExtensions<TypeAliasDecl>(
1007+
decl, ctx.getIdentifier("iterator"));
1008+
if (!valueType || !iterType || !mutableIterType)
10071009
return;
10081010

10091011
ProtocolDecl *cxxRandomAccessIteratorProto =
10101012
ctx.getProtocol(KnownProtocolKind::UnsafeCxxRandomAccessIterator);
1011-
if (!cxxRandomAccessIteratorProto)
1013+
ProtocolDecl *cxxMutableRandomAccessIteratorProto =
1014+
ctx.getProtocol(KnownProtocolKind::UnsafeCxxMutableRandomAccessIterator);
1015+
if (!cxxRandomAccessIteratorProto || !cxxMutableRandomAccessIteratorProto)
10121016
return;
10131017

10141018
auto rawIteratorTy = iterType->getUnderlyingType();
1019+
auto rawMutableIteratorTy = mutableIterType->getUnderlyingType();
10151020

10161021
// Check if RawIterator conforms to UnsafeCxxRandomAccessIterator.
10171022
if (!checkConformance(rawIteratorTy, cxxRandomAccessIteratorProto))
10181023
return;
10191024

1025+
// Check if RawMutableIterator conforms to UnsafeCxxMutableInputIterator.
1026+
if (!checkConformance(rawMutableIteratorTy,
1027+
cxxMutableRandomAccessIteratorProto))
1028+
return;
1029+
10201030
impl.addSynthesizedTypealias(decl, ctx.Id_Element,
10211031
valueType->getUnderlyingType());
10221032
impl.addSynthesizedTypealias(decl, ctx.Id_ArrayLiteralElement,
10231033
valueType->getUnderlyingType());
10241034
impl.addSynthesizedTypealias(decl, ctx.getIdentifier("RawIterator"),
10251035
rawIteratorTy);
1036+
impl.addSynthesizedTypealias(decl, ctx.getIdentifier("RawMutableIterator"),
1037+
rawMutableIteratorTy);
10261038
impl.addSynthesizedProtocolAttrs(decl, {KnownProtocolKind::CxxVector});
10271039
}
10281040

stdlib/public/Cxx/CxxVector.swift

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,19 @@ public protocol CxxVector<Element>: ExpressibleByArrayLiteral {
1717
associatedtype Element
1818
associatedtype RawIterator: UnsafeCxxRandomAccessIterator
1919
where RawIterator.Pointee == Element
20+
associatedtype RawMutableIterator: UnsafeCxxMutableRandomAccessIterator
21+
where RawMutableIterator.Pointee == Element
2022

2123
init()
2224

25+
/// Do not implement this function manually in Swift.
26+
mutating func __beginUnsafe() -> RawIterator
27+
2328
mutating func push_back(_ element: Element)
29+
30+
/// Do not implement this function manually in Swift.
31+
@discardableResult
32+
mutating func __eraseUnsafe(_ iterator: RawIterator) -> RawMutableIterator
2433
}
2534

2635
extension CxxVector {
@@ -37,11 +46,20 @@ extension CxxVector {
3746
self.push_back(item)
3847
}
3948
}
40-
}
4149

42-
extension CxxVector {
4350
@inlinable
4451
public init(arrayLiteral elements: Element...) {
4552
self.init(elements)
4653
}
54+
55+
@discardableResult
56+
@inlinable
57+
public mutating func remove(at index: Int) -> Element {
58+
// Not using CxxIterator here to avoid making a copy of the collection.
59+
var rawIterator = self.__beginUnsafe()
60+
rawIterator += RawIterator.Distance(index)
61+
let element = rawIterator.pointee
62+
self.__eraseUnsafe(rawIterator)
63+
return element
64+
}
4765
}

test/Interop/Cxx/stdlib/use-std-vector.swift

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,48 @@ func fill(vector v: inout Vector) {
8585
v.push_back(CInt(3))
8686
}
8787

88+
StdVectorTestSuite.test("VectorOfInt.remove(at:)") {
89+
var v = Vector()
90+
fill(vector: &v)
91+
92+
let rm1 = v.remove(at: 1)
93+
expectEqual(rm1, 2)
94+
expectEqual(v.size(), 2)
95+
expectEqual(v[0], 1)
96+
expectEqual(v[1], 3)
97+
98+
let rm2 = v.remove(at: 0)
99+
expectEqual(rm2, 1)
100+
expectEqual(v.size(), 1)
101+
expectEqual(v[0], 3)
102+
}
103+
104+
StdVectorTestSuite.test("VectorOfString.remove(at:)") {
105+
var v = VectorOfString()
106+
v.push_back(std.string())
107+
v.push_back(std.string("123"))
108+
v.push_back(std.string("abc"))
109+
v.push_back(std.string("qwe"))
110+
111+
let rm1 = v.remove(at: 3)
112+
expectEqual(rm1, std.string("qwe"))
113+
expectEqual(v.size(), 3)
114+
expectEqual(v[0], std.string())
115+
expectEqual(v[1], std.string("123"))
116+
expectEqual(v[2], std.string("abc"))
117+
118+
let rm2 = v.remove(at: 1)
119+
expectEqual(rm2, std.string("123"))
120+
expectEqual(v.size(), 2)
121+
122+
v.remove(at: 0)
123+
expectEqual(v.size(), 1)
124+
125+
v.remove(at: 0)
126+
expectEqual(v.size(), 0)
127+
expectTrue(v.empty())
128+
}
129+
88130
StdVectorTestSuite.test("VectorOfInt for loop") {
89131
var v = Vector()
90132
fill(vector: &v)

0 commit comments

Comments
 (0)