From f8c286713624ea5ff70ddb13630b80818df58f9c Mon Sep 17 00:00:00 2001 From: Janeirungu01 Date: Sat, 24 Oct 2026 00:51:00 +0300 Subject: [PATCH 1/5] Add std::multimap constructors and overloaded insert --- cpp-overload-test/src/lib.rs | 2 + cpp-overload-test/src/stdcpp/multimap.rs | 352 +++++++++++++++++++++++ 2 files changed, 354 insertions(+) create mode 100644 cpp-overload-test/src/stdcpp/multimap.rs diff --git a/cpp-overload-test/src/lib.rs b/cpp-overload-test/src/lib.rs index b72552c..4b2ebbf 100644 --- a/cpp-overload-test/src/lib.rs +++ b/cpp-overload-test/src/lib.rs @@ -8,4 +8,6 @@ pub mod stdcpp { // Add new modules for each type here, in alphabetical order. pub mod forward_list; + + pub mod multimap; } diff --git a/cpp-overload-test/src/stdcpp/multimap.rs b/cpp-overload-test/src/stdcpp/multimap.rs new file mode 100644 index 0000000..45d8a4e --- /dev/null +++ b/cpp-overload-test/src/stdcpp/multimap.rs @@ -0,0 +1,352 @@ +//! Test that C++ overloads for [std::multimap][multimap] can be successfully represented in Rust. +//! +//! [multimap]: https://en.cppreference.com/w/cpp/container/multimap +//! +//! Overloaded method names wrapped here: +//! - `new`: default, copy, slice of pairs, and single pair +//! - `insert`: key and value, and hint with key and value +//! +//! # Limitations +//! +//! - The wrapper uses `c_int` for both keys and values because the `overload!` macro +//! does not currently support generics. +//! +//! ## Not wrapped +//! +//! - Overloads that only add an allocator argument (not interesting overload to test). +//! - `operator=` and `swap`, because Rust has no equivalent. +//! - Constructors taking a comparator (not yet tested) +//! - The C++ iterator-range constructor taking two C++ iterators (needs an iterator +//! wrapper type, which does not exist yet). The slice overload of `new` covers the +//! same idea using a Rust slice instead. +//! - The C++23 `std::from_range` constructor (not yet tested). +//! - Move constructor (not yet tested). +//! - Initializer-list constructors with more than one pair(not tested). +//! +//! # Test helpers +//! +//! - `len`, `is_empty` and `count_key` are not overloaded. They exist only to verify the +//! wrapper's behavior from Rust. + +#![allow(unused_braces, reason = "Generated by overload! macro")] + +use cpp::cpp; +use splat_overload::overload; +use std::ffi::{c_size_t, c_void}; +use std::marker::PhantomData; + +cpp! {{ + #include + #include + #include + #include + #include + +}} + +// Workaround for missing generic support in the `overload!` macro. +pub type K = std::ffi::c_int; +pub type V = std::ffi::c_int; + +// The C++ multimap is deleted when the Rust wrapper is dropped. +pub struct StdMultimap(*mut c_void, PhantomData<(K, V)>); + +#[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 StdMultimap { + // Create an empty C++ std::multimap. + fn default() -> StdMultimap { + let map = unsafe { + cpp!([] -> *mut c_void as "std::multimap*" { + return new std::multimap(); + }) + }; + + StdMultimap(map, PhantomData) + } + + // Return the number of key-value pairs. + fn len(&self) -> c_size_t { + let map = self.0 as *const c_void; + + unsafe { + cpp!([map as "const std::multimap*"] + -> c_size_t as "size_t" { + return map->size(); + }) + } + } + + // Check whether the map is empty. + fn is_empty(&self) -> bool { + let map = self.0 as *const c_void; + + unsafe { + cpp!([map as "const std::multimap*"] + -> bool as "bool" { + return map->empty(); + }) + } + } + + // Construct a multimap from a single key-value pair. + fn from_initializer_list(a: (K, V)) -> StdMultimap { + let (ak, av) = a; + let map = unsafe { + cpp!([ak as "int", av as "int"] -> *mut c_void as "std::multimap*" { + return new std::multimap{ {ak, av} }; + }) + }; + StdMultimap(map, PhantomData) + } + + // Number of elements with this key (not overloaded, used to check duplicates are kept). + fn count_key(&self, key: K) -> c_size_t { + let slf = self.0 as *const c_void; + unsafe { + cpp!([slf as "const std::multimap*", key as "int"] -> c_size_t as "size_t" { + return slf->count(key); + }) + } + } + + // Construct a new multimap by copying an existing one. + fn from_copy(other: &StdMultimap) -> StdMultimap { + let other_map = other.0 as *const c_void; + + let map = unsafe { + cpp!([other_map as "const std::multimap*"] + -> *mut c_void as "std::multimap*" { + return new std::multimap(*other_map); + }) + }; + + StdMultimap(map, PhantomData) + } + + // Construct a multimap from a slice of key-value pairs. + fn from_slice(entries: &[(K, V)]) -> StdMultimap { + let keys: Vec = entries.iter().map(|(key, _)| *key).collect(); + let values: Vec = entries.iter().map(|(_, value)| *value).collect(); + + let keys_ptr = keys.as_ptr(); + let values_ptr = values.as_ptr(); + let count = entries.len(); + + let map = unsafe { + cpp!([ + keys_ptr as "const int*", + values_ptr as "const int*", + count as "size_t" + ] -> *mut c_void as "std::multimap*" { + std::vector> pairs; + + for (size_t i = 0; i < count; ++i) { + pairs.emplace_back(keys_ptr[i], values_ptr[i]); + } + + return new std::multimap( + pairs.begin(), + pairs.end() + ); + }) + }; + + StdMultimap(map, PhantomData) + } + + //use insert methods to check that the multimap is working correctly. + fn insert_internal(&mut self, key: K, value: V) { + let map = self.0; + + unsafe { + cpp!([ + map as "std::multimap*", + key as "int", + value as "int" + ] { + map->insert({key, value}); + }) + } + } + + fn insert_with_hint_internal(&mut self, hint_key: K, key: K, value: V) { + let map = self.0; + + unsafe { + cpp!([ + map as "std::multimap*", + hint_key as "int", + key as "int", + value as "int" + ] { + auto hint = map->lower_bound(hint_key); + map->insert(hint, {key, value}); + }) + } + } +} + +// We ignore constructors that only differ by an allocator argument, because they're not interesting overloads to test. +overload! { + impl StdMultimap { + /// Construct an empty multimap. + pub fn new() -> StdMultimap { + StdMultimap::default() + } + + /// Construct a multimap by copying another multimap. + pub fn new(other: &StdMultimap) -> StdMultimap { + StdMultimap::from_copy(other) + } + + /// Construct a multimap from a slice of key-value pairs. + /// + /// ### Limitations + /// + /// Rust tuples are not FFI-safe, so the pairs are split into separate key and value + /// arrays and rebuilt as C++ pairs. This costs extra copies. It also mirrors the C++ + /// iterator-range constructor, but takes a Rust slice rather than C++ iterators. + pub fn new(entries: &[(K, V)]) -> StdMultimap { + StdMultimap::from_slice(entries) + } + + /// Construct a multimap from one key-value pair. + /// Duplicate keys are kept. + /// + /// ### Ergonomics + /// + /// The call needs double parentheses, `StdMultimap::new((1, 10))`, and is easy to confuse + /// with a two-argument call such as `new(1, 10)`, which does not exist. + pub fn new(a: (K, V)) -> StdMultimap { + StdMultimap::from_initializer_list(a) + } + } + +} + +overload! { + impl StdMultimap { + /// Insert a key-value pair into the multimap. + /// Duplicate keys are kept. + /// + /// ### Limitations + /// + /// C++ `insert(value)` returns an iterator to the inserted element. This wrapper + /// returns nothing, because there is no iterator wrapper type yet. Callers can use + /// `count_key` to check the result instead. + /// + /// C++ also takes a single `std::pair`, but Rust tuples are not + /// FFI-safe, so this overload takes the key and value as two separate arguments. + /// + /// ### Ergonomics + /// + /// `insert(1, 10)` and the hint overload `insert(0, 1, 10)` differ only in + /// argument count, so it is easy to mix up which number is the key, the value, or + /// the hint. + pub fn insert(&mut self, key: K, value: V) { + self.insert_internal(key, value); + } + + /// Insert a key-value pair, using the position of `hint_key` as a placement hint. + /// + /// ### Limitations + /// + /// C++ takes an iterator as the hint. This wrapper takes a key and calls + /// `lower_bound` to produce the iterator, so callers cannot pass an arbitrary + /// iterator. The return iterator is also dropped, as above. + /// + /// ### Ergonomics + /// + /// The hint is only a performance hint. A wrong `hint_key` never changes the + /// result, but this isn't obvious from the signature. + pub fn insert(&mut self, hint_key: K, key: K, value: V) { + self.insert_with_hint_internal(hint_key, key, value) + } + } +} + +// The C++ multimap is deleted when the Rust wrapper is dropped. +impl Drop for StdMultimap { + fn drop(&mut self) { + let map = self.0; + + unsafe { + cpp!([map as "std::multimap*"] { + delete map; + }) + } + } +} + +/// These tests show that the correct Rust and C++ overloads are being called. +#[test] +fn test_multimap() { + let default_map = StdMultimap::new(); + assert!(default_map.is_empty()); + assert_eq!(default_map.len(), 0); + + let one = StdMultimap::new((1, 10)); + assert_eq!(one.len(), 1); + assert_eq!(one.count_key(1), 1); +} + +#[test] +fn test_multimap_copy_constructor() { + let entries: &[(K, V)] = &[(1, 10), (1, 20)]; + let original = StdMultimap::new(entries); + let copied = StdMultimap::new(&original); + + assert_eq!(copied.len(), 2); + assert_eq!(copied.count_key(1), 2); + + let mut original = original; + original.insert(2, 30); + + assert_eq!(original.len(), 3); + assert_eq!(copied.len(), 2); +} + +#[test] +fn test_multimap_range_constructor() { + let entries: &[(K, V)] = &[(2, 20), (1, 10), (1, 15)]; + let map = StdMultimap::new(entries); + + assert_eq!(map.len(), 3); + assert_eq!(map.count_key(1), 2); + assert_eq!(map.count_key(2), 1); + assert_eq!(map.count_key(3), 0); +} + +#[test] +fn test_multimap_insert() { + let mut map = StdMultimap::new(); + + assert!(map.is_empty()); + + map.insert(1, 10); + map.insert(1, 20); + map.insert(2, 50); + + assert_eq!(map.len(), 3); + assert_eq!(map.count_key(1), 2); + assert_eq!(map.count_key(2), 1); +} + +#[test] +fn test_multimap_insert_with_hint() { + let mut map = StdMultimap::new(); + + assert!(map.is_empty()); + + map.insert(0, 1, 10); + map.insert(1, 1, 20); + map.insert(2, 2, 50); + + assert_eq!(map.len(), 3); + assert_eq!(map.count_key(1), 2); + assert_eq!(map.count_key(2), 1); +} From b7820deaebd2901b25856186bd33326ee44064e0 Mon Sep 17 00:00:00 2001 From: Janeirungu01 Date: Sun, 25 Oct 2026 19:37:43 +0300 Subject: [PATCH 2/5] add std::multimap move constructor and insert slice overload --- cpp-overload-test/src/stdcpp/multimap.rs | 303 ++++++++++++++++++++--- 1 file changed, 263 insertions(+), 40 deletions(-) diff --git a/cpp-overload-test/src/stdcpp/multimap.rs b/cpp-overload-test/src/stdcpp/multimap.rs index 45d8a4e..d269965 100644 --- a/cpp-overload-test/src/stdcpp/multimap.rs +++ b/cpp-overload-test/src/stdcpp/multimap.rs @@ -1,10 +1,11 @@ //! Test that C++ overloads for [std::multimap][multimap] can be successfully represented in Rust. //! //! [multimap]: https://en.cppreference.com/w/cpp/container/multimap +//! [multimap.rs]: https://github.com/rustfoundation/overloading-macros/blob/main/cpp-overload-test/src/stdcpp/multimap.rs //! //! Overloaded method names wrapped here: -//! - `new`: default, copy, slice of pairs, and single pair -//! - `insert`: key and value, and hint with key and value +//! - `new`: default, copy, move, slice of pairs, and key-value pairs(1 pair, 2 pairs, 3 pairs) +//! - `insert`: (key, value), (hint_key, key, value), (&[(K, V)]) //! //! # Limitations //! @@ -13,42 +14,37 @@ //! //! ## Not wrapped //! -//! - Overloads that only add an allocator argument (not interesting overload to test). +//! skipped by task rules: +//! - Overloads that only add an allocator argument. //! - `operator=` and `swap`, because Rust has no equivalent. -//! - Constructors taking a comparator (not yet tested) -//! - The C++ iterator-range constructor taking two C++ iterators (needs an iterator -//! wrapper type, which does not exist yet). The slice overload of `new` covers the -//! same idea using a Rust slice instead. -//! - The C++23 `std::from_range` constructor (not yet tested). -//! - Move constructor (not yet tested). -//! - Initializer-list constructors with more than one pair(not tested). -//! -//! # Test helpers -//! -//! - `len`, `is_empty` and `count_key` are not overloaded. They exist only to verify the -//! wrapper's behavior from Rust. +//! - `insert(value_type&&)` and `insert(hint, value_type&&)`: differ from the `const&` +//! versions only by `&&`. +//! - Compare constructor - `Compare` is a type parameter, and the wrapper is fixed to `std::multimap`. +//! - C++ iterator-range constructor (first, last) - slice constructor covers the idea +//! - std::from_range (C++23) - depends on C++23 library support +//! - insert(P&&) - has no Rust equivalent +//! - insert(node_type&&) and insert(hint, node_type&&) - node handles come from extracting nodes, which is not yet wrapped +//! - insert(initializer_list) and insert(first, last) - implemented as insert(&[(K, V)])(slice overload) #![allow(unused_braces, reason = "Generated by overload! macro")] use cpp::cpp; use splat_overload::overload; -use std::ffi::{c_size_t, c_void}; +use std::ffi::{c_char, c_size_t, c_void}; use std::marker::PhantomData; cpp! {{ #include - #include #include #include #include - }} // Workaround for missing generic support in the `overload!` macro. pub type K = std::ffi::c_int; pub type V = std::ffi::c_int; -// The C++ multimap is deleted when the Rust wrapper is dropped. +// Rust wrapper around a heap-allocated C++ std::multimap. pub struct StdMultimap(*mut c_void, PhantomData<(K, V)>); #[cfg_attr(not(test), expect(dead_code, reason = "Only used in tests"))] @@ -58,6 +54,7 @@ pub struct StdMultimap(*mut c_void, PhantomData<(K, V)>); )] impl StdMultimap { // Create an empty C++ std::multimap. + // not overloaded, but used to implement the default constructor. fn default() -> StdMultimap { let map = unsafe { cpp!([] -> *mut c_void as "std::multimap*" { @@ -68,7 +65,8 @@ impl StdMultimap { StdMultimap(map, PhantomData) } - // Return the number of key-value pairs. + // Returns the number of key-value pairs. + // not overloaded, but used to check that the multimap is working correctly. fn len(&self) -> c_size_t { let map = self.0 as *const c_void; @@ -80,20 +78,23 @@ impl StdMultimap { } } - // Check whether the map is empty. + // not overloaded, but used to check that the multimap is working correctly. fn is_empty(&self) -> bool { let map = self.0 as *const c_void; - unsafe { - cpp!([map as "const std::multimap*"] - -> bool as "bool" { + let empty = unsafe { + cpp!([map as "const std::multimap*"] -> c_char as "char" { + // `char` is FFI-safe, but Rust `bool` is not. + // C++ automatically promotes `bool` to `char`. return map->empty(); }) - } + }; + + empty != 0 } // Construct a multimap from a single key-value pair. - fn from_initializer_list(a: (K, V)) -> StdMultimap { + fn from_initializer_list_1(a: (K, V)) -> StdMultimap { let (ak, av) = a; let map = unsafe { cpp!([ak as "int", av as "int"] -> *mut c_void as "std::multimap*" { @@ -103,6 +104,42 @@ impl StdMultimap { StdMultimap(map, PhantomData) } + fn from_initializer_list_2(a: (K, V), b: (K, V)) -> StdMultimap { + let (ak, av) = a; + let (bk, bv) = b; + + let map = unsafe { + cpp!([ + ak as "int", av as "int", bk as "int", bv as "int"] -> *mut c_void as "std::multimap*" { + return new std::multimap{{ak, av},{bk, bv} }; + }) + }; + + StdMultimap(map, PhantomData) + } + + fn from_initializer_list_3(a: (K, V), b: (K, V), c: (K, V)) -> StdMultimap { + let (ak, av) = a; + let (bk, bv) = b; + let (ck, cv) = c; + + let map = unsafe { + cpp!([ + ak as "int", av as "int", + bk as "int", bv as "int", + ck as "int", cv as "int" + ] -> *mut c_void as "std::multimap*" { + return new std::multimap{ + {ak, av}, + {bk, bv}, + {ck, cv} + }; + }) + }; + + StdMultimap(map, PhantomData) + } + // Number of elements with this key (not overloaded, used to check duplicates are kept). fn count_key(&self, key: K) -> c_size_t { let slf = self.0 as *const c_void; @@ -127,8 +164,29 @@ impl StdMultimap { StdMultimap(map, PhantomData) } - // Construct a multimap from a slice of key-value pairs. + // Construct a new multimap by moving from another multimap. + fn move_from(other: &mut StdMultimap) -> StdMultimap { + let other_map = other.0; + + let map = unsafe { + cpp!([other_map as "std::multimap*"] + -> *mut c_void as "std::multimap*" { + return new std::multimap(std::move(*other_map)); + }) + }; + + StdMultimap(map, PhantomData) + } + fn from_slice(entries: &[(K, V)]) -> StdMultimap { + // An empty Rust slice has a dangling pointer, see + // + if entries.is_empty() { + return StdMultimap::default(); + } + + // Rust tuples are not assumed to have the same layout as C++ pairs. + // Split the entries into separate arrays for the FFI boundary. let keys: Vec = entries.iter().map(|(key, _)| *key).collect(); let values: Vec = entries.iter().map(|(_, value)| *value).collect(); @@ -136,6 +194,8 @@ impl StdMultimap { let values_ptr = values.as_ptr(); let count = entries.len(); + // SAFETY: both pointers are non-null and point to `count` initialized ints. + // `keys` and `values` stay alive until after the C++ call returns. let map = unsafe { cpp!([ keys_ptr as "const int*", @@ -143,6 +203,7 @@ impl StdMultimap { count as "size_t" ] -> *mut c_void as "std::multimap*" { std::vector> pairs; + pairs.reserve(count); // avoids repeated reallocation for (size_t i = 0; i < count; ++i) { pairs.emplace_back(keys_ptr[i], values_ptr[i]); @@ -158,10 +219,12 @@ impl StdMultimap { StdMultimap(map, PhantomData) } - //use insert methods to check that the multimap is working correctly. + // Insert one key-value pair (the C++ `insert(value)` overload). fn insert_internal(&mut self, key: K, value: V) { let map = self.0; + // SAFETY: `map` is a valid pointer to a live `std::multimap`, owned by + // `self`, and `&mut self` guarantees no other access during the call. unsafe { cpp!([ map as "std::multimap*", @@ -173,9 +236,13 @@ impl StdMultimap { } } + // Insert one key-value pair, using the position found by `lower_bound(hint_key)` + // as the hint (the C++ `insert(hint, value)` overload). fn insert_with_hint_internal(&mut self, hint_key: K, key: K, value: V) { let map = self.0; + // SAFETY: `map` is a valid pointer to a live `std::multimap`, owned by + // `self`, and `&mut self` guarantees no other access during the call. unsafe { cpp!([ map as "std::multimap*", @@ -188,6 +255,39 @@ impl StdMultimap { }) } } + + // Insert every pair from a slice (the C++ `insert(first, last)` overload). + fn insert_slice_internal(&mut self, entries: &[(K, V)]) { + // Inserting an empty slice should leave the map unchanged. + if entries.is_empty() { + return; + } + + // Rust tuples are not assumed to have the same layout as C++ pairs. + let keys: Vec = entries.iter().map(|(key, _)| *key).collect(); + let values: Vec = entries.iter().map(|(_, value)| *value).collect(); + + let keys_ptr = keys.as_ptr(); + let values_ptr = values.as_ptr(); + let count = entries.len(); + let map_ptr = self.0; + + // SAFETY: both pointers point to `count` initialized ints, and `keys` and + // `values` stay alive until the C++ call returns. `map_ptr` is a valid pointer + // owned by `self`, and `&mut self` guarantees no other access. + unsafe { + cpp!([ + map_ptr as "std::multimap*", + keys_ptr as "const int*", + values_ptr as "const int*", + count as "size_t" + ] { + for (size_t i = 0; i < count; ++i) { + map_ptr->insert({keys_ptr[i], values_ptr[i]}); + } + }) + } + } } // We ignore constructors that only differ by an allocator argument, because they're not interesting overloads to test. @@ -198,11 +298,16 @@ overload! { StdMultimap::default() } - /// Construct a multimap by copying another multimap. + /// Copy-construct a new multimap from a shared multimap reference. pub fn new(other: &StdMultimap) -> StdMultimap { StdMultimap::from_copy(other) } + /// Move-construct a new multimap from a mutable multimap reference. + pub fn new(other: &mut StdMultimap) -> StdMultimap { + StdMultimap::move_from(other) + } + /// Construct a multimap from a slice of key-value pairs. /// /// ### Limitations @@ -214,15 +319,46 @@ overload! { StdMultimap::from_slice(entries) } - /// Construct a multimap from one key-value pair. - /// Duplicate keys are kept. + /// Construct a multimap from the supplied key-value pairs. + /// + /// Each argument represents one `(key, value)` pair. The resulting + /// multimap contains all supplied pairs, including pairs with duplicate keys. + /// + /// ### Limitations + /// + /// This is just an example; a production implementation would support + /// any number of key-value pairs. + /// + /// Rust doesn't have a language equivalent to C++'s `std::initializer_list`, + /// and it doesn't have variadic tuples, so we provide separate constructors for + /// one, two, and three key-value pairs instead of a single constructor that + /// accepts any number of pairs. + /// + /// These overloads do not clash with the other `new` overloads. Each pair is a + /// tuple `(K, V)`, which is a different type from `&StdMultimap` and `&[(K, V)]`. + /// + /// These examples use fixed-size tuples for key-value pairs because the + /// current wrapper uses `c_int` for both keys and values. The underlying + /// C++ type and FFI conversions are defined for `std::multimap`. /// /// ### Ergonomics /// - /// The call needs double parentheses, `StdMultimap::new((1, 10))`, and is easy to confuse - /// with a two-argument call such as `new(1, 10)`, which does not exist. + /// Each key-value pair must be passed as a tuple. For example, constructing a multimap with + /// one pair requires `StdMultimap::new((1, 10))`, while multiple pairs can be passed as + /// `StdMultimap::new((1, 10), (2, 20))`. + /// The double parentheses can make the syntax confusing, especially + /// for users who might expect to pass the key and value as separate + /// arguments, such as `StdMultimap::new(1, 10)`. pub fn new(a: (K, V)) -> StdMultimap { - StdMultimap::from_initializer_list(a) + StdMultimap::from_initializer_list_1(a) + } + + pub fn new(a: (K, V), b: (K, V)) -> StdMultimap { + StdMultimap::from_initializer_list_2(a, b) + } + + pub fn new(a: (K, V), b: (K, V), c: (K, V)) -> StdMultimap { + StdMultimap::from_initializer_list_3(a, b, c) } } @@ -261,11 +397,31 @@ overload! { /// /// ### Ergonomics /// - /// The hint is only a performance hint. A wrong `hint_key` never changes the - /// result, but this isn't obvious from the signature. + /// The hint is only a performance hint. A wrong `hint_key` never changes which elements + /// are stored, but can change order for equal keys. pub fn insert(&mut self, hint_key: K, key: K, value: V) { self.insert_with_hint_internal(hint_key, key, value) } + + /// Insert every key-value pair from a slice. Duplicate keys are kept. + /// + /// ### Limitations + /// + /// This one overload stands in for two C++ overloads: `insert(first, last)` and + /// `insert(initializer_list)`. Rust has no `initializer_list`, and a slice covers the + /// same use. It takes a Rust slice, not C++ iterators, so a range from another C++ + /// container can't be passed in. + /// + /// Rust tuples are not FFI-safe, so the pairs are split into two arrays first, which + /// costs extra copies. + /// + /// ### Ergonomics + /// + /// An array needs a slice conversion, or a typed variable first, for example + /// `let entries: &[(K, V)] = &[(1, 10)];`. + pub fn insert(&mut self, entries: &[(K, V)]) { + self.insert_slice_internal(entries) + } } } @@ -294,6 +450,24 @@ fn test_multimap() { assert_eq!(one.count_key(1), 1); } +#[test] +fn test_multimap_initializer_list_two_pairs() { + let map = StdMultimap::new((2, 20), (1, 10)); + + assert_eq!(map.len(), 2); + assert_eq!(map.count_key(1), 1); + assert_eq!(map.count_key(2), 1); +} + +#[test] +fn test_multimap_initializer_list_three_pairs() { + let map = StdMultimap::new((1, 10), (1, 20), (2, 30)); + + assert_eq!(map.len(), 3); + assert_eq!(map.count_key(1), 2); + assert_eq!(map.count_key(2), 1); +} + #[test] fn test_multimap_copy_constructor() { let entries: &[(K, V)] = &[(1, 10), (1, 20)]; @@ -311,14 +485,37 @@ fn test_multimap_copy_constructor() { } #[test] -fn test_multimap_range_constructor() { - let entries: &[(K, V)] = &[(2, 20), (1, 10), (1, 15)]; +fn test_multimap_move_constructor() { + let mut original = StdMultimap::new((1, 10), (1, 20)); + + let moved = StdMultimap::new(&mut original); + + assert_eq!(moved.len(), 2); + assert_eq!(moved.count_key(1), 2); + + let _ = original.len(); + original.insert(5, 50); + assert_eq!(original.count_key(5), 1); +} + +#[test] +fn test_multimap_empty_slice_constructor() { + let entries: &[(K, V)] = &[]; + let map = StdMultimap::new(entries); + + assert!(map.is_empty()); + assert_eq!(map.len(), 0); +} + +#[test] +fn test_multimap_slice_constructor_with_duplicate_keys() { + let entries: &[(K, V)] = &[(1, 10), (2, 20), (1, 30)]; + let map = StdMultimap::new(entries); assert_eq!(map.len(), 3); assert_eq!(map.count_key(1), 2); assert_eq!(map.count_key(2), 1); - assert_eq!(map.count_key(3), 0); } #[test] @@ -337,7 +534,7 @@ fn test_multimap_insert() { } #[test] -fn test_multimap_insert_with_hint() { +fn test_multimap_insert_hint_overload() { let mut map = StdMultimap::new(); assert!(map.is_empty()); @@ -350,3 +547,29 @@ fn test_multimap_insert_with_hint() { assert_eq!(map.count_key(1), 2); assert_eq!(map.count_key(2), 1); } + +#[test] +fn test_multimap_insert_slice() { + let mut map = StdMultimap::new(); + map.insert(1, 10); + + let entries: &[(K, V)] = &[(2, 20), (3, 30), (2, 25)]; + map.insert(entries); + + assert_eq!(map.len(), 4); + assert_eq!(map.count_key(1), 1); + assert_eq!(map.count_key(2), 2); + assert_eq!(map.count_key(3), 1); +} + +#[test] +fn test_multimap_insert_empty_slice() { + let mut map = StdMultimap::new(); + map.insert(1, 10); + + let entries: &[(K, V)] = &[]; + map.insert(entries); + + assert_eq!(map.len(), 1); + assert_eq!(map.count_key(1), 1); +} From 0c937183caff190fa15a597c56855940c806465e Mon Sep 17 00:00:00 2001 From: Janeirungu01 Date: Tue, 27 Oct 2026 17:47:18 +0300 Subject: [PATCH 3/5] add erase and merge overloads to multimap --- cpp-overload-test/src/stdcpp/multimap.rs | 335 ++++++++++++++++++++--- 1 file changed, 292 insertions(+), 43 deletions(-) diff --git a/cpp-overload-test/src/stdcpp/multimap.rs b/cpp-overload-test/src/stdcpp/multimap.rs index d269965..4d1ee37 100644 --- a/cpp-overload-test/src/stdcpp/multimap.rs +++ b/cpp-overload-test/src/stdcpp/multimap.rs @@ -4,8 +4,10 @@ //! [multimap.rs]: https://github.com/rustfoundation/overloading-macros/blob/main/cpp-overload-test/src/stdcpp/multimap.rs //! //! Overloaded method names wrapped here: -//! - `new`: default, copy, move, slice of pairs, and key-value pairs(1 pair, 2 pairs, 3 pairs) -//! - `insert`: (key, value), (hint_key, key, value), (&[(K, V)]) +//! - `new`: default, move, slice of pairs, and one, two or three pairs +//! - `insert`: `(key, value)`, `(hint_key, key, value)`, and `(&[(K, V)])` +//! - `erase`: `(key)`, `(first_key, last_key)`, `(&StdMultimapIter)` +//! - `merge`: `(&mut StdMultimap)` //! //! # Limitations //! @@ -14,17 +16,37 @@ //! //! ## Not wrapped //! -//! skipped by task rules: +//! Skipped by task rules: +//! //! - Overloads that only add an allocator argument. //! - `operator=` and `swap`, because Rust has no equivalent. //! - `insert(value_type&&)` and `insert(hint, value_type&&)`: differ from the `const&` //! versions only by `&&`. +//! +//! Constructors (`new`): +//! - Copy constructor `multimap(const multimap&)`: differs from the wrapped move constructor +//! only by `const&` vs `&&`, so one example is enough. //! - Compare constructor - `Compare` is a type parameter, and the wrapper is fixed to `std::multimap`. //! - C++ iterator-range constructor (first, last) - slice constructor covers the idea //! - std::from_range (C++23) - depends on C++23 library support -//! - insert(P&&) - has no Rust equivalent -//! - insert(node_type&&) and insert(hint, node_type&&) - node handles come from extracting nodes, which is not yet wrapped -//! - insert(initializer_list) and insert(first, last) - implemented as insert(&[(K, V)])(slice overload) +//! +//! (`insert`): +//! - `insert(P&&)` - has no Rust equivalent +//! - `insert(node_type&&)` and `insert(hint, node_type&&)` - node handles come from extracting nodes, which is not yet wrapped +//! - `insert(initializer_list)` and `insert(first, last)` - implemented as `insert(&[(K, V)])`(slice overload) +//! +//! (`erase`): +//! - `erase(iterator pos)`: have been covered by `erase(const_iterator pos)` - differs by const +//! - `erase(first, last)` with C++ iterators: approximated by `erase(first_key, last_key)`. +//! - `erase(K&& x)`: the wrapper uses a fixed `int` key type, and the key-based +//! `erase(const key_type& key)` overload already covers the main erasure behaviour. +//! +//! (`merge`): +//! - `merge(std::map<..., C2>&)` and `merge(std::map<..., C2>&&)`: need a `std::map` +//! wrapper type, which does not exist here. +//! - `merge(std::multimap<..., C2>&&)`: differs from the wrapped `merge(std::multimap<..., C2>&)` by `&&`. +//! - Source maps with a comparator other than `std::less`: the macro cannot +//! express the `C2` type parameter. #![allow(unused_braces, reason = "Generated by overload! macro")] @@ -40,18 +62,35 @@ cpp! {{ #include }} -// Workaround for missing generic support in the `overload!` macro. +/// Workaround for missing generic support in the `overload!` macro. pub type K = std::ffi::c_int; pub type V = std::ffi::c_int; -// Rust wrapper around a heap-allocated C++ std::multimap. +/// A wrapper struct to hold the returned C++ `std::multimap` pointer. +/// +/// ### Limitations +/// +/// This struct and the impl should be fully generic over the key and value types. +/// FIXME: make the `overload!` macro support generics. pub struct StdMultimap(*mut c_void, PhantomData<(K, V)>); +/// A wrapper struct to hold a C++ `std::multimap::const_iterator` object. +/// +/// ### Limitations +/// +/// The iterator is not tied to the map's lifetime. A borrowing iterator would stop +/// `map.erase(&pos)` from compiling, because `erase` needs `&mut map` while the iterator +/// still borrows the map. +#[cfg_attr(not(test), expect(dead_code, reason = "Only used in tests"))] +pub struct StdMultimapIter(*mut c_void, PhantomData<(K, V)>); + #[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" )] +// The cpp! macro doesn't work inside the overload! macro, so we extract C++ calls into separate methods. +// A mature overload feature (or a production C++ project) wouldn't need this impl block. impl StdMultimap { // Create an empty C++ std::multimap. // not overloaded, but used to implement the default constructor. @@ -150,18 +189,18 @@ impl StdMultimap { } } - // Construct a new multimap by copying an existing one. - fn from_copy(other: &StdMultimap) -> StdMultimap { - let other_map = other.0 as *const c_void; - - let map = unsafe { - cpp!([other_map as "const std::multimap*"] - -> *mut c_void as "std::multimap*" { - return new std::multimap(*other_map); + // Not overloaded, but used to get an iterator to pass to `erase(pos)`. + // Returns an iterator to the first element with a key >= `key`, or `end()`. + fn lower_bound(&self, key: K) -> StdMultimapIter { + let map = self.0 as *const c_void; + let iter = unsafe { + // FIXME: is the returned type always a pointer type? + cpp!([map as "const std::multimap*", key as "int"] + -> *mut c_void as "std::multimap::const_iterator" { + return map->lower_bound(key); }) }; - - StdMultimap(map, PhantomData) + StdMultimapIter(iter, PhantomData) } // Construct a new multimap by moving from another multimap. @@ -256,7 +295,8 @@ impl StdMultimap { } } - // Insert every pair from a slice (the C++ `insert(first, last)` overload). + // Insert every pair from a slice `insert(first, last)` and + // `insert(initializer_list)` overloads. fn insert_slice_internal(&mut self, entries: &[(K, V)]) { // Inserting an empty slice should leave the map unchanged. if entries.is_empty() { @@ -288,6 +328,80 @@ impl StdMultimap { }) } } + + // Erase every element with this key (the C++ `erase(const key_type&)` overload). + fn erase_key_internal(&mut self, key: K) -> c_size_t { + let map = self.0; + + // SAFETY: `map` is a valid pointer owned by `self`, and `&mut self` + // guarantees no other access during the call. + unsafe { + cpp!([map as "std::multimap*", key as "int"] -> c_size_t as "size_t" { + return map->erase(key); + }) + } + } + + // Erase every element with a key in [first_key, last_key) (the C++ `erase(first, last)` overload). + fn erase_range_internal(&mut self, first_key: K, last_key: K) -> c_size_t { + let map = self.0; + + // SAFETY: `map` is a valid pointer owned by `self`, and `&mut self` + // guarantees no other access during the call. + unsafe { + cpp!([ + map as "std::multimap*", + first_key as "int", + last_key as "int" + ] -> c_size_t as "size_t" { + if (last_key <= first_key) { + return 0; + } + auto first = map->lower_bound(first_key); + auto last = map->lower_bound(last_key); + size_t removed = std::distance(first, last); + map->erase(first, last); + return removed; + }) + } + } + + // Erase the element at `pos` (the C++ `erase(const_iterator)` overload). + fn erase_pos_internal(&mut self, pos: &StdMultimapIter) { + let map = self.0; + let pos = pos.0 as *const c_void; + + // SAFETY: `map` is a valid pointer owned by `self`, and `&mut self` guarantees no + // other access during the call. Erasing `end()` is undefined behaviour in C++, so + // it is checked first. `pos` must come from this map and must not be invalidated. + unsafe { + cpp!([ + map as "std::multimap*", + pos as "std::multimap::const_iterator" + ] { + if (pos != map->cend()) { + map->erase(pos); + } + }) + } + } + + // Move all elements from `other` into this multimap (the C++ `merge(multimap&)` overload). + fn merge_internal(&mut self, other: &mut StdMultimap) { + let map = self.0; + let other_map = other.0; + + // SAFETY: both pointers are valid, live multimaps. `&mut self` and `&mut other` + // cannot alias, so they are distinct maps, as `merge` requires. + unsafe { + cpp!([ + map as "std::multimap*", + other_map as "std::multimap*" + ] { + map->merge(*other_map); + }) + } + } } // We ignore constructors that only differ by an allocator argument, because they're not interesting overloads to test. @@ -298,12 +412,20 @@ overload! { StdMultimap::default() } - /// Copy-construct a new multimap from a shared multimap reference. - pub fn new(other: &StdMultimap) -> StdMultimap { - StdMultimap::from_copy(other) - } - - /// Move-construct a new multimap from a mutable multimap reference. + /// Move-construct - a multimap from a mutable multimap reference. + /// + /// The contents of `other` are moved into the new map, leaving `other` in a valid + /// but unspecified state. + /// + /// ### Limitations + /// + /// C++ also has a copy constructor, `multimap(const multimap&)`, which differs only + /// by `const&` vs `&&`, so only the move form is wrapped. + /// + /// ### Ergonomics + /// + /// Rust moves by value, so taking `&mut` and leaving the source usable is unusual + /// and may surprise callers. pub fn new(other: &mut StdMultimap) -> StdMultimap { StdMultimap::move_from(other) } @@ -372,7 +494,7 @@ overload! { /// ### Limitations /// /// C++ `insert(value)` returns an iterator to the inserted element. This wrapper - /// returns nothing, because there is no iterator wrapper type yet. Callers can use + /// returns nothing, because the wrapper does not return iterators. Callers can use /// `count_key` to check the result instead. /// /// C++ also takes a single `std::pair`, but Rust tuples are not @@ -425,10 +547,85 @@ overload! { } } +overload! { + impl StdMultimap { + /// Erase every element whose key equals `key`. + /// Returns the number of elements removed. + /// + /// Unlike a normal map, a multimap can hold several elements per key, so + /// removes all of them, not just one. + /// + /// ### Ergonomics + /// + /// `erase(key)` and `erase(first_key, last_key)` differ only in argument count, + /// so it is easy to confuse a single key with the start of a range. + pub fn erase(&mut self, key: K) -> c_size_t { + self.erase_key_internal(key) + } + + /// Erase every element with a key in `[first_key, last_key)`. + /// Returns the number of elements removed. + /// + /// ### Limitations + /// + /// C++ takes two iterators and returns an iterator to the element after the + /// erased range. This wrapper takes two keys, finds the iterators with + /// `lower_bound`, and returns the number of elements removed instead. + pub fn erase(&mut self, first_key: K, last_key: K) -> c_size_t { + self.erase_range_internal(first_key, last_key) + } + + /// Erase the element at `pos`. + /// + /// Unlike `erase(key)`, this removes exactly one element, even if the key has + /// duplicates. + /// + /// ### Limitations + /// + /// C++ also has `erase(iterator)`, which differs only by iterator constness, so + /// only `erase(const_iterator)` is wrapped. + /// + /// The iterator is not tied to the map's lifetime, so using `pos` after the map + /// is modified is undefined behaviour. + /// + /// ### Ergonomics + /// + /// This does not clash with `erase(key: K)`, because `&StdMultimapIter` is a + /// different type from `K`. + pub fn erase(&mut self, pos: &StdMultimapIter) { + self.erase_pos_internal(pos) + } + } +} + +overload! { + impl StdMultimap { + /// Move all elements from `other` into this multimap. + /// + /// Elements are moved, not copied. A multimap accepts duplicate keys, so every + /// element moves and `other` is empty afterwards. + /// + /// ### Limitations + /// + /// C++ is a template over the source's comparator type `C2`. The wrapper is fixed + /// to `std::multimap`, so only the same comparator is supported. + /// + /// ### Ergonomics + /// + /// Merging a map into itself is impossible, because `&mut self` and `&mut other` + /// cannot alias. + pub fn merge(&mut self, other: &mut StdMultimap) { + self.merge_internal(other) + } + } +} + // The C++ multimap is deleted when the Rust wrapper is dropped. impl Drop for StdMultimap { fn drop(&mut self) { let map = self.0; + // SAFETY: `map` was created by `new` in this wrapper, is deleted only here, and + // `self` is being dropped, so it cannot be used afterwards unsafe { cpp!([map as "std::multimap*"] { @@ -438,7 +635,7 @@ impl Drop for StdMultimap { } } -/// These tests show that the correct Rust and C++ overloads are being called. +// These tests show that the correct Rust and C++ overloads are being called. #[test] fn test_multimap() { let default_map = StdMultimap::new(); @@ -468,22 +665,6 @@ fn test_multimap_initializer_list_three_pairs() { assert_eq!(map.count_key(2), 1); } -#[test] -fn test_multimap_copy_constructor() { - let entries: &[(K, V)] = &[(1, 10), (1, 20)]; - let original = StdMultimap::new(entries); - let copied = StdMultimap::new(&original); - - assert_eq!(copied.len(), 2); - assert_eq!(copied.count_key(1), 2); - - let mut original = original; - original.insert(2, 30); - - assert_eq!(original.len(), 3); - assert_eq!(copied.len(), 2); -} - #[test] fn test_multimap_move_constructor() { let mut original = StdMultimap::new((1, 10), (1, 20)); @@ -573,3 +754,71 @@ fn test_multimap_insert_empty_slice() { assert_eq!(map.len(), 1); assert_eq!(map.count_key(1), 1); } + +#[test] +fn test_multimap_erase_key() { + let mut map = StdMultimap::new((1, 10), (1, 20), (2, 30)); + + assert_eq!(map.erase(1), 2); + assert_eq!(map.len(), 1); + assert_eq!(map.count_key(1), 0); + assert_eq!(map.count_key(2), 1); +} + +#[test] +fn test_multimap_erase_key_range() { + let entries: &[(K, V)] = &[(1, 10), (1, 11), (2, 20), (3, 30), (4, 40)]; + let mut map = StdMultimap::new(entries); + + // Removes keys 1 and 2, keeps 3 and 4 (last key is excluded). + assert_eq!(map.erase(1, 3), 3); + assert_eq!(map.len(), 2); + assert_eq!(map.count_key(3), 1); + assert_eq!(map.count_key(4), 1); +} + +#[test] +fn test_multimap_erase_empty_or_reversed_range() { + let mut map = StdMultimap::new((1, 10), (2, 20)); + + assert_eq!(map.erase(2, 2), 0); + assert_eq!(map.erase(3, 1), 0); + assert_eq!(map.len(), 2); +} + +#[test] +fn test_multimap_erase_iterator() { + let mut map = StdMultimap::new((1, 10), (1, 20), (2, 30)); + + let pos = map.lower_bound(1); + map.erase(&pos); + + // Unlike `erase(key)`, this removes only one of the duplicates. + assert_eq!(map.len(), 2); + assert_eq!(map.count_key(1), 1); + assert_eq!(map.count_key(2), 1); +} + +#[test] +fn test_multimap_erase_end_iterator() { + let mut map = StdMultimap::new((1, 10), (2, 20)); + + let end = map.lower_bound(99); // past every key, so this is `end()` + map.erase(&end); + + assert_eq!(map.len(), 2); +} + +#[test] +fn test_multimap_merge() { + let mut a = StdMultimap::new((1, 10), (2, 20)); + let mut b = StdMultimap::new((1, 11), (3, 30)); + + a.merge(&mut b); + + assert_eq!(a.len(), 4); + assert_eq!(a.count_key(1), 2); + assert_eq!(a.count_key(2), 1); + assert_eq!(a.count_key(3), 1); + assert!(b.is_empty()); +} From 0e67b4bef247200e82d805cfc205278a1b83a8f6 Mon Sep 17 00:00:00 2001 From: Janeirungu01 Date: Tue, 3 Nov 2026 12:21:41 +0300 Subject: [PATCH 4/5] add multimap failing overloads --- cpp-overload-test/examples/stdcpp-multimap.rs | 109 ++++++++++++++++++ .../examples/stdcpp-multimap.stderr | 31 +++++ cpp-overloads-in-rust.md | 7 +- 3 files changed, 146 insertions(+), 1 deletion(-) create mode 100644 cpp-overload-test/examples/stdcpp-multimap.rs create mode 100644 cpp-overload-test/examples/stdcpp-multimap.stderr diff --git a/cpp-overload-test/examples/stdcpp-multimap.rs b/cpp-overload-test/examples/stdcpp-multimap.rs new file mode 100644 index 0000000..084ac3b --- /dev/null +++ b/cpp-overload-test/examples/stdcpp-multimap.rs @@ -0,0 +1,109 @@ +//! Test C++ [std::multimap][multimap] overloads that can't be represented in Rust, +//! and record the macro diagnostics. +//! Error output is in [stdcpp-multimap.stderr]. +//! Working overloads are in [multimap.rs]. +//! +//! [multimap]: https://en.cppreference.com/w/cpp/container/multimap +//! [stdcpp-multimap.stderr]: https://github.com/rustfoundation/overloading-macros/blob/main/cpp-overload-test/examples/stdcpp-multimap.stderr +//! [multimap.rs]: https://github.com/rustfoundation/overloading-macros/blob/main/cpp-overload-test/src/stdcpp/multimap.rs +//! +//! The `cpp!` macro doesn't work here, because of technical limitations it can only run one build +//! per crate. (Examples and binaries are built separately from `lib.rs`.) +//! To add C++ code here, use /* ... */ code comment blocks, or just skip it. + +#![feature(splat, tuple_trait, c_size_t)] +#![allow(incomplete_features)] +#![allow(unused_braces)] + +use splat_overload::overload; +use std::ffi::{c_int, c_size_t}; + +/// Workaround for missing generic support in the `overload!` macro. +type K = c_int; + +/// Workaround for missing generic support in the `overload!` macro. +type V = c_int; + +/// A fake multimap wrapper used to test overload resolution. +/// The C++ container is represented by a Rust vector for type checking. +#[expect(dead_code, reason = "Incompatible overloads can't be used")] +struct StdMultimapA(Vec<(K, V)>); + +// Accessors: `find(...)` +overload! { + impl StdMultimapA { + /// Find an element with the key, from a shared reference. + /// + /// ### Incompatibilities + /// + /// The `overload!` macro can't overload on `&self` vs `&mut self`, + /// because all overloads must have the same receiver kind. + /// + /// This also affects other const and non-const accessors, such as + /// `lower_bound`, `upper_bound`, `equal_range`, `begin` and `end`. + fn find(&self, _key: K) -> Option<&(K, V)> { + todo!() + } + + /// Find an element with the key, from a mutable reference. + fn find(&mut self, _key: K) -> Option<&mut (K, V)> { + todo!() + } + } +} + +/// A wrapper struct to demonstrate key and iterator-position collisions. +#[expect(dead_code, reason = "Incompatible overloads can't be used")] +struct StdMultimapB(Vec<(K, V)>); +struct StdMultimapIter; + +/// Workaround for missing generic support in the `overload!` macro. +/// Models a C++ iterator as an integer position. +type Pos = K; + +// Modifiers: `erase(...)` +overload! { + impl StdMultimapB { + /// Erase every element whose key equals `key`. + fn erase(&mut self, _key: K) -> c_size_t { + todo!() + } + + /// Erase the element at `pos`. + /// + /// ### Incompatibilities + /// + /// This overload has the same types as `erase(key)` when the iterator + /// is modelled as an integer, but different semantics: + /// - `erase(key)` removes every element with that key. + /// - `erase(pos)` removes one element. + fn erase(&mut self, _pos: Pos) -> StdMultimapIter { + todo!() + } + } +} + +/// A wrapper struct to demonstrate the workaround for different receiver kinds. +struct StdMultimapC(Vec<(K, V)>); + +// Workaround: associated functions with an explicit `this` argument. +overload! { + impl StdMultimapC { + /// Find an element with the key, from a shared reference. + /// + /// ### Workaround + /// + /// Uses an associated function instead of a method, with `&'static` + /// to avoid generic lifetimes. This is less ergonomic than a normal method. + fn find(_this: &'static StdMultimapC, _key: K) -> Option<&'static (K, V)> { + todo!() + } + + /// Find an element with the key, from a mutable reference. + fn find(_this: &'static mut StdMultimapC, _key: K) -> Option<&'static mut (K, V)> { + todo!() + } + } +} + +pub fn main() {} diff --git a/cpp-overload-test/examples/stdcpp-multimap.stderr b/cpp-overload-test/examples/stdcpp-multimap.stderr new file mode 100644 index 0000000..92303ec --- /dev/null +++ b/cpp-overload-test/examples/stdcpp-multimap.stderr @@ -0,0 +1,31 @@ +error: proc macro panicked + --> cpp-overload-test/examples/stdcpp-multimap.rs:33:1 + | +33 | / overload! { +34 | | impl StdMultimapA { +35 | | /// Find an element with the key, from a shared reference. +36 | | /// +... | +53 | | } + | |_^ + | + = help: message: all overloads must have the same receiver kind (&self, &mut self, self, or no receiver), got find: &self and find: &mut self + +error[E0119]: conflicting implementations of trait `EraseArgs` for type `(i32,)` + --> cpp-overload-test/examples/stdcpp-multimap.rs:65:1 + | +65 | / overload! { +66 | | impl StdMultimapB { +67 | | /// Erase every element whose key equals `key`. +68 | | fn erase(&mut self, _key: K) -> c_size_t { +... | +84 | | } + | | ^ + | | | + | |_first implementation here + | conflicting implementation for `(i32,)` + | + = note: this error originates in the macro `overload` (in Nightly builds, run with -Z macro-backtrace for more info) + +For more information about this error, try `rustc --explain E0119`. +error: could not compile `cpp-overload-test` (example "stdcpp-multimap") due to 2 previous errors diff --git a/cpp-overloads-in-rust.md b/cpp-overloads-in-rust.md index dd22d16..822bdc9 100644 --- a/cpp-overloads-in-rust.md +++ b/cpp-overloads-in-rust.md @@ -27,7 +27,8 @@ See the Limitations section below the table for more details. | TODO | ? | ? | ? | ? | | *Template Classes* | Constructors | Accessors | Modifiers | Transforms | | [forward_list] | ✅ | 🌗⚙️[^1] | 🌗⚙️?[^2] | ✅ | -| TODO | ? | ? | ? | ? | +| [multimap] | ✅ | 🌗⚙️?[^3] | 🌗?[^4] | | + Constructors: Create an instance of the class, called as `Class::Class(...)`
Accessors: Get information about the class, called as `instance->get(...)`
@@ -35,9 +36,13 @@ Modifiers: Add or change the items in a list class, called as `instance->put(... 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 +[multimap]: https://github.com/rustfoundation/overloading-macros/blob/main/cpp-overload-test/src/stdcpp/multimap.rs [^1]: Rust and C++ iterators have different semantics, using adapter types is an ergonomic workaround. [^2]: `insert_after` and `splice_after` still need to be analysed. +[^3]: `lower_bound`, `upper_bound`, `equal_range` and `at` still need to be analysed. +[^4]: `erase(key)` and `erase(pos)` conflict unless the iterator is a distinct type. `emplace` and `extract` still need to be analysed. + ## Limitations From 27385dd1e006393df0589660e851837ec582fd84 Mon Sep 17 00:00:00 2001 From: teor Date: Fri, 6 Nov 2026 19:33:08 +1000 Subject: [PATCH 5/5] Fix broken git merge deleting table entries Co-authored-by: teor --- cpp-overloads-in-rust.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cpp-overloads-in-rust.md b/cpp-overloads-in-rust.md index 730f664..f8a355a 100644 --- a/cpp-overloads-in-rust.md +++ b/cpp-overloads-in-rust.md @@ -29,6 +29,7 @@ See the Limitations section below the table for more details. | [forward_list] | ✅ | 🌗⚙️[^1] | 🌗⚙️?[^2] | ✅ | | [hash] | ✅ | ✅ | | | | [multimap] | ✅ | 🌗⚙️?[^ma] | 🌗?[^mm] | | +| [unordered_map] | ✅ | 🌗⚙️[^5] | 🌗⚙️ | | | [unordered_multiset] | ✅ | ✅⚙️[^3] | ✅ | ?[^4] | | TODO | ? | ? | ? | ? | @@ -40,6 +41,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 [hash]: https://github.com/rustfoundation/overloading-macros/blob/main/cpp-overload-test/src/stdcpp/hash.rs [multimap]: https://github.com/rustfoundation/overloading-macros/blob/main/cpp-overload-test/src/stdcpp/multimap.rs +[unordered_map]: https://github.com/rustfoundation/overloading-macros/blob/main/cpp-overload-test/src/stdcpp/unordered_map.rs [unordered_multiset]: https://github.com/rustfoundation/overloading-macros/blob/main/cpp-overload-test/src/stdcpp/unordered_multiset.rs [^1]: Rust and C++ iterators have different semantics, using adapter types is an ergonomic workaround.