cbindgen
cbindgen copied to clipboard
Handle fully qualified paths and `use` aliases
Each Rust Item has a path that includes the crate and mods needed to get to it. Currently cbindgen just ignores all segments of a path except the last one, which is the name of the item.
This works as long as there are no duplicates in different modules, but isn't correct and will probably cause someone an issue someday.
My comments earlier were wrong.
cbindgen used to refuse to parse paths that had multiple segments. This wasn't great so I fixed it in 015572877a9eec5b538c71ff73ddedae40e6210c.
Any updates on this? I've got multiple struct Configs in different modules and want to export them all.
- What's the best workaround currently?
- Do you have any specific plans on implementing this?
- If it's not very hard, I could try to implement the feature (equipped with some instructions).
Unfortunately, I'm not sure there is a workaround yet besides renaming the types. My first thought would have been to use the structs behind type aliases with different names, but that won't work if you want to expose them..
I don't currently have specific plans on implementing this. Unfortunately this is a tricky issue with a couple different options.
- Implement a custom path resolution algorithm on top of syn
- Use rls-analysis to hook into the work going on there for goto definition
- Write a rustc integration to get this data
I've been told by @Gankro that option 1 is not going to be fun as this is a very complicated and not well understood part of rustc and has evolved. I think this part of the rust reference is relevant.
Using rls-analysis could be viable, but needs investigation. My concern with using rls would be if it is using a lossy/mostly correct goto definition feature and not focused on strict correctness.
I have no idea of how feasible option 3 would be.
Issue: Import renames à la use Foo as Bar; are not resolved properly.
A minimalistic crate like this …
// lib.rs
pub struct Foo { pub field: bool }
use Foo as Bar;
#[no_mangle]
pub unsafe extern "C" fn placebo(_bar: &Bar) {}
… generates a header like this …
// lib.h
#include <cstdint>
#include <cstdlib>
extern "C" {
void placebo(const Bar *_bar);
} // extern "C"
… emitting the following warning:
WARN: Can't find
Bar. This usually means that this type was incompatible or not found.
… while it should generate something like this instead:
// lib.h
#include <cstdint>
#include <cstdlib>
struct Foo;
extern "C" {
void placebo(const Foo *_bar);
} // extern "C"
What's wrong:
- cbindgen doesn't recognize that
Foois used in an exportedfnthus does not generate it. _bar: &Baris not properly resolved to_bar: &Fooin the function argument.
(Cross-posted from https://github.com/eqrion/cbindgen/issues/200, which was closed in favor of this original issue)
Issue: Incorrect type resolving for differently scoped types of same name.
A minimalistic crate like this …
// lib.rs
pub mod foo {
pub struct Foobar<T> { pub field: T }
}
pub mod bar {
pub struct Foobar { pub field: u8 }
#[no_mangle]
pub unsafe extern "C" fn placebo(_foobar: &Foobar) {}
}
… generates a header like this …
// lib.h
#include <cstdint>
#include <cstdlib>
template<typename T>
struct Foobar;
extern "C" {
void placebo(const Foobar *_foobar);
} // extern "C"
… while it should generate something like this instead:
#include <cstdint>
#include <cstdlib>
struct Foobar;
extern "C" {
void placebo(const Foobar *_foobar);
} // extern "C"
What's wrong:
Foobar<T>is not used in any exportedfnand hence shouldn't be generated.Foobarhowever is used in an exportedfnand hence should be generated.
Observations:
- Changing the order of
mod fooandmod barmakes it behave correctly. - Adding
#[repr(C)]tobar::Foobarmakes it behave correctly, strangely enough. - The
<T>doesn't seem to be strictly necessary for triggering this bug, but makes things easier to spot in the generated header.
(Cross-posted from https://github.com/eqrion/cbindgen/issues/199, which was closed in favor of this original issue)
I just pushed a PR (https://github.com/eqrion/cbindgen/pull/201) that aims to move us a bit closer towards a direction of fixing this.
This is an issue with https://github.com/KZen-networks/multi-party-ecdsa
Did #233 fix this?
Not really, though it added the infrastructure to potentially do it.
Any updates here? Would love to see this implemented 🙏
Not really, though it added the infrastructure to potentially do it.
Can you elaborate on this a bit? I know nothing about the internals of this project but would really like to see this feature implemented 😅
@UebelAndre A look at the diff shows that the PR replaces the use of bare strings with
pub struct Path {
name: String,
}
As such the PR has improved type-safety, but has not actually made the jump from "name-based" to "full qualified path-based", unlike what the name might suggest.
My workaround for the "Issue: Import renames à la use Foo as Bar; are not resolved properly." issue is a macro for opaque newtype wrapping.
macro_rules! opaque {
($exported:ident, $internal:ty) => {
pub struct $exported($internal);
impl From<$internal> for $exported {
fn from(v: $internal) -> Self {
Self(v)
}
}
impl From<$exported> for $internal {
fn from(value: $exported) -> Self {
value.0
}
}
};
}
opaque!(LoadConfig, load::Config);
opaque!(EmitConfig, emit::Config);
(where the parse section of the cbindgen config carefully doesn't include either of the load or emit crates)
This only works in my specific case because these types are opaque (they're always provided behind a pointer). I don't know a workaround for non-opaque types.