Skip to content

[cxx-interop] Add CxxRandomAccessCollection protocol #61554

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

Merged
merged 2 commits into from
Oct 19, 2022
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
CxxRandomAccessCollection.swift
CxxSequence.swift

SWIFT_COMPILE_FLAGS ${SWIFT_RUNTIME_SWIFT_COMPILE_FLAGS} ${SWIFT_STANDARD_LIBRARY_SWIFT_FLAGS}
Expand Down
69 changes: 69 additions & 0 deletions stdlib/public/Cxx/CxxRandomAccessCollection.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
//===----------------------------------------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//

/// Bridged C++ iterator that allows computing the distance between two of its
/// instances, and advancing an instance by a given number of elements.
///
/// Mostly useful for conforming a type to the `CxxRandomAccessCollection`
/// protocol and should not generally be used directly.
///
/// - SeeAlso: https://en.cppreference.com/w/cpp/named_req/RandomAccessIterator
public protocol UnsafeCxxRandomAccessIterator: UnsafeCxxInputIterator {
associatedtype Distance: BinaryInteger

static func -(lhs: Self, rhs: Self) -> Distance
static func +=(lhs: inout Self, rhs: Distance)
}

extension UnsafePointer: UnsafeCxxRandomAccessIterator {}

extension UnsafeMutablePointer: UnsafeCxxRandomAccessIterator {}

public protocol CxxRandomAccessCollection: CxxSequence, RandomAccessCollection {
override associatedtype RawIterator: UnsafeCxxRandomAccessIterator
override associatedtype Element = RawIterator.Pointee

/// Do not implement this function manually in Swift.
func __beginUnsafe() -> RawIterator

/// Do not implement this function manually in Swift.
func __endUnsafe() -> RawIterator
}

extension CxxRandomAccessCollection {
@inlinable
public var startIndex: Int {
return 0
}

@inlinable
public var endIndex: Int {
return count
}

@inlinable
public var count: Int {
return Int(__endUnsafe() - __beginUnsafe())
}

/// A C++ implementation of the subscript might be more performant. This
/// overload should only be used if the C++ type does not define `operator[]`.
@inlinable
public subscript(_ index: Int) -> Element {
_read {
// Not using CxxIterator here to avoid making a copy of the collection.
var rawIterator = __beginUnsafe()
rawIterator += RawIterator.Distance(index)
yield rawIterator.pointee as! Element
}
}
}
31 changes: 31 additions & 0 deletions test/Interop/Cxx/stdlib/overlay/Inputs/custom-collection.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#ifndef TEST_INTEROP_CXX_STDLIB_INPUTS_CUSTOM_COLLECTION_H
#define TEST_INTEROP_CXX_STDLIB_INPUTS_CUSTOM_COLLECTION_H

#include "custom-iterator.h"
#include <iterator>

struct SimpleCollectionNoSubscript {
private:
int x[5] = {1, 2, 3, 4, 5};

public:
using iterator = ConstRACIterator;

iterator begin() const { return iterator(*x); }
iterator end() const { return iterator(*x + 5); }
};

struct SimpleCollectionReadOnly {
private:
int x[5] = {1, 2, 3, 4, 5};

public:
using iterator = ConstRACIteratorRefPlusEq;

iterator begin() const { return iterator(*x); }
iterator end() const { return iterator(*x + 5); }

const int& operator[](int index) const { return x[index]; }
};

#endif // TEST_INTEROP_CXX_STDLIB_INPUTS_CUSTOM_COLLECTION_H
115 changes: 115 additions & 0 deletions test/Interop/Cxx/stdlib/overlay/Inputs/custom-iterator.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,121 @@ struct ConstIterator {
}
};

struct ConstRACIterator {
private:
int value;

public:
using iterator_category = std::random_access_iterator_tag;
using value_type = int;
using pointer = int *;
using reference = const int &;
using difference_type = int;

ConstRACIterator(int value) : value(value) {}
ConstRACIterator(const ConstRACIterator &other) = default;

const int &operator*() const { return value; }

ConstRACIterator &operator++() {
value++;
return *this;
}
ConstRACIterator operator++(int) {
auto tmp = ConstRACIterator(value);
value++;
return tmp;
}

void operator+=(difference_type v) { value += v; }
void operator-=(difference_type v) { value -= v; }
ConstRACIterator operator+(difference_type v) const {
return ConstRACIterator(value + v);
}
ConstRACIterator operator-(difference_type v) const {
return ConstRACIterator(value - v);
}
friend ConstRACIterator operator+(difference_type v,
const ConstRACIterator &it) {
return it + v;
}
int operator-(const ConstRACIterator &other) const {
return value - other.value;
}

bool operator<(const ConstRACIterator &other) const {
return value < other.value;
}

bool operator==(const ConstRACIterator &other) const {
return value == other.value;
}
bool operator!=(const ConstRACIterator &other) const {
return value != other.value;
}
};

// Same as ConstRACIterator, but operator+= returns a reference to this.
struct ConstRACIteratorRefPlusEq {
private:
int value;

public:
using iterator_category = std::random_access_iterator_tag;
using value_type = int;
using pointer = int *;
using reference = const int &;
using difference_type = int;

ConstRACIteratorRefPlusEq(int value) : value(value) {}
ConstRACIteratorRefPlusEq(const ConstRACIteratorRefPlusEq &other) = default;

const int &operator*() const { return value; }

ConstRACIteratorRefPlusEq &operator++() {
value++;
return *this;
}
ConstRACIteratorRefPlusEq operator++(int) {
auto tmp = ConstRACIteratorRefPlusEq(value);
value++;
return tmp;
}

ConstRACIteratorRefPlusEq &operator+=(difference_type v) {
value += v;
return *this;
}
ConstRACIteratorRefPlusEq &operator-=(difference_type v) {
value -= v;
return *this;
}
ConstRACIteratorRefPlusEq operator+(difference_type v) const {
return ConstRACIteratorRefPlusEq(value + v);
}
ConstRACIteratorRefPlusEq operator-(difference_type v) const {
return ConstRACIteratorRefPlusEq(value - v);
}
friend ConstRACIteratorRefPlusEq
operator+(difference_type v, const ConstRACIteratorRefPlusEq &it) {
return it + v;
}
int operator-(const ConstRACIteratorRefPlusEq &other) const {
return value - other.value;
}

bool operator<(const ConstRACIteratorRefPlusEq &other) const {
return value < other.value;
}

bool operator==(const ConstRACIteratorRefPlusEq &other) const {
return value == other.value;
}
bool operator!=(const ConstRACIteratorRefPlusEq &other) const {
return value != other.value;
}
};

/// Same as ConstIterator, but defines `operator==` as a non-member.
struct ConstIteratorOutOfLineEq {
int value;
Expand Down
1 change: 1 addition & 0 deletions test/Interop/Cxx/stdlib/overlay/Inputs/module.modulemap
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
module CustomSequence {
header "custom-iterator.h" // TODO: extract into another module
header "custom-sequence.h"
header "custom-collection.h"
requires cplusplus
}
51 changes: 51 additions & 0 deletions test/Interop/Cxx/stdlib/overlay/custom-collection.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// 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 CxxCollectionTestSuite = TestSuite("CxxCollection")

// === SimpleCollectionNoSubscript ===

extension SimpleCollectionNoSubscript.iterator : UnsafeCxxRandomAccessIterator {
public typealias Distance = difference_type
}
extension SimpleCollectionNoSubscript : CxxRandomAccessCollection {
}

CxxCollectionTestSuite.test("SimpleCollectionNoSubscript as Swift.Collection") {
let c = SimpleCollectionNoSubscript()
expectEqual(c.first, 1)
expectEqual(c.last, 5)
}

// === SimpleCollectionReadOnly ===

extension SimpleCollectionReadOnly.iterator : UnsafeCxxRandomAccessIterator {
public typealias Distance = difference_type
}
extension SimpleCollectionReadOnly : CxxRandomAccessCollection {
}

CxxCollectionTestSuite.test("SimpleCollectionReadOnly as Swift.Collection") {
let c = SimpleCollectionReadOnly()
expectEqual(c.first, 1)
expectEqual(c.last, 5)
}

// === SimpleArrayWrapper ===

extension SimpleArrayWrapper : CxxRandomAccessCollection {
}

CxxCollectionTestSuite.test("SimpleArrayWrapper as Swift.Collection") {
let c = SimpleArrayWrapper()
expectEqual(c.first, 10)
expectEqual(c.last, 50)
}

runAllTests()