-
Notifications
You must be signed in to change notification settings - Fork 338
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
Support type aliases across bridges using different C++ namespaces #298
Conversation
17a8230
to
7a8c258
Compare
@@ -2,16 +2,12 @@ | |||
// https://github.com/rust-lang/rustfmt/issues/4159 | |||
#[rustfmt::skip] | |||
#[cxx::bridge(namespace = alias_tests)] | |||
#[cxx::alias_namespace(crate::ffi = tests)] |
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.
Needs documentation but please let me know what you think first.
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.
Thanks for the PR! I have not gotten a chance to read the implementation yet but I am skimming the tests (tests/ffi/alias*.rs) -- my sense is that the usage is going to feel pretty clumsy as a partial solution for "how do we allow one bridge to involve items from more than one namespace", which is the actual thing to solve, I think. If a C++ function in one namespace has an argument type in a different namespace (like alias::ffi::c_take_unique_ptr in the test suite), requiring to express that as two separate cxx::bridge modules with an extern type alias from one to the other and an alias_namespace attribute seems too convoluted.
This isn't fully designed but I wonder if allowing namespace controlled per extern block (inheriting from the namespace of the cxx::bridge if not specified on the extern block) would solve the same thing better.
#[cxx::bridge]
mod ffi {
#[namespace = types]
extern "C" {
type SameC;
}
#[namespace = alias::tests]
extern "C" {
fn take_unique_ptr(c: UniquePtr<SameC>);
}
}
Then, for the case where you do actually want items to be split across different cxx::bridge modules for Rust code organization reasons, the above just composes with the existing support for sharing a single Rust type across all different mentions of the opaque C++ type, by inserting = path::to::SameC
.
Any thoughts on this in comparison to the approach in the PR?
Regarding your earlier comment about "unexpected token" error on an unquoted namespace: that isn't a proc macro or syn thing, just a silly rustc parsing limitation. See this playground for a repro without any macro involved. I opened rust-lang/rust#76734 with a fix.
Great point. There's a couple different use cases being solved for here. I've been approaching this mostly with my use case in mind - where I'm going to use this for CXX shared types (once I add aliasing support for that), not C++ opaque types - but the use case you bring up is also totally valid. I think
We need to keep the module-level attribute that I currently have to support shared type aliases (once I add those, right after or in parallel with this PR), but supporting an attribute at the extern block level as well makes a lot of sense to me. I had forgotten that you can have multiple extern blocks. What do you think of the example below? #[cxx::bridge(namespace = alias::tests)]
#[cxx::alias_namespace(other::ffi = foobar)
mod ffi {
type Shared = other::ffi::Shared;
#[namespace = types]
extern "C" {
type SameC;
}
extern "C" {
fn convert(c: UniquePtr<SameC>) -> Shared;
}
} Also, potential alternative names for FYI: You may not have noticed if you didn't dig into the individual commits, but I had originally added exactly that
Ah great, thanks! |
Ping, looking for a response to proposal above. |
Sure thing -- I apologize about the holdup. The timing was unfortunate here since I was on vacation from the day before this PR was submitted until Tuesday of this week, and now I am working through an enormous review backlog in both https://github.com/rust-lang/rust and my own projects in addition to work. |
No problem, just wanted to get it back on your radar and agree on a design so I don't go through multiple implementation iterations unnecessarily. |
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.
We need to keep the module-level attribute that I currently have to support shared type aliases (once I add those, right after or in parallel with this PR)
I would still very much prefer no module-level alias_namespace attribute. I think the mental model for knowing when to reach for it is too complicated. Is there any other way to express what you need?
From the code snippet in #298 (comment) you could do:
#[cxx::bridge]
mod ffi {
#[namespace = foobar]
use other::ffi::Shared;
#[namespace = types]
extern "C" {
type SameC;
}
#[namespace = alias::tests]
extern "C" {
fn convert(c: UniquePtr<SameC>) -> Shared;
}
}
so basically:
- #[alias_namespace(other::ffi = foobar)]
...
- type Shared = other::ffi::Shared;
+ #[namespace = foobar]
+ use other::ffi::Shared;
...
- type Shared1 = other::ffi::Shared1;
- type Shared2 = other::ffi::Shared2;
+ #[namespace = foobar]
+ use other::ffi::{Shared1, Shared2};
...
- type MyShared = other::ffi::Shared;
+ #[namespace = foobar]
+ use other::ffi::Shared as MyShared;
I’d say it depends on whether you’re still thinking of namespace as a module level setting, as it is today and what I had in mind initially. I notice above you shifted the bridge’s Putting the namespace attribute at only the individual alias and extern block level is certainly the most flexible option if you have to pick one, but conceptually something seems off to me about it that I can’t put my finger on. It doesn’t seem like how namespaces are naturally used in C++, if that makes sense, but I could be wrong. Maybe it’s because it feels odd to have to repeat the attribute if I have a module with both some shared types and an extern C block that should use the same namespace? It could also just feel weird because this doesn’t have a clear C++ analogue; normally you’d never need to specify the namespace at both the type declaration and everywhere it’s included. I also see from your example above that you seem to be favoring use decls rather than type aliases for pulling in shared types from other modules. Are you thinking of switching the current type alias support to that? I guess we’d have to without the module level attribute, to avoid needing lots of namespace attributes to pull in multiple shared types. Should I drop these PRs and just let you implement, or are these useful to you? |
I think this is the case. And it's not just about opaque C++ types, but all FFI items. It is not unusual for a function's argument types or return type to be from a different namespace as the function. I indicated in #298 (review) that I think the actual problem to solve is how to allow one bridge to involve items from more than one namespace, after which a resolution for type aliases across bridges would fall out naturally.
I would say I don't see this as a design goal. The way to view a cxx::bridge module is just as describing what exists in Rust and C++ that each side needs to know about the other. What matters to me is that the description is straightforward such that the individual pieces of functionality implemented by cxx::bridge are easily comprehensible in isolation and compose intuitively, not that it necessarily resembles semantics of C++ as closely as possible.
Yes; the ExternType::Kind associate type/marker types from #325 make this possible because we now have a way for a downstream module to reflect on whether a particular symbol from outside the module is an opaque (extern) type or one which both Rust and C++ can hold by value.
The advantage you have is potentially more time able to dedicate to cxx, since I am also on the hook for rust-lang/rust and 100 other projects maintenance at the same time. One thing that worked well in #325 was a substantial amount of back-and-forth in #313 to iterate and exactly pin down the design beforehand, such that the PR was implementing a design that we both already agreed was good. For #298 (review) there is a chain of reasonable implementation steps which I'd be working through at whatever rate I'm able, but if a PR exists, I would prioritize accepting 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.
I've copied the implementation plan from the previous comment to #353, so I'll close the PR in favor of tracking it there.
Thanks anyway!
I’ve been on vacation this week. Will read above in more detail and see if there’s ways I can contribute next week when I’m back. |
Related to #297