Repository navigation
Add unordered_multiset wrapper #51
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
+333
−0
Merged
Changes from all commits
Commits
Show all changes
24 commits
Select commit
Hold shift + click to select a range
c3f44a0
added unordered_multisetmodule
Lynndabel 371f659
Add unordered_multiset wrapper
Lynndabel b0c7534
Merge branch 'main' into unordered_multiset
Lynndabel 087664b
Merge branch 'main' into unordered_multiset
Lynndabel be53e07
Split documentation and add missing constructor overloads
Lynndabel 8c5a097
Merge branch 'main' into unordered_multiset
Lynndabel ff27429
Improve documentation about Rust default arguments limitation
Lynndabel 597b2cf
Add hash and key_equal constructor overloads with default arguments
Lynndabel 7e354ae
Remove hash and key_equal constructors due to C++ template complexity
Lynndabel 57c9dc8
Merge branch 'main' into unordered_multiset
Lynndabel 96151cf
Document failing overloads for step 2
Lynndabel 27b4afe
Merge branch 'main' into unordered_multiset
Lynndabel 8f280fb
Add more insert overloads and move default args documentation
Lynndabel e46dbb6
Remove failing overloads section and move to constructor docs
Lynndabel c1909ee
Move generic limitation comment and add step 2 failing overloads
Lynndabel c791fc3
Merge branch 'main' into unordered_multiset
Lynndabel 0eb87b0
Add step 2 example file for failing hash/key_equal overloads
Lynndabel 4477558
Merge branch 'main' into unordered_multiset
Lynndabel 93f5e06
Move hash/key_equal to working file and add failing overload example
Lynndabel bd541f6
Fix stderr file format
Lynndabel 0de1a8a
Add unordered_multiset to compatibility table
Lynndabel b4625ff
Add missing transforms overloads to unordered_multiset entry
Lynndabel 11d26f7
Regenerate stderr file
Lynndabel 5ce8e6c
Remove Compiling line from stderr to match CI filtering
Lynndabel File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,39 @@ | ||
| //! Test C++ [std::unordered_multiset][unordered_multiset] overloads that can't be represented in Rust. | ||
| //! Working overloads are in [unordered_multiset.rs]. | ||
| //! | ||
| //! [unordered_multiset]: https://en.cppreference.com/cpp/container/unordered_multiset | ||
| //! [unordered_multiset.rs]: https://github.com/rustfoundation/overloading-macros/blob/main/cpp-overload-test/src/stdcpp/unordered_multiset.rs | ||
| //! | ||
| //! The `cpp!` macro doesn't work here because examples are built separately from `lib.rs`. | ||
|
|
||
| #![feature(splat, tuple_trait, c_size_t)] | ||
| #![allow(incomplete_features)] | ||
| #![allow(unused_braces)] | ||
|
|
||
| use splat_overload::overload; | ||
| use std::ffi::c_void; | ||
| use std::marker::PhantomData; | ||
|
|
||
| #[expect(dead_code, reason = "Incompatible overloads can't be used")] | ||
| struct StdUnorderedMultiset(*mut c_void, PhantomData<T>); | ||
|
|
||
| type T = std::ffi::c_int; | ||
|
|
||
| // The overload! macro can't overload on &self vs &mut self | ||
| overload! { | ||
| impl StdUnorderedMultiset { | ||
| /// ### Incompatibilities | ||
| /// | ||
| /// The overload! macro can't overload on `&self` vs `&mut self`, because it has to | ||
| /// dispatch the overload through a single function, which can only have one receiver type. | ||
| fn begin(&self) where T: Sized { | ||
| todo!() | ||
| } | ||
|
|
||
| fn begin(&mut self) where T: Sized { | ||
| todo!() | ||
| } | ||
| } | ||
| } | ||
|
|
||
| pub fn main() {} |
14 changes: 14 additions & 0 deletions
14
cpp-overload-test/examples/stdcpp-unordered-multiset.stderr
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| error: proc macro panicked | ||
| --> cpp-overload-test/examples/stdcpp-unordered-multiset.rs:23:1 | ||
| | | ||
| 23 | / overload! { | ||
| 24 | | impl StdUnorderedMultiset { | ||
| 25 | | /// ### Incompatibilities | ||
| 26 | | /// | ||
| ... | | ||
| 37 | | } | ||
| | |_^ | ||
| | | ||
| = help: message: all overloads must have the same receiver kind (&self, &mut self, self, or no receiver), got begin: &self and begin: &mut self | ||
|
|
||
| error: could not compile `cpp-overload-test` (example "stdcpp-unordered-multiset") due to 1 previous error |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,275 @@ | ||
| //! Test that C++ overloads for [std::unordered_multiset][unordered_multiset] can be successfully represented in Rust. | ||
| //! | ||
| //! [unordered_multiset]: https://en.cppreference.com/cpp/container/unordered_multiset | ||
|
|
||
| #![allow(unused_braces, reason = "Generated by overload! macro")] | ||
|
|
||
| use cpp::cpp; | ||
| use splat_overload::overload; | ||
| use std::ffi::{c_char, c_size_t, c_void}; | ||
| use std::marker::PhantomData; | ||
|
|
||
| // C++ header includes | ||
| cpp! {{ | ||
| #include <unordered_set> | ||
| #include <vector> | ||
| #include <cstddef> | ||
| }} | ||
|
|
||
| /// A wrapper struct to hold the returned C++ `std::unordered_multiset<int>` pointer. | ||
| /// | ||
| /// ### Limitations | ||
| /// | ||
| /// This struct and the impl should be fully generic over the set type. | ||
| /// FIXME: make the `overload!` macro support generics. | ||
| #[cfg_attr(not(test), expect(dead_code, reason = "Only used in tests"))] | ||
| pub struct StdUnorderedMultiset(*mut c_void, PhantomData<T>); | ||
|
|
||
| /// Workaround for missing generic support in the `overload!` macro. | ||
| /// | ||
| /// The `overload!` macro does not support generics, so the implementation is hardcoded to `int` type. | ||
| pub type T = std::ffi::c_int; | ||
|
|
||
| #[cfg_attr(not(test), expect(dead_code, reason = "Only used in tests"))] | ||
| #[allow( | ||
| non_upper_case_globals, | ||
| reason = "Triggered by cpp! macro in rust-analyzer" | ||
| )] | ||
| impl StdUnorderedMultiset { | ||
| fn default() -> StdUnorderedMultiset { | ||
| let set = unsafe { | ||
| cpp!([] -> *mut c_void as "std::unordered_multiset<int>*" { | ||
| return new std::unordered_multiset<int>(); | ||
| }) | ||
| }; | ||
| StdUnorderedMultiset(set, PhantomData) | ||
| } | ||
|
|
||
| fn from_bucket_count(bucket_count: c_size_t) -> StdUnorderedMultiset { | ||
| let set = unsafe { | ||
| cpp!([bucket_count as "size_t"] -> *mut c_void as "std::unordered_multiset<int>*" { | ||
| return new std::unordered_multiset<int>(bucket_count); | ||
| }) | ||
| }; | ||
| StdUnorderedMultiset(set, PhantomData) | ||
| } | ||
|
|
||
| fn copy_from(other: &StdUnorderedMultiset) -> StdUnorderedMultiset { | ||
| let other_ptr = other.0 as *const c_void; | ||
| let set = unsafe { | ||
| cpp!([other_ptr as "const std::unordered_multiset<int>*"] -> *mut c_void as "std::unordered_multiset<int>*" { | ||
| return new std::unordered_multiset<int>(*other_ptr); | ||
| }) | ||
| }; | ||
| StdUnorderedMultiset(set, PhantomData) | ||
| } | ||
|
|
||
| fn from_slice(items: &[T]) -> StdUnorderedMultiset { | ||
| let len = items.len(); | ||
| let items: *const T = items.as_ptr(); | ||
| let set = unsafe { | ||
| cpp!([items as "const int*", len as "size_t"] -> *mut c_void as "std::unordered_multiset<int>*" { | ||
| return new std::unordered_multiset<int>(items, items + len); | ||
| }) | ||
| }; | ||
| StdUnorderedMultiset(set, PhantomData) | ||
| } | ||
|
|
||
| fn from_initializer_list_2(a: T, b: T) -> StdUnorderedMultiset { | ||
| let set = unsafe { | ||
| cpp!([a as "int", b as "int"] -> *mut c_void as "std::unordered_multiset<int>*" { | ||
| return new std::unordered_multiset<int>{ a, b }; | ||
| }) | ||
| }; | ||
| StdUnorderedMultiset(set, PhantomData) | ||
| } | ||
|
|
||
| fn insert_value(&mut self, value: T) { | ||
| let slf: *mut c_void = self.0; | ||
| unsafe { | ||
| cpp!([slf as "std::unordered_multiset<int>*", value as "int"] { | ||
| slf->insert(value); | ||
| }) | ||
| } | ||
| } | ||
|
|
||
| fn insert_range(&mut self, items: &[T]) { | ||
| let slf: *mut c_void = self.0; | ||
| let len = items.len(); | ||
| let items: *const T = items.as_ptr(); | ||
| unsafe { | ||
| cpp!([slf as "std::unordered_multiset<int>*", items as "const int*", len as "size_t"] { | ||
| slf->insert(items, items + len); | ||
| }) | ||
| } | ||
| } | ||
|
|
||
| fn insert_initializer_list_2(&mut self, a: T, b: T) { | ||
| let slf: *mut c_void = self.0; | ||
| unsafe { | ||
| cpp!([slf as "std::unordered_multiset<int>*", a as "int", b as "int"] { | ||
| slf->insert({ a, b }); | ||
| }) | ||
| } | ||
| } | ||
|
|
||
| fn insert_with_hint(&mut self, hint: c_size_t, value: T) { | ||
| let slf: *mut c_void = self.0; | ||
| unsafe { | ||
| cpp!([slf as "std::unordered_multiset<int>*", hint as "size_t", value as "int"] { | ||
| auto it = slf->begin(); | ||
| std::advance(it, hint); | ||
| slf->insert(it, value); | ||
| }) | ||
| } | ||
| } | ||
|
|
||
| fn count(&self, value: T) -> c_size_t { | ||
| let slf = self.0 as *const c_void; | ||
| unsafe { | ||
| cpp!([slf as "const std::unordered_multiset<int>*", value as "int"] -> c_size_t as "size_t" { | ||
| return slf->count(value); | ||
| }) | ||
| } | ||
| } | ||
|
|
||
| fn is_empty(&self) -> bool { | ||
| let slf = self.0 as *const c_void; | ||
| let empty = unsafe { | ||
| cpp!([slf as "const std::unordered_multiset<int>*"] -> c_char as "char" { | ||
| return slf->empty(); | ||
| }) | ||
| }; | ||
| empty != 0 | ||
| } | ||
| } | ||
|
|
||
| overload! { | ||
| impl StdUnorderedMultiset { | ||
| /// Construct an empty set. | ||
| /// | ||
| /// ### Limitations | ||
| /// | ||
| /// The `overload!` macro does not support generics, so the implementation is hardcoded to `int` type. | ||
| pub fn new() -> StdUnorderedMultiset { | ||
| StdUnorderedMultiset::default() | ||
| } | ||
|
|
||
| /// Construct a set with at least `bucket_count` buckets. | ||
| /// | ||
| /// ### Limitations | ||
| /// | ||
| /// Rust doesn't have default function arguments. In C++, this constructor accepts | ||
| /// optional hash and key_equal arguments with default values. In Rust, each argument | ||
| /// combination would require a separate overload, which becomes unwieldy. | ||
| pub fn new(bucket_count: c_size_t) -> StdUnorderedMultiset { | ||
| StdUnorderedMultiset::from_bucket_count(bucket_count) | ||
| } | ||
|
|
||
| /// Construct a set with at least `bucket_count` buckets and a custom hash function. | ||
| /// | ||
| /// FIXME: This overload can be implemented using the same pattern as BinaryPredicateRust | ||
| /// in forward_list. The C++ function type needs to be declared in a cpp! block. | ||
| pub fn new(_bucket_count: c_size_t, _hash_fn: extern "C" fn(&T) -> c_size_t) -> StdUnorderedMultiset { | ||
| todo!() | ||
| } | ||
|
|
||
| /// Construct a set with at least `bucket_count` buckets and a custom key equality function. | ||
| /// | ||
| /// FIXME: This overload can be implemented using the same pattern as BinaryPredicateRust | ||
| /// in forward_list. The C++ function type needs to be declared in a cpp! block. | ||
| pub fn new(_bucket_count: c_size_t, _key_equal: extern "C" fn(&T, &T) -> bool) -> StdUnorderedMultiset { | ||
| todo!() | ||
| } | ||
|
|
||
| /// Construct a set by copying from another set. | ||
| pub fn new(other: &StdUnorderedMultiset) -> StdUnorderedMultiset { | ||
| StdUnorderedMultiset::copy_from(other) | ||
| } | ||
|
|
||
| /// Construct a set by copying items from a slice. | ||
| /// | ||
| /// ### Ergonomics | ||
| /// | ||
| /// The `as_slice()` call is required to match the slice overload, as arrays don't automatically coerce. | ||
| pub fn new(items: &[T]) -> StdUnorderedMultiset { | ||
| StdUnorderedMultiset::from_slice(items) | ||
| } | ||
|
|
||
| /// Construct a set from an initializer list. | ||
| /// | ||
| /// ### Ergonomics | ||
| /// | ||
| /// Initializer list overloads require separate functions for each argument count, which is not ergonomic. | ||
| pub fn new(a: T, b: T) -> StdUnorderedMultiset { | ||
| StdUnorderedMultiset::from_initializer_list_2(a, b) | ||
| } | ||
| } | ||
| } | ||
|
|
||
| overload! { | ||
| impl StdUnorderedMultiset { | ||
| /// Insert a single value into the set. | ||
| pub fn insert(&mut self, value: T) { | ||
|
teor2345 marked this conversation as resolved.
Resolved
|
||
| self.insert_value(value) | ||
| } | ||
|
|
||
| /// Insert a range of values from a slice into the set. | ||
| /// | ||
| /// ### Ergonomics | ||
| /// | ||
| /// The `as_slice()` call is required to match the slice overload, as arrays don't automatically coerce. | ||
| pub fn insert(&mut self, items: &[T]) { | ||
| self.insert_range(items) | ||
| } | ||
|
|
||
| /// Insert values from an initializer list. | ||
| /// | ||
| /// ### Ergonomics | ||
| /// | ||
| /// Initializer list overloads require separate functions for each argument count, which is not ergonomic. | ||
| pub fn insert(&mut self, a: T, b: T) { | ||
| self.insert_initializer_list_2(a, b) | ||
| } | ||
|
|
||
| /// Insert a value with a position hint. | ||
| pub fn insert(&mut self, hint: c_size_t, value: T) { | ||
| self.insert_with_hint(hint, value) | ||
| } | ||
| } | ||
| } | ||
|
|
||
| #[test] | ||
| pub fn test_unordered_multiset() { | ||
| let default_set = StdUnorderedMultiset::new(); | ||
| assert!(default_set.is_empty()); | ||
|
|
||
| let from_slice = StdUnorderedMultiset::new([1, 2, 3].as_slice()); | ||
| assert_eq!(from_slice.count(1), 1); | ||
|
|
||
| let from_bucket_count = StdUnorderedMultiset::new(10 as c_size_t); | ||
| assert!(from_bucket_count.is_empty()); | ||
|
|
||
| let from_initializer_list = StdUnorderedMultiset::new(2, 1); | ||
| assert_eq!(from_initializer_list.count(2), 1); | ||
|
|
||
| let empty_set = StdUnorderedMultiset::new(); | ||
| let copied = StdUnorderedMultiset::new(&empty_set); | ||
| assert!(copied.is_empty()); | ||
|
|
||
| let mut insert_set = StdUnorderedMultiset::new(); | ||
| insert_set.insert(42); | ||
| assert_eq!(insert_set.count(42), 1); | ||
|
|
||
| let mut insert_set = StdUnorderedMultiset::new(); | ||
| insert_set.insert([1, 2, 3].as_slice()); | ||
| assert_eq!(insert_set.count(1), 1); | ||
|
|
||
| let mut insert_init = StdUnorderedMultiset::new(); | ||
| insert_init.insert(5, 10); | ||
| assert_eq!(insert_init.count(5), 1); | ||
|
|
||
| let mut insert_hint = StdUnorderedMultiset::new(); | ||
| insert_hint.insert(0 as c_size_t, 7); | ||
| assert_eq!(insert_hint.count(7), 1); | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.