|
| 1 | +//! Test that C++ overloads for [std::unordered_multiset][unordered_multiset] can be successfully represented in Rust. |
| 2 | +//! |
| 3 | +//! [unordered_multiset]: https://en.cppreference.com/cpp/container/unordered_multiset |
| 4 | +
|
| 5 | +#![allow(unused_braces, reason = "Generated by overload! macro")] |
| 6 | + |
| 7 | +use cpp::cpp; |
| 8 | +use splat_overload::overload; |
| 9 | +use std::ffi::{c_char, c_size_t, c_void}; |
| 10 | +use std::marker::PhantomData; |
| 11 | + |
| 12 | +// C++ header includes |
| 13 | +cpp! {{ |
| 14 | + #include <unordered_set> |
| 15 | + #include <vector> |
| 16 | + #include <cstddef> |
| 17 | +}} |
| 18 | + |
| 19 | +/// A wrapper struct to hold the returned C++ `std::unordered_multiset<int>` pointer. |
| 20 | +/// |
| 21 | +/// ### Limitations |
| 22 | +/// |
| 23 | +/// This struct and the impl should be fully generic over the set type. |
| 24 | +/// FIXME: make the `overload!` macro support generics. |
| 25 | +#[cfg_attr(not(test), expect(dead_code, reason = "Only used in tests"))] |
| 26 | +pub struct StdUnorderedMultiset(*mut c_void, PhantomData<T>); |
| 27 | + |
| 28 | +/// Workaround for missing generic support in the `overload!` macro. |
| 29 | +/// |
| 30 | +/// The `overload!` macro does not support generics, so the implementation is hardcoded to `int` type. |
| 31 | +pub type T = std::ffi::c_int; |
| 32 | + |
| 33 | +#[cfg_attr(not(test), expect(dead_code, reason = "Only used in tests"))] |
| 34 | +#[allow( |
| 35 | + non_upper_case_globals, |
| 36 | + reason = "Triggered by cpp! macro in rust-analyzer" |
| 37 | +)] |
| 38 | +impl StdUnorderedMultiset { |
| 39 | + fn default() -> StdUnorderedMultiset { |
| 40 | + let set = unsafe { |
| 41 | + cpp!([] -> *mut c_void as "std::unordered_multiset<int>*" { |
| 42 | + return new std::unordered_multiset<int>(); |
| 43 | + }) |
| 44 | + }; |
| 45 | + StdUnorderedMultiset(set, PhantomData) |
| 46 | + } |
| 47 | + |
| 48 | + fn from_bucket_count(bucket_count: c_size_t) -> StdUnorderedMultiset { |
| 49 | + let set = unsafe { |
| 50 | + cpp!([bucket_count as "size_t"] -> *mut c_void as "std::unordered_multiset<int>*" { |
| 51 | + return new std::unordered_multiset<int>(bucket_count); |
| 52 | + }) |
| 53 | + }; |
| 54 | + StdUnorderedMultiset(set, PhantomData) |
| 55 | + } |
| 56 | + |
| 57 | + fn copy_from(other: &StdUnorderedMultiset) -> StdUnorderedMultiset { |
| 58 | + let other_ptr = other.0 as *const c_void; |
| 59 | + let set = unsafe { |
| 60 | + cpp!([other_ptr as "const std::unordered_multiset<int>*"] -> *mut c_void as "std::unordered_multiset<int>*" { |
| 61 | + return new std::unordered_multiset<int>(*other_ptr); |
| 62 | + }) |
| 63 | + }; |
| 64 | + StdUnorderedMultiset(set, PhantomData) |
| 65 | + } |
| 66 | + |
| 67 | + fn from_slice(items: &[T]) -> StdUnorderedMultiset { |
| 68 | + let len = items.len(); |
| 69 | + let items: *const T = items.as_ptr(); |
| 70 | + let set = unsafe { |
| 71 | + cpp!([items as "const int*", len as "size_t"] -> *mut c_void as "std::unordered_multiset<int>*" { |
| 72 | + return new std::unordered_multiset<int>(items, items + len); |
| 73 | + }) |
| 74 | + }; |
| 75 | + StdUnorderedMultiset(set, PhantomData) |
| 76 | + } |
| 77 | + |
| 78 | + fn from_initializer_list_2(a: T, b: T) -> StdUnorderedMultiset { |
| 79 | + let set = unsafe { |
| 80 | + cpp!([a as "int", b as "int"] -> *mut c_void as "std::unordered_multiset<int>*" { |
| 81 | + return new std::unordered_multiset<int>{ a, b }; |
| 82 | + }) |
| 83 | + }; |
| 84 | + StdUnorderedMultiset(set, PhantomData) |
| 85 | + } |
| 86 | + |
| 87 | + fn insert_value(&mut self, value: T) { |
| 88 | + let slf: *mut c_void = self.0; |
| 89 | + unsafe { |
| 90 | + cpp!([slf as "std::unordered_multiset<int>*", value as "int"] { |
| 91 | + slf->insert(value); |
| 92 | + }) |
| 93 | + } |
| 94 | + } |
| 95 | + |
| 96 | + fn insert_range(&mut self, items: &[T]) { |
| 97 | + let slf: *mut c_void = self.0; |
| 98 | + let len = items.len(); |
| 99 | + let items: *const T = items.as_ptr(); |
| 100 | + unsafe { |
| 101 | + cpp!([slf as "std::unordered_multiset<int>*", items as "const int*", len as "size_t"] { |
| 102 | + slf->insert(items, items + len); |
| 103 | + }) |
| 104 | + } |
| 105 | + } |
| 106 | + |
| 107 | + fn insert_initializer_list_2(&mut self, a: T, b: T) { |
| 108 | + let slf: *mut c_void = self.0; |
| 109 | + unsafe { |
| 110 | + cpp!([slf as "std::unordered_multiset<int>*", a as "int", b as "int"] { |
| 111 | + slf->insert({ a, b }); |
| 112 | + }) |
| 113 | + } |
| 114 | + } |
| 115 | + |
| 116 | + fn insert_with_hint(&mut self, hint: c_size_t, value: T) { |
| 117 | + let slf: *mut c_void = self.0; |
| 118 | + unsafe { |
| 119 | + cpp!([slf as "std::unordered_multiset<int>*", hint as "size_t", value as "int"] { |
| 120 | + auto it = slf->begin(); |
| 121 | + std::advance(it, hint); |
| 122 | + slf->insert(it, value); |
| 123 | + }) |
| 124 | + } |
| 125 | + } |
| 126 | + |
| 127 | + fn count(&self, value: T) -> c_size_t { |
| 128 | + let slf = self.0 as *const c_void; |
| 129 | + unsafe { |
| 130 | + cpp!([slf as "const std::unordered_multiset<int>*", value as "int"] -> c_size_t as "size_t" { |
| 131 | + return slf->count(value); |
| 132 | + }) |
| 133 | + } |
| 134 | + } |
| 135 | + |
| 136 | + fn is_empty(&self) -> bool { |
| 137 | + let slf = self.0 as *const c_void; |
| 138 | + let empty = unsafe { |
| 139 | + cpp!([slf as "const std::unordered_multiset<int>*"] -> c_char as "char" { |
| 140 | + return slf->empty(); |
| 141 | + }) |
| 142 | + }; |
| 143 | + empty != 0 |
| 144 | + } |
| 145 | +} |
| 146 | + |
| 147 | +overload! { |
| 148 | + impl StdUnorderedMultiset { |
| 149 | + /// Construct an empty set. |
| 150 | + /// |
| 151 | + /// ### Limitations |
| 152 | + /// |
| 153 | + /// The `overload!` macro does not support generics, so the implementation is hardcoded to `int` type. |
| 154 | + pub fn new() -> StdUnorderedMultiset { |
| 155 | + StdUnorderedMultiset::default() |
| 156 | + } |
| 157 | + |
| 158 | + /// Construct a set with at least `bucket_count` buckets. |
| 159 | + /// |
| 160 | + /// ### Limitations |
| 161 | + /// |
| 162 | + /// Rust doesn't have default function arguments. In C++, this constructor accepts |
| 163 | + /// optional hash and key_equal arguments with default values. In Rust, each argument |
| 164 | + /// combination would require a separate overload, which becomes unwieldy. |
| 165 | + pub fn new(bucket_count: c_size_t) -> StdUnorderedMultiset { |
| 166 | + StdUnorderedMultiset::from_bucket_count(bucket_count) |
| 167 | + } |
| 168 | + |
| 169 | + /// Construct a set with at least `bucket_count` buckets and a custom hash function. |
| 170 | + /// |
| 171 | + /// FIXME: This overload can be implemented using the same pattern as BinaryPredicateRust |
| 172 | + /// in forward_list. The C++ function type needs to be declared in a cpp! block. |
| 173 | + pub fn new(_bucket_count: c_size_t, _hash_fn: extern "C" fn(&T) -> c_size_t) -> StdUnorderedMultiset { |
| 174 | + todo!() |
| 175 | + } |
| 176 | + |
| 177 | + /// Construct a set with at least `bucket_count` buckets and a custom key equality function. |
| 178 | + /// |
| 179 | + /// FIXME: This overload can be implemented using the same pattern as BinaryPredicateRust |
| 180 | + /// in forward_list. The C++ function type needs to be declared in a cpp! block. |
| 181 | + pub fn new(_bucket_count: c_size_t, _key_equal: extern "C" fn(&T, &T) -> bool) -> StdUnorderedMultiset { |
| 182 | + todo!() |
| 183 | + } |
| 184 | + |
| 185 | + /// Construct a set by copying from another set. |
| 186 | + pub fn new(other: &StdUnorderedMultiset) -> StdUnorderedMultiset { |
| 187 | + StdUnorderedMultiset::copy_from(other) |
| 188 | + } |
| 189 | + |
| 190 | + /// Construct a set by copying items from a slice. |
| 191 | + /// |
| 192 | + /// ### Ergonomics |
| 193 | + /// |
| 194 | + /// The `as_slice()` call is required to match the slice overload, as arrays don't automatically coerce. |
| 195 | + pub fn new(items: &[T]) -> StdUnorderedMultiset { |
| 196 | + StdUnorderedMultiset::from_slice(items) |
| 197 | + } |
| 198 | + |
| 199 | + /// Construct a set from an initializer list. |
| 200 | + /// |
| 201 | + /// ### Ergonomics |
| 202 | + /// |
| 203 | + /// Initializer list overloads require separate functions for each argument count, which is not ergonomic. |
| 204 | + pub fn new(a: T, b: T) -> StdUnorderedMultiset { |
| 205 | + StdUnorderedMultiset::from_initializer_list_2(a, b) |
| 206 | + } |
| 207 | + } |
| 208 | +} |
| 209 | + |
| 210 | +overload! { |
| 211 | + impl StdUnorderedMultiset { |
| 212 | + /// Insert a single value into the set. |
| 213 | + pub fn insert(&mut self, value: T) { |
| 214 | + self.insert_value(value) |
| 215 | + } |
| 216 | + |
| 217 | + /// Insert a range of values from a slice into the set. |
| 218 | + /// |
| 219 | + /// ### Ergonomics |
| 220 | + /// |
| 221 | + /// The `as_slice()` call is required to match the slice overload, as arrays don't automatically coerce. |
| 222 | + pub fn insert(&mut self, items: &[T]) { |
| 223 | + self.insert_range(items) |
| 224 | + } |
| 225 | + |
| 226 | + /// Insert values from an initializer list. |
| 227 | + /// |
| 228 | + /// ### Ergonomics |
| 229 | + /// |
| 230 | + /// Initializer list overloads require separate functions for each argument count, which is not ergonomic. |
| 231 | + pub fn insert(&mut self, a: T, b: T) { |
| 232 | + self.insert_initializer_list_2(a, b) |
| 233 | + } |
| 234 | + |
| 235 | + /// Insert a value with a position hint. |
| 236 | + pub fn insert(&mut self, hint: c_size_t, value: T) { |
| 237 | + self.insert_with_hint(hint, value) |
| 238 | + } |
| 239 | + } |
| 240 | +} |
| 241 | + |
| 242 | +#[test] |
| 243 | +pub fn test_unordered_multiset() { |
| 244 | + let default_set = StdUnorderedMultiset::new(); |
| 245 | + assert!(default_set.is_empty()); |
| 246 | + |
| 247 | + let from_slice = StdUnorderedMultiset::new([1, 2, 3].as_slice()); |
| 248 | + assert_eq!(from_slice.count(1), 1); |
| 249 | + |
| 250 | + let from_bucket_count = StdUnorderedMultiset::new(10 as c_size_t); |
| 251 | + assert!(from_bucket_count.is_empty()); |
| 252 | + |
| 253 | + let from_initializer_list = StdUnorderedMultiset::new(2, 1); |
| 254 | + assert_eq!(from_initializer_list.count(2), 1); |
| 255 | + |
| 256 | + let empty_set = StdUnorderedMultiset::new(); |
| 257 | + let copied = StdUnorderedMultiset::new(&empty_set); |
| 258 | + assert!(copied.is_empty()); |
| 259 | + |
| 260 | + let mut insert_set = StdUnorderedMultiset::new(); |
| 261 | + insert_set.insert(42); |
| 262 | + assert_eq!(insert_set.count(42), 1); |
| 263 | + |
| 264 | + let mut insert_set = StdUnorderedMultiset::new(); |
| 265 | + insert_set.insert([1, 2, 3].as_slice()); |
| 266 | + assert_eq!(insert_set.count(1), 1); |
| 267 | + |
| 268 | + let mut insert_init = StdUnorderedMultiset::new(); |
| 269 | + insert_init.insert(5, 10); |
| 270 | + assert_eq!(insert_init.count(5), 1); |
| 271 | + |
| 272 | + let mut insert_hint = StdUnorderedMultiset::new(); |
| 273 | + insert_hint.insert(0 as c_size_t, 7); |
| 274 | + assert_eq!(insert_hint.count(7), 1); |
| 275 | +} |
0 commit comments