Skip to content

Add unordered_multiset wrapper - #51

Open
Lynndabel wants to merge 9 commits into
rustfoundation:mainfrom
Lynndabel:unordered_multiset
Open

Lynndabel wants to merge 9 commits into
rustfoundation:mainfrom
Lynndabel:unordered_multiset

Conversation

@Lynndabel

@Lynndabel Lynndabel commented Oct 14, 2026 •

Copy link
Copy Markdown

Added a wrapper for std::unordered_multiset with 5 overloads (constructors and insert).

  1. Write Rust wrappers for that C++ type, for 3-5 different overloaded method names:
    • Create a new file named after your type in overloading-macros/cpp-overload-test/src/stdcpp
    • Wrap constructors first, writing tests to make sure the correct constructor is called
    • Explain any potential compatibility issues in a Limitations doc section
    • Explain usability issues in an Ergonomics doc section
    • Check that you have implemented all the overloads for each method name, or documented why you can't
    • Push your changes and wait for a review
  2. Add a separate example with some failing overloads
    • Create a new file named after your type in overloading-macros/cpp-overload-test/examples
    • Wrap accessor/modifier methods
    • Explain how they fail in an Incompatibilities doc section
    • Explain how the overload failures could be resolved in a Workarounds doc section
    • Optional: If there's a C++ feature with no equivalent in Rust, explain that in an Inexpressible Overloads doc section
    • Push your changes and wait for a review
  3. Update the overload macro compatibility table Markdown doc
    • If you haven't checked some kinds of overloads, put a ?in those squares
    • Push your changes and wait for a review

Mark items in this checklist as done by clicking the box next to each item.

Focus on Interesting Overloads

Only wrap functions that have overloads. (It is ok to wrap non-overloaded functions just for testing.)

Ignore these overloads:

  • overloaded operators: they are already possible in Rust
  • overloads with an allocator argument, or that only differ by noexcept: not an interesting difference
  • assignment functions, assignment operators, and swap: no equivalent in Rust (which uses memcpy for assignment/swaps)

Only code one example of overloads that:

  • only differ by const/non-const, &/&& (or &/&mut)
  • are iterator methods that only differ by position (before_begin/begin,end)

You can look at stdcpp/forward_list.rs (working overloads) and stdcpp-forward-list.rs (failing overloads) for example code.

Closes #41

@Lynndabel Lynndabel changed the title Unordered multiset Add unordered_multiset wrapper Oct 14, 2026

@teor2345 teor2345 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Today I updated the PR template to make it clear that all overloads need to be covered, so I updated this PR's description to match it. I also fixed the GitHub checklist formatting.

For this class constructor, there are many overloads, so we can just wrap the ones that are more likely to conflict.

No need to add any more overloaded methods, the constructor and the insert method have enough overloads.

StdUnorderedMultiset::from_slice(items)
}

pub fn new(a: T, b: T) -> StdUnorderedMultiset {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To check that an overload set works in Rust, we need to wrap all of the C++ constructor overloads.

There are 14 constructor overloads in C++, excluding overloads that only differ by an allocator argument:
https://en.cppreference.com/cpp/container/unordered_multiset/unordered_multiset

That's a lot of overloads, so you don't have to implement them all. Instead, pick overloads that are very different from each other. For example, you could add one overload starting with these arguments:

  • bucket_count
  • unordered_multiset
  • from_range_t

It would also be interesting to use C++ default arguments to call an overload with just one argument of type:

  • hash
  • key_equal

This would be enough to cover the constructor overload set reasonably well.

Please document the lack of default argument values in Rust as a limitation.

Comment thread cpp-overload-test/src/stdcpp/unordered_multiset.rs Outdated
overload! {
impl StdUnorderedMultiset {
/// Insert a single value into the set.
pub fn insert(&mut self, value: T) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's a lot of insert overloads as well, feel free to update this PR in stages, and ask for a review after every stage.

@Lynndabel

This comment was marked as resolved.

@Lynndabel

Copy link
Copy Markdown
Author

@teor2345 I didnnt add the hash and key_equal constructor overloads because they would require tag types to disambiguate in Rust. Should I add them using the approach you mentioned or is the current set of constructors sufficient? Thank you.

@teor2345

Copy link
Copy Markdown
Collaborator

@teor2345 I didnnt add the hash and key_equal constructor overloads because they would require tag types to disambiguate in Rust. Should I add them using the approach you mentioned or is the current set of constructors sufficient? Thank you.

If you think the full set of overloaded arguments will fail, then they can wait until step 2 (an example with some failing overloads). We are currently at step 1 in this PR (working overloads).

I think it would be useful to show at least one working example with C++ optional/default argument values.

Since Rust doesn't have optional/default argument values, please write that up as a limitation, and explain what it would take to represent all the different combinations of optional/default argument values in Rust.

@Lynndabel

Copy link
Copy Markdown
Author

@teor2345 I didnnt add the hash and key_equal constructor overloads because they would require tag types to disambiguate in Rust. Should I add them using the approach you mentioned or is the current set of constructors sufficient? Thank you.

If you think the full set of overloaded arguments will fail, then they can wait until step 2 (an example with some failing overloads). We are currently at step 1 in this PR (working overloads).

I think it would be useful to show at least one working example with C++ optional/default argument values.

Since Rust doesn't have optional/default argument values, please write that up as a limitation, and explain what it would take to represent all the different combinations of optional/default argument values in Rust.

I tried to add the hash/key_equal overloads, but the cpp! macro doesn't work inside the overload! macro body, so I couldn't implement them. I've documented the default arguments limitation in detail instead as you requested.

@teor2345

Copy link
Copy Markdown
Collaborator

I think it would be useful to show at least one working example with C++ optional/default argument values.
Since Rust doesn't have optional/default argument values, please write that up as a limitation, and explain what it would take to represent all the different combinations of optional/default argument values in Rust.

I tried to add the hash/key_equal overloads, but the cpp! macro doesn't work inside the overload! macro body, so I couldn't implement them. I've documented the default arguments limitation in detail instead as you requested.

Please use the same workaround you used for constructors, call a method with a unique name that contains the cpp! macro:
https://github.com/Lynndabel/overloading-macros/blob/ff27429e55482c9b97e9e6899a982b66e93f08f1/cpp-overload-test/src/stdcpp/unordered_multiset.rs#L149

@teor2345

Copy link
Copy Markdown
Collaborator

Ah, I see you are specifically concerned about function types. Here is an example of passing a function type:

// The `cpp!` macro can't handle C/C++ function type syntax, so we have to declare function
// types here.
typedef bool BinaryPredicateCpp(const int&, const int&);
}}
/// An ABI-compatible Rust function type for `BinaryPredicateCpp`.
pub type BinaryPredicateRust = extern "C" fn(&T, &T) -> bool;

fn unique_with(&mut self, eq_predicate: BinaryPredicateRust) -> 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 "BinaryPredicateCpp*"] -> c_size_t as "size_t" {
return slf->unique(eq_predicate);

pub fn unique(&mut self, eq_predicate: BinaryPredicateRust) -> c_size_t {
self.unique_with(eq_predicate)

@Lynndabel

Copy link
Copy Markdown
Author

I think it would be useful to show at least one working example with C++ optional/default argument values.
Since Rust doesn't have optional/default argument values, please write that up as a limitation, and explain what it would take to represent all the different combinations of optional/default argument values in Rust.

I tried to add the hash/key_equal overloads, but the cpp! macro doesn't work inside the overload! macro body, so I couldn't implement them. I've documented the default arguments limitation in detail instead as you requested.

Please use the same workaround you used for constructors, call a method with a unique name that contains the cpp! macro: https://github.com/Lynndabel/overloading-macros/blob/ff27429e55482c9b97e9e6899a982b66e93f08f1/cpp-overload-test/src/stdcpp/unordered_multiset.rs#L149

I have added the hash and key_equal constructor overloads using the helper method pattern you suggested. Please review and let me know if thereis anything else. Thank you.

@teor2345 teor2345 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The goal of wrapping an overload set is to expose different and interesting behaviours to Rust.

/// This demonstrates calling a C++ constructor with default arguments.
/// In C++, this would be `unordered_multiset(0, std::hash<int>(), std::equal_to<int>())`
/// where the remaining arguments use their default values.
pub fn new(_tag: DefaultKeyEqual) -> StdUnorderedMultiset {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The constructors you just added seem to do the same thing as the constructor with no arguments. If they have different behaviour, please add a test that shows the different behaviour. If they behave the same as another constructor, please remove them.

Have a look at my previous suggestion again. It can help you add overloads that allow the user to supply custom Rust functions as the hash and equal functions.

Feel free to remove the tag type, it shouldn't be needed. But if it helps you to get the code working, you can use it, and we can work on removing it later.

@Lynndabel

Copy link
Copy Markdown
Author

I tried adding overloads that accept custom Rust functions for hash and key_equal, but from my research, C++ templates are complex - std::unordered_multiset expects specific functor types like std::hash<int>, not function pointers or std::function. I've removed the overloads and kept the doc about the default arguments limitation.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

unordered_multiset

2 participants