From c3f44a08f84c0e3a67e725d196092a9d99074de5 Mon Sep 17 00:00:00 2001 From: Lynndabel Date: Tue, 13 Oct 2026 16:52:08 +0100 Subject: [PATCH 1/3] added unordered_multisetmodule --- cpp-overload-test/src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/cpp-overload-test/src/lib.rs b/cpp-overload-test/src/lib.rs index b72552c..3b29bb3 100644 --- a/cpp-overload-test/src/lib.rs +++ b/cpp-overload-test/src/lib.rs @@ -8,4 +8,5 @@ pub mod stdcpp { // Add new modules for each type here, in alphabetical order. pub mod forward_list; + pub mod unordered_multiset; } From 371f659ef1428fdaca437299910e7b5ea82c451c Mon Sep 17 00:00:00 2001 From: Lynndabel Date: Wed, 14 Oct 2026 14:04:12 +0100 Subject: [PATCH 2/3] Add unordered_multiset wrapper --- .../src/stdcpp/unordered_multiset.rs | 171 ++++++++++++++++++ 1 file changed, 171 insertions(+) create mode 100644 cpp-overload-test/src/stdcpp/unordered_multiset.rs diff --git a/cpp-overload-test/src/stdcpp/unordered_multiset.rs b/cpp-overload-test/src/stdcpp/unordered_multiset.rs new file mode 100644 index 0000000..d75fd2e --- /dev/null +++ b/cpp-overload-test/src/stdcpp/unordered_multiset.rs @@ -0,0 +1,171 @@ +//! 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 + +//! ## Limitations +//! +//! - The `overload!` macro does not support generics, so the implementation is hardcoded to `int` type. +//! - Type annotations are required for some constructor calls due to macro limitations. +//! - Array-to-slice conversion requires explicit `.as_slice()` calls. +//! - Initializer list overloads are limited to fixed argument counts (1-3 arguments). +//! - The slice satisfies C++ `ContiguousIterator`, even though it's not a requirement for the C++ iterator overload. +//! +//! ## Ergonomics +//! +//! - Type annotations are currently required for some overloads where inference should work. +//! - The `as_slice()` call is required to match the slice overload, as arrays don't automatically coerce. +//! - Initializer list overloads require separate functions for each argument count, which is not ergonomic. + +#![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 + #include + #include +}} + +/// A wrapper struct to hold the returned C++ `std::unordered_multiset` 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); + +/// Workaround for missing generic support in the `overload!` macro. +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*" { + return new std::unordered_multiset(); + }) + }; + 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*" { + return new std::unordered_multiset(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*" { + return new std::unordered_multiset{ 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*", 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*", items as "const int*", len as "size_t"] { + slf->insert(items, items + len); + }) + } + } + + fn count(&self, value: T) -> c_size_t { + let slf = self.0 as *const c_void; + unsafe { + cpp!([slf as "const std::unordered_multiset*", 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*"] -> c_char as "char" { + return slf->empty(); + }) + }; + empty != 0 + } +} + +overload! { + impl StdUnorderedMultiset { + /// Construct an empty set. + pub fn new() -> StdUnorderedMultiset { + StdUnorderedMultiset::default() + } + + /// Construct a set by copying items from a slice. + pub fn new(items: &[T]) -> StdUnorderedMultiset { + StdUnorderedMultiset::from_slice(items) + } + + 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) { + self.insert_value(value) + } + + /// Insert a range of values from a slice into the set. + pub fn insert(&mut self, items: &[T]) { + self.insert_range(items) + } + } +} + +#[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_initializer_list = StdUnorderedMultiset::new(2, 1); + assert_eq!(from_initializer_list.count(2), 1); + + 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); +} From be53e0750945ff154ebbb720dba2d966d2d27b3c Mon Sep 17 00:00:00 2001 From: Lynndabel Date: Thu, 15 Oct 2026 22:26:41 +0100 Subject: [PATCH 3/3] Split documentation and add missing constructor overloads --- .../src/stdcpp/unordered_multiset.rs | 69 +++++++++++++++---- 1 file changed, 55 insertions(+), 14 deletions(-) diff --git a/cpp-overload-test/src/stdcpp/unordered_multiset.rs b/cpp-overload-test/src/stdcpp/unordered_multiset.rs index d75fd2e..ff7ba55 100644 --- a/cpp-overload-test/src/stdcpp/unordered_multiset.rs +++ b/cpp-overload-test/src/stdcpp/unordered_multiset.rs @@ -2,20 +2,6 @@ //! //! [unordered_multiset]: https://en.cppreference.com/cpp/container/unordered_multiset -//! ## Limitations -//! -//! - The `overload!` macro does not support generics, so the implementation is hardcoded to `int` type. -//! - Type annotations are required for some constructor calls due to macro limitations. -//! - Array-to-slice conversion requires explicit `.as_slice()` calls. -//! - Initializer list overloads are limited to fixed argument counts (1-3 arguments). -//! - The slice satisfies C++ `ContiguousIterator`, even though it's not a requirement for the C++ iterator overload. -//! -//! ## Ergonomics -//! -//! - Type annotations are currently required for some overloads where inference should work. -//! - The `as_slice()` call is required to match the slice overload, as arrays don't automatically coerce. -//! - Initializer list overloads require separate functions for each argument count, which is not ergonomic. - #![allow(unused_braces, reason = "Generated by overload! macro")] use cpp::cpp; @@ -57,6 +43,24 @@ impl StdUnorderedMultiset { 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*" { + return new std::unordered_multiset(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*"] -> *mut c_void as "std::unordered_multiset*" { + return new std::unordered_multiset(*other_ptr); + }) + }; + StdUnorderedMultiset(set, PhantomData) + } fn from_slice(items: &[T]) -> StdUnorderedMultiset { let len = items.len(); @@ -121,15 +125,41 @@ impl StdUnorderedMultiset { overload! { impl StdUnorderedMultiset { /// Construct an empty set. + /// + /// ### Limitations + /// + /// The `overload!` macro does not support generics, so the implementation is hardcoded to `int` type. + /// Rust does not have default function arguments, so C++ constructors with default arguments + /// (e.g., `unordered_multiset(size_type bucket_count, const Hash& hash = Hash())`) + /// require separate overloads for each argument combination. pub fn new() -> StdUnorderedMultiset { StdUnorderedMultiset::default() } + /// Construct a set with at least `bucket_count` buckets. + pub fn new(bucket_count: c_size_t) -> StdUnorderedMultiset { + StdUnorderedMultiset::from_bucket_count(bucket_count) + } + + /// 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) } @@ -144,6 +174,10 @@ overload! { } /// 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) } @@ -158,9 +192,16 @@ pub fn test_unordered_multiset() { 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);