-
Notifications
You must be signed in to change notification settings - Fork 745
Calling overloaded C++ operators from Rust #1904
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
Calling overloaded C++ operators from Rust #1904
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Exciting! I need to look a bit more in detail but here's some feedback from a quick look.
Thanks for working on this!
src/ir/context.rs
Outdated
("[]", "_index"), | ||
("!", "_not"), | ||
]; | ||
let mut invalid_name = false; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: let invalid_name = subs.iter().any(|sub| name.contains(sub.0))
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed in the next commit
src/codegen/mod.rs
Outdated
let trait_name = proc_macro2::TokenStream::from_str(el.1).unwrap(); | ||
let func_name = proc_macro2::TokenStream::from_str(el.2).unwrap(); | ||
result.push(quote!( | ||
impl std::ops::#trait_name<#rhs_type> for &#ty_for_impl { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This needs to preserve the trait_prefix, to handle use_core
correctly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed in the next commit
src/codegen/mod.rs
Outdated
None | ||
} else { | ||
dbg!("test"); | ||
let temp = utils::type_id_to_rust_type(ctx, signature.argument_types()[1].1); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can't we dig here if there is a pointer instead here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What do you mean with "digging"?
pub fn type_id_to_rust_type( | ||
ctx: &BindgenContext, | ||
ty: crate::ir::context::TypeId, | ||
) -> proc_macro2::TokenStream { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems it'd be doable here to add a flag to convert pointers to references, but maybe I'm missing something? Seems better than fixing up the token-stream afterwards.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Adding a flag to convert pointers to references would be a nice solution, but doing this is tricky, at least for me.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Something like this seems to be relatively straight-forward. Should apply on top of your patch.
diff --git a/src/codegen/mod.rs b/src/codegen/mod.rs
index 9eb69f01..00ee92df 100644
--- a/src/codegen/mod.rs
+++ b/src/codegen/mod.rs
@@ -2173,45 +2173,6 @@ impl CodeGenerator for CompInfo {
}
}
-/// This function takes a TokenStream, removes all "const" and replaces all "*"
-/// with "&".
-///
-/// This is a bit hacky, but we need it because argument_type_id_to_rust_type
-/// returns something like "const *MyClass" instead of "&MyClass" and the latter
-/// is what we need to write into bindgen.rs. The less hacky way would be to
-/// make argument_type_id_to_rust_type use "&" instead of "*" when assembling
-/// the TokenStream, but doing this is tricky.
-fn raw_pointer_to_reference(
- rust_source: proc_macro2::TokenStream,
-) -> proc_macro2::TokenStream {
- // Todo: Check if the performance of this function is terrible if rust_source is long.
- use proc_macro2::*;
- rust_source
- .into_iter()
- .filter(|elem| match &elem {
- TokenTree::Ident(ident) => {
- let comp = Ident::new("const", ident.span());
- if comp == ident.clone() {
- false
- } else {
- true
- }
- }
- _ => (true),
- })
- .map(|elem| match &elem {
- TokenTree::Punct(punct) => {
- if punct.as_char() == '*' {
- TokenTree::Punct(Punct::new('&', Spacing::Alone))
- } else {
- elem
- }
- }
- _ => (elem),
- })
- .collect()
-}
-
impl Method {
/// If self is an overloaded operator that this function recognizes, this
/// function will put the correct wrapper into result and return true. Note
@@ -2261,11 +2222,11 @@ impl Method {
let second_arg_type = if args.len() < 2 {
None
} else {
- let temp = utils::argument_type_id_to_rust_type(
+ Some(utils::argument_type_id_to_rust_type(
ctx,
signature.argument_types()[1].1,
- );
- Some(raw_pointer_to_reference(temp))
+ /* pointer_to_ref = */ true
+ ))
};
// We then check if the function name is in one of the following three
@@ -4617,6 +4578,7 @@ mod utils {
pub fn argument_type_id_to_rust_type(
ctx: &BindgenContext,
ty: crate::ir::context::TypeId,
+ pointer_to_ref: bool,
) -> proc_macro2::TokenStream {
use super::ToPtr;
@@ -4641,6 +4603,11 @@ mod utils {
stream.to_ptr(ctx.resolve_type(t).is_const())
}
TypeKind::Pointer(inner) => {
+ if pointer_to_ref {
+ let inner = inner.to_rust_ty_or_opaque(ctx, &());
+ return quote! { &#inner }
+ }
+
let inner = ctx.resolve_item(inner);
let inner_ty = inner.expect_type();
if let TypeKind::ObjCInterface(ref interface) =
@@ -4679,7 +4646,7 @@ mod utils {
assert!(!arg_name.is_empty());
let arg_name = ctx.rust_ident(arg_name);
- let arg_ty = argument_type_id_to_rust_type(ctx, ty);
+ let arg_ty = argument_type_id_to_rust_type(ctx, ty, /* pointer_to_ref = */ false);
quote! {
#arg_name : #arg_ty
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems like a nice solution. Should I commit this patch or do you want to do that?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Feel free to commit it yourself :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This patch is c9111e3
src/codegen/mod.rs
Outdated
@@ -4411,6 +4616,52 @@ mod utils { | |||
} | |||
} | |||
|
|||
pub fn type_id_to_rust_type( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this need to be public? Also, I think the name of this function should have something to do with arguments. The conversions done here are only valid for arguments.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If I remove the "pub" it does not compile because try_codegen_operator calls it.
I changed the name to argument_type_id_to_rust_type in the next commit.
src/codegen/mod.rs
Outdated
let retptr = unsafe { | ||
#function_name(self, rhs) | ||
}; | ||
assert_eq!(retptr, self as *mut #ty_for_impl, "The bindgen authors have no idea how to handle this case."); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think we necessarily care about asserting this here, do we? As in, the rust trait doesn't have any return value, it's fine to drop it on the floor.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Where C++ thinks the object lives and where Rust thinks the object lives have to be identical. Most C++ operators just return this; , but afaik this is not required by the standard.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right, my point is that where the object lives is not meaningful is it? Like, a C++ operator can return whatever it wants, but it can't destroy this
. So I still don't know why this assert is useful.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I removed this assertion in the newest commit. I hope you are right...
bintest/example.cpp
Outdated
@@ -0,0 +1,41 @@ | |||
#include <cstddef> | |||
class MyClass { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems this could reuse the bindgen-integration
test that is set up already, can't it?
I think that'd be preferable.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I did not know that bindgen-integrat existed. Fixed in the newest commit.
This reverts commit d765dff.
("=", "_equal"), | ||
("[]", "_index"), | ||
("!", "_not"), | ||
]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm a bit concerned about the perf implications of this, but it should be optimizable relatively easily so no need to dig into that if not wanted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The old way had the problem that you have to write the reserved symbols twice, a violation of the dry rule.
Is performance even that much of an issue for bindgen?
☔ The latest upstream changes (presumably c509ef1) made this pull request unmergeable. Please resolve the merge conflicts. |
I'm abondoning this in favor of https://github.com/google/autocxx . |
I still want to get to review this (if you're fine with that). I'm sorry for the massive lag here again. |
I solved the merge conflicts. |
☔ The latest upstream changes (presumably 8ac787a) made this pull request unmergeable. Please resolve the merge conflicts. |
If you don't want to merge it, that's fine for me. If you do want to merge it, tell me then I will fix the failing tests and merge conflicts. |
☔ The latest upstream changes (presumably 9738fb9) made this pull request unmergeable. Please resolve the merge conflicts. |
@emilio, do you still want this feature in bindgen? I can rebase this PR if that's the case. |
Note that this PR might be unneccesary due to the existance of autocxx. |
What I did in this PR can be best described using the "bintest" test that I created:
The following works now (See "bintest" folder for full source, of the testcase run "Cargo run" in the "bintest" folder for a proof that it works.):
Up next, I will try to fix #1840 #1841
Note: This is my second open source contribution that is not just a bugfix/documentation fix. Thank you for writing easy to read code.