Code Monkey home page Code Monkey logo

Comments (6)

dtolnay avatar dtolnay commented on July 29, 2024

I've encountered this in my work codebase as well. What we've done for now is declare RawOptionalBaseValue as an opaque C type in both files, and then do unsafe pointer casts from one to the other where necessary:

// :(
fn convert(bv: &mut first::ffi::BaseValue) -> &mut second::ffi::BaseValue {
    unsafe {
        &mut *(bv as *mut first::ffi::BaseValue as *mut second::ffi::BaseValue)
    }
}

Obviously I'd like to support this better. It needs some more design work but I have some ideas for how to make it work safely and seamlessly, based on treating opaque C types in a way that doesn't produce a distinguishable representation in different places when using the same header and type name:

// effectively this, but encoded without relying on const generics
type RawOptionalBaseValue = OpaqueC<"base/values_rust.h", "RawOptionalBaseValue">;

from cxx.

dtolnay avatar dtolnay commented on July 29, 2024

The part that needs design work is how to deal with codebases in which the same import path might not always refer to the same exact file; in that case allowing the interconversion would be bad. I'm confident it's solvable but just needs some attention.

from cxx.

adetaylor avatar adetaylor commented on July 29, 2024

Thanks, your workaround worked nicely for me.

from cxx.

dtolnay avatar dtolnay commented on July 29, 2024

Does anyone know how bindgen deals with the same issue? If we do two bindgen invocations in two different crates but some of the types are in common (something like folly::StringPiece which both crates might use) then do they also have the problem of those becoming two incompatible StringPiece types on the Rust side?

I guess they provide a workaround of --opaque-type folly::StringPiece which turns it into pub type folly_StringPiece = [u64; 2usize] which is the same type in every crate, but it's not great because it's also the same type as lots of other non-StringPiece types.

from cxx.

dtolnay avatar dtolnay commented on July 29, 2024

I got a chance to think about this a bit today. I think what I'd like looks more or less like this:

// basevalue.rs

#[cxx::bridge(namespace = base)]
pub mod ffi {
    extern "C" {
        include!("base/values_rust.h");

        type RawOptionalBaseValue;

        fn /* ... */
    }
}
// json.rs

#[cxx::bridge(namespace = base)]
pub mod ffi {
    extern "C" {
        type RawOptionalBaseValue = basevalue::ffi::RawOptionalBaseValue;

        fn /* ... */
    }
}

where the first type expands to something like:

// same as today
#[repr(C)]
pub struct RawOptionalBaseValue {
    _private: ::cxx::private::Opaque,
}

// new
unsafe impl ::cxx::private::ExternType for RawOptionalBaseValue {
    type Id = /*type-encoding of "base::RawOptionalBaseValue"*/;
}

while the second type expands to something like:

// assume that they're the same
pub use basevalue::ffi::RawOptionalBaseValue as RawOptionalBaseValue;

// but also enforce that they're the same at compile time
const _: fn() = ::cxx::private::enforce_extern_type_id::<
    RawOptionalBaseValue,
    /*type-encoding of "base::RawOptionalBaseValue"*/,
>;

and in CXX we provide:

#[doc(hidden)]
pub fn enforce_extern_type_id<T: ExternType<Id = Id>, Id>() {}

The exact type-encoding there for the strings isn't important and I am not particularly worried about figuring out something that will work. Longer term we'll use a "non-type template parameter" a.k.a. "const generic" with the actual string literal, but for now you can conceptualize a possible encoding as:

  • "ABC" <-> ch::A<ch::B<ch::C>>

where each of those is a distinct struct A<Rest = ()>(PhantomData<Rest>). This wouldn't necessarily be how we do it for various reasons but it gives the same effect.

from cxx.

dtolnay avatar dtolnay commented on July 29, 2024

In fact we'd likely end up making the trait ExternType public (but still unsafe) so that the user can write their own impls for any bindgen-generated types to let those interop seamlessly with cxx bridge extern functions.

unsafe impl cxx::ExternType for folly_sys::StringPiece {
    type Id = cxx::type_id!("folly::StringPiece");
}

from cxx.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.