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
36 changes: 18 additions & 18 deletions cpp-overload-test/examples/stdcpp-forward-list.stderr
Original file line number Diff line number Diff line change
@@ -1,25 +1,25 @@
error: proc macro panicked
--> cpp-overload-test/examples/stdcpp-forward-list.rs:151:1
--> cpp-overload-test/examples/stdcpp-forward-list.rs:157:1
|
151 | / overload! {
152 | | impl StdForwardList {
153 | | /// ### Incompatibilities
154 | | ///
157 | / overload! {
158 | | impl StdForwardList {
159 | | /// ### Incompatibilities
160 | | ///
... |
165 | | }
171 | | }
| |_^
|
= help: message: all overloads must have the same receiver kind (&self, &mut self, self, or no receiver), got front: &self and front: &mut self

error[E0119]: conflicting implementations of trait `NewArgs<StdForwardListT>` for type `(usize,)`
--> cpp-overload-test/examples/stdcpp-forward-list.rs:29:1
--> cpp-overload-test/examples/stdcpp-forward-list.rs:35:1
|
29 | / overload! {
30 | | impl StdForwardListT {
31 | | /// Construct a list with `count` default-constructed items.
32 | | fn new(_count: c_size_t) -> StdForwardListT {
35 | / overload! {
36 | | impl StdForwardListT {
37 | | /// Construct a list with `count` default-constructed items.
38 | | fn new(_count: c_size_t) -> StdForwardListT {
... |
65 | | }
71 | | }
| | ^
| | |
| |_first implementation here
Expand All @@ -28,14 +28,14 @@ error[E0119]: conflicting implementations of trait `NewArgs<StdForwardListT>` fo
= note: this error originates in the macro `overload` (in Nightly builds, run with -Z macro-backtrace for more info)

error[E0119]: conflicting implementations of trait `NewArgs<StdForwardListT>` for type `(usize, usize)`
--> cpp-overload-test/examples/stdcpp-forward-list.rs:29:1
--> cpp-overload-test/examples/stdcpp-forward-list.rs:35:1
|
29 | / overload! {
30 | | impl StdForwardListT {
31 | | /// Construct a list with `count` default-constructed items.
32 | | fn new(_count: c_size_t) -> StdForwardListT {
35 | / overload! {
36 | | impl StdForwardListT {
37 | | /// Construct a list with `count` default-constructed items.
38 | | fn new(_count: c_size_t) -> StdForwardListT {
... |
65 | | }
71 | | }
| | ^
| | |
| |_first implementation here
Expand Down
160 changes: 158 additions & 2 deletions cpp-overload-test/src/stdcpp/forward_list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ cpp! {{
#include <vector>
#include <ranges>
#include <cstddef>

// The `cpp!` macro can't handle C/C++ function type syntax, so we have to declare function
// types here.
typedef bool BinaryPredicate(const int&, const int&);
}}

/// A wrapper struct to hold the returned C++ pointer.
Expand Down Expand Up @@ -204,7 +208,71 @@ impl StdForwardList {
}
}

// List checks
// Modifiers: internal implementations for `merge(...)`
fn merge_from(self: &mut StdForwardList, other: &mut StdForwardList) {
let slf: *mut c_void = self.0;
let other: *mut c_void = other.0;
unsafe {
cpp!([slf as "std::forward_list<int>*", other as "std::forward_list<int>*"] {
slf->merge(*other);
})
}
}

fn merge_with(
self: &mut StdForwardList,
other: &mut StdForwardList,
less_than_predicate: extern "C" fn(&T, &T) -> bool,
) {
let slf: *mut c_void = self.0;
let other: *mut c_void = other.0;
unsafe {
cpp!([slf as "std::forward_list<int>*", other as "std::forward_list<int>*", less_than_predicate as "BinaryPredicate*"] {
slf->merge(*other, less_than_predicate);
})
}
}

// Transforms: internal implementations for `resize(...)`, `unique(...)`
fn resize_default(&mut self, count: c_size_t) {
let slf: *mut c_void = self.0;
unsafe {
cpp!([slf as "std::forward_list<int>*", count as "size_t"] {
slf->resize(count);
})
}
}

fn resize_with(&mut self, count: c_size_t, value: T) {
let slf: *mut c_void = self.0;
unsafe {
cpp!([slf as "std::forward_list<int>*", count as "size_t", value as "int"] {
slf->resize(count, value);
})
}
}

fn unique_equals(&mut self) -> c_size_t {
let slf: *mut c_void = self.0;
unsafe {
cpp!([slf as "std::forward_list<int>*"] -> c_size_t as "size_t" {
return slf->unique();
})
}
}

fn unique_with(&mut self, eq_predicate: extern "C" fn(&T, &T) -> bool) -> c_size_t {
let slf: *mut c_void = self.0;
unsafe {
// The `cpp!` macro can't handle C/C++ function type syntax, so we have to declare the
// type separately above.
cpp!([slf as "std::forward_list<int>*", eq_predicate as "BinaryPredicate*"] -> c_size_t as "size_t" {
return slf->unique(eq_predicate);
})
}
}

// List checks (not overloaded)
fn is_empty(&self) -> bool {
let slf = self.0 as *const c_void;
let empty = unsafe {
Expand Down Expand Up @@ -303,6 +371,8 @@ overload! {
// Accessors: `front(...)`
overload! {
impl StdForwardList {
/// Get the first element of the list as a constant reference.
///
/// ### Limitations
///
/// The `overload!` macro can't overload on `&self` vs `&mut self`, because it has to
Expand Down Expand Up @@ -339,12 +409,63 @@ overload! {
StdForwardList::front_const(this)
}

/// Get the first element of the list as a mutable reference.
pub fn front(this: &'static mut StdForwardList) -> Option<&'static mut T> where T: Sized {
StdForwardList::front_mut(this)
}
}
}

// Modifiers: `merge(...)`
overload! {
impl StdForwardList {
/// Move elements from `other` into this list, preserving sort order.
/// Both lists must be sorted.
pub fn merge(&mut self, other: &mut StdForwardList) {
self.merge_from(other)
}

/// Move elements from `other` into this list, preserving sort order with the given
/// predicate. Both lists must be sorted according to that predicate.
pub fn merge(&mut self, other: &mut StdForwardList, less_than_predicate: extern "C" fn(&T, &T) -> bool) {
self.merge_with(other, less_than_predicate)
}
}
}

// Transforms: `resize(...)`, `unique(...)`
// We skip `sort(...)` because its argument set is almost identical to `unique(...)`.
overload! {
impl StdForwardList {
/// Resize the list to the given size, filling any new elements with the default value.
pub fn resize(&mut self, count: c_size_t) {
self.resize_default(count)
}

/// Resize the list to the given size, filling any new elements with the given value.
pub fn resize(&mut self, count: c_size_t, value: T) {
self.resize_with(count, value)
}
}
}

overload! {
impl StdForwardList {
/// Remove all consecutive duplicate elements from the list.
/// Returns the number of elements removed.
pub fn unique(&mut self) -> c_size_t {
self.unique_equals()
}

/// Remove all consecutive duplicate elements from the list, using the given predicate to
/// compare elements as equal.
/// Returns the number of elements removed.
pub fn unique(&mut self, eq_predicate: extern "C" fn(&T, &T) -> bool) -> c_size_t {
self.unique_with(eq_predicate)
}
}
}

#[test]
pub fn test_forward_list() {
// Constructors: `new(...)`
Expand All @@ -367,7 +488,7 @@ pub fn test_forward_list() {
//
// The `as_slice` call is required to match the overload, an array doesn't automatically coerce.
// FIXME: maybe add a const generic overload for arrays.
let from_slice = StdForwardList::new([1, 2, 3].as_slice());
let mut from_slice = StdForwardList::new([1, 2, 3].as_slice());
assert_eq!(from_slice.front_const(), Some(&1));

// ### Limitations
Expand All @@ -391,6 +512,8 @@ pub fn test_forward_list() {
let from_initializer_list = StdForwardList::new(3, 2, 1);
assert_eq!(from_initializer_list.front_const(), Some(&3));

// Accessors: `front(...)`
//
// ### Workaround
//
// Leak these lists to get static references to them. This would never work in production.
Expand All @@ -406,4 +529,37 @@ pub fn test_forward_list() {
StdForwardList::front(static_mut(repeat_with)),
Some(&mut 42)
);

// Modifiers: `merge(...)`
let mut merge_into_list = StdForwardList::new([1, 3, 5].as_slice());
let mut merge_from_list = StdForwardList::new([0, 2, 4, 6].as_slice());
merge_into_list.merge(&mut merge_from_list);
assert_eq!(merge_into_list.front_const(), Some(&0));

let mut merge_into_list = StdForwardList::new([5, 3, 1].as_slice());
let mut merge_from_list = StdForwardList::new([6, 4, 2, 0].as_slice());
// This predicate is deliberately inverted.
extern "C" fn more_than_predicate(a: &T, b: &T) -> bool {
a > b
}
merge_into_list.merge(&mut merge_from_list, more_than_predicate);
assert_eq!(merge_into_list.front_const(), Some(&6));

// Transforms: `resize(...)`, `unique(...)`
from_slice.resize(0);
assert!(from_slice.is_empty());
let mut resized_list = StdForwardList::new();
resized_list.resize(1, 42);
assert_eq!(resized_list.front_const(), Some(&42));

let mut unique_list = StdForwardList::new([1, 2, 2, 3, 3, 3, 1].as_slice());
assert_eq!(unique_list.unique(), 3);
assert_eq!(unique_list.front_const(), Some(&1));

let mut unique_list = StdForwardList::new([1, 2, 2, 3, 3, 3, 1].as_slice());
extern "C" fn predicate(a: &T, b: &T) -> bool {
a == b
}
assert_eq!(unique_list.unique(predicate), 3);
assert_eq!(unique_list.front_const(), Some(&1));
}
31 changes: 21 additions & 10 deletions cpp-overloads-in-rust.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,15 @@ See the Limitations section below the table for more details.
? Needs Analysis<br/>
*blank* No C++ Overloads<br/>

| C++ Standard Library Class | Constructors | Accessors | Modifiers | Transforms |
| :------------------------- | :----------- | :-------- | :-------- | :--------- |
| *Free Functions* | Constructors | Accessors | Modifiers | Transforms |
| TODO | | ? | ? | ? |
| *Non-Template Classes* | Constructors | Accessors | Modifiers | Transforms |
| TODO | ? | ? | ? | ? |
| *Template Classes* | Constructors | Accessors | Modifiers | Transforms |
| [forward_list] | ✅ | 🌗⚙️ | ? | ?[^1] |
| TODO | ? | ? | ? | ? |
| C++ Standard Library Class | Constructors | Accessors | Modifiers | Transforms |
| :------------------------- | :----------- | :-------- | :--------- | :--------- |
| *Free Functions* | Constructors | Accessors | Modifiers | Transforms |
| TODO | | ? | ? | ? |
| *Non-Template Classes* | Constructors | Accessors | Modifiers | Transforms |
| TODO | ? | ? | ? | ? |
| *Template Classes* | Constructors | Accessors | Modifiers | Transforms |
| [forward_list] | ✅ | 🌗⚙️ | 🌗⚙️?[^1] | ✅ |
| TODO | ? | ? | ? | ? |

Constructors: Create an instance of the class, called as `Class::Class(...)`<br/>
Accessors: Get information about the class, called as `instance->get(...)`<br/>
Expand All @@ -36,7 +36,7 @@ Transforms: Rearrange the items in a list class, called as `instance->sort(...)`

[forward_list]: https://github.com/rustfoundation/overloading-macros/blob/main/cpp-overload-test/src/stdcpp/forward_list.rs

[^1]: TODO: are [C++ predicate functions](https://en.cppreference.com/cpp/container/forward_list/unique) with const refs ABI-compatible with Rust?
[^1]: Mostly caused by the differences between Rust and C++ iterators, could be overcome with type wrappers.

## Limitations

Expand Down Expand Up @@ -101,3 +101,14 @@ These overloads have no equivalent in Rust:
(`before_begin`/`begin`/`end`/`after_end`)
- initializer list methods: these are also a common source of overload conflicts for C++ templated
lists

#### Equivalences & Supersets

When the same overloaded functions are available on different classes, we only analyse the overload
on one of those classes.

When one overloaded function takes the same argument sets (or a subset of the argument set) of
another overloaded function, we only analyse one of the argument sets. For example,
`unique()`/`unique(predicate)` and `sort()`/`sort(predicate)`.

In some cases, we focus on the most "difficult" overload in a category, and skip the rest.
Loading