Skip to content

Commit 58ae64c

Browse files
committed
[cxx-interop] Add CxxSequence protocol to the stdlib overlay
This change adds basic helper protocols and structs that are going to be used for making C++ sequences and collection Swifty by adding conformances to `Swift.Sequence`, `Swift.Collection`, etc. This is not meant to be a final design.
1 parent 40a7e68 commit 58ae64c

File tree

5 files changed

+180
-0
lines changed

5 files changed

+180
-0
lines changed

stdlib/public/Cxx/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ add_dependencies(sdk-overlay libstdcxx-modulemap)
129129
#
130130
add_swift_target_library(swiftstd ${SWIFT_STDLIB_LIBRARY_BUILD_TYPES} IS_SDK_OVERLAY
131131
std.swift
132+
CxxSequence.swift
132133
String.swift
133134

134135
SWIFT_MODULE_DEPENDS_OSX Darwin

stdlib/public/Cxx/CxxSequence.swift

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2022 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
/// Bridged C++ iterator that allows to traverse the elements of a sequence using a for-in loop.
14+
///
15+
/// Mostly useful for conforming a type to the `CxxSequence` protocol and should not generally be used directly.
16+
///
17+
/// - SeeAlso: https://en.cppreference.com/w/cpp/named_req/InputIterator
18+
public protocol UnsafeCxxInputIterator: Equatable {
19+
associatedtype Pointee
20+
21+
/// Returns the unwrapped result of C++ `operator*()`.
22+
///
23+
/// Generally, Swift creates this property automatically for C++ types that define `operator*()`.
24+
var pointee: Pointee { get }
25+
26+
/// Returns an iterator pointing to the next item in the sequence.
27+
func successor() -> Self
28+
}
29+
30+
extension UnsafePointer: UnsafeCxxInputIterator {}
31+
32+
extension UnsafeMutablePointer: UnsafeCxxInputIterator {}
33+
34+
public protocol CxxSequence: Sequence {
35+
associatedtype RawIterator: UnsafeCxxInputIterator
36+
associatedtype Element = RawIterator.Pointee
37+
func begin() -> RawIterator
38+
func end() -> RawIterator
39+
}
40+
41+
public struct CxxIterator<T>: IteratorProtocol where T: CxxSequence {
42+
public typealias Element = T.RawIterator.Pointee
43+
private let sequence: T
44+
private var rawIterator: T.RawIterator
45+
46+
public init(sequence: T, rawIterator: T.RawIterator) {
47+
self.sequence = sequence
48+
self.rawIterator = rawIterator
49+
}
50+
51+
public mutating func next() -> Element? {
52+
// TODO: Should `sequence.end()` be stored in a field of this struct?
53+
// That would change the semantics if someone creates an iterator, and then modifies the sequence.
54+
if self.rawIterator == self.sequence.end() {
55+
return nil
56+
}
57+
let object = self.rawIterator.pointee
58+
self.rawIterator = self.rawIterator.successor()
59+
return object
60+
}
61+
}
62+
63+
extension CxxSequence {
64+
public func makeIterator() -> CxxIterator<Self> {
65+
return CxxIterator(sequence: self, rawIterator: begin())
66+
}
67+
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
#ifndef TEST_INTEROP_CXX_STDLIB_INPUTS_CUSTOM_SEQUENCE_H
2+
#define TEST_INTEROP_CXX_STDLIB_INPUTS_CUSTOM_SEQUENCE_H
3+
4+
#include <cstddef>
5+
#include <iterator>
6+
7+
struct SimpleSequence {
8+
struct ConstIterator {
9+
private:
10+
int value;
11+
12+
public:
13+
using iterator_category = std::input_iterator_tag;
14+
using value_type = int;
15+
using pointer = int *;
16+
using reference = int &;
17+
using difference_type = int;
18+
19+
ConstIterator(int value) : value(value) {}
20+
ConstIterator(const ConstIterator &other) = default;
21+
22+
const int &operator*() const { return value; }
23+
24+
ConstIterator &operator++() {
25+
value++;
26+
return *this;
27+
}
28+
ConstIterator operator++(int) {
29+
auto tmp = ConstIterator(value);
30+
value++;
31+
return tmp;
32+
}
33+
34+
friend bool operator==(const ConstIterator &a, const ConstIterator &b) {
35+
return a.value == b.value;
36+
}
37+
friend bool operator!=(const ConstIterator &a, const ConstIterator &b) {
38+
return a.value != b.value;
39+
}
40+
};
41+
42+
ConstIterator begin() const { return ConstIterator(1); }
43+
ConstIterator end() const { return ConstIterator(5); }
44+
};
45+
46+
struct SimpleArrayWrapper {
47+
private:
48+
int a[5] = {10, 20, 30, 40, 50};
49+
50+
public:
51+
// Unfortunately `__attribute__((returns_nonnull))` is required, otherwise
52+
// the return type would be `UnsafePointer<Int32>!` instead of
53+
// `UnsafePointer<Int32>`, and `UnsafePointer<Int32>!` does not conform to
54+
// UnsafeCxxInputIterator.
55+
const int *begin() const __attribute__((returns_nonnull)) { return &a[0]; }
56+
const int *end() const __attribute__((returns_nonnull)) { return &a[5]; }
57+
};
58+
59+
#endif // TEST_INTEROP_CXX_STDLIB_INPUTS_CUSTOM_SEQUENCE_H
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
module CustomSequence {
2+
header "custom-sequence.h"
3+
requires cplusplus
4+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
// RUN: %target-typecheck-verify-swift -I %S/Inputs -enable-experimental-cxx-interop
2+
//
3+
// REQUIRES: OS=macosx || OS=linux-gnu
4+
5+
import CustomSequence
6+
import std
7+
8+
// === SimpleSequence ===
9+
10+
extension SimpleSequence.ConstIterator: UnsafeCxxInputIterator {
11+
// This should probably be synthesized from operator++.
12+
public func successor() -> Self {
13+
return SimpleSequence.ConstIterator(pointee + 1)
14+
}
15+
16+
// This shouldn't be required, since operator== is defined on the C++ side.
17+
// However, without this decl it fails to typecheck, likely due to a lookup issue.
18+
public static func ==(lhs: SimpleSequence.ConstIterator, rhs: SimpleSequence.ConstIterator) -> Bool {
19+
return lhs.pointee == rhs.pointee
20+
}
21+
}
22+
23+
extension SimpleSequence: CxxSequence {}
24+
25+
func checkSimpleSequence() {
26+
let seq = SimpleSequence()
27+
let contains = seq.contains(where: { $0 == 3 })
28+
print(contains)
29+
30+
for item in seq {
31+
print(item)
32+
}
33+
}
34+
35+
// === SimpleArrayWrapper ===
36+
37+
// No UnsafeCxxInputIterator conformance required, since the iterators are actually UnsafePointers here.
38+
39+
extension SimpleArrayWrapper: CxxSequence {}
40+
41+
func checkSimpleArrayWrapper() {
42+
let seq = SimpleArrayWrapper()
43+
let contains = seq.contains(where: { $0 == 25 })
44+
print(contains)
45+
46+
for item in seq {
47+
print(item)
48+
}
49+
}

0 commit comments

Comments
 (0)