cxx icon indicating copy to clipboard operation
cxx copied to clipboard

Reusing simple structs in the bridge interface

Open Timmmm opened this issue 4 years ago • 1 comments

We have a Rust API that we're trying to expose to C++. It has a ton of simple structs like this:

#[derive(Debug)]
pub struct LibVersion {
  pub string: String,
  pub package_hash: String,
  pub major: u32,
  pub minor: u32,
  pub point: u32,
}

#[derive(Debug, Clone)]
pub struct CompilationTraceEvent {
  pub cat: String,
  pub dur: u64,
  pub name: String,
  pub ph: String,
  pub pid: u32,
  pub tid: u32,
  pub ts: u64,
}

pub struct ItemType {
  pub name: String,
  pub size: u64,
  pub source: ItemSource,
}

#[derive(Debug, Copy, Clone)]
pub enum ItemSource {
  A,
  B,
}

You get the idea. In order to expose these to C++ the obvious but annoying thing to do is to copy/paste them all into the mod ffi { and then tediously add From implementations. Obviously not ideal!

The second best solution that I can think of at the moment is to move everything into the ffi mod, and then we can add pub use ffi::LibVersion etc. which is much less tedious. I think this will work, but it seems like only Copy and Clone auto-derive traits are supported by the macro.

I assume the proc macro system only gives you the contents of the macro, so you could never do something like mod ffi { type LibVersion = super::LibVersion; } because then your proc macro has no idea what the fields of super::LibVersion are - is that right?

I'm not sure what the solution is here but it's definitely an annoying problem....

Timmmm avatar Jan 13 '21 10:01 Timmmm

Has anyone figured out what to do in this situation? CXX seems awesome but we have a lot of structs with custom derives and have run into exactly the same problem here...


Update: As a workaround, I implemented the following patch. If you try to derive something that is not derivable in C++, this patch will still derive those traits for Rust, instead of giving up.

diff --git a/syntax/attrs.rs b/syntax/attrs.rs
index cb40295b..57e79b7b 100644
--- a/syntax/attrs.rs
+++ b/syntax/attrs.rs
@@ -65,9 +65,17 @@ pub fn parse(cx: &mut Errors, attrs: Vec<Attribute>, mut parser: Parser) -> Othe
             }
         } else if attr.path.is_ident("derive") {
             match attr.parse_args_with(|attr: ParseStream| parse_derive_attribute(cx, attr)) {
-                Ok(attr) => {
-                    if let Some(derives) = &mut parser.derives {
-                        derives.extend(attr);
+                Ok((attr_if_ok, ok)) => {
+                    if ok {
+                        if let Some(derives) = &mut parser.derives {
+                            derives.extend(attr_if_ok);
+                            continue;
+                        }
+                    } else {
+                        // If the derive attribute is not valid, we pass it through. Then, it will be available
+                        // in Rust but not in C++. This is often fine.
+                        // For the traits that you do want to derive in C++, provide them in a separate #[derive(...)]
+                        passthrough_attrs.push(attr);
                         continue;
                     }
                 }
@@ -208,10 +216,11 @@ fn parse_doc_attribute(input: ParseStream) -> Result<DocAttribute> {
     }
 }
 
-fn parse_derive_attribute(cx: &mut Errors, input: ParseStream) -> Result<Vec<Derive>> {
+fn parse_derive_attribute(_cx: &mut Errors, input: ParseStream) -> Result<(Vec<Derive>, bool)> {
     let paths = input.parse_terminated::<Path, Token![,]>(Path::parse_mod_style)?;
 
     let mut derives = Vec::new();
+    let mut ok = true;
     for path in paths {
         if let Some(ident) = path.get_ident() {
             if let Some(derive) = Derive::from(ident) {
@@ -219,9 +228,9 @@ fn parse_derive_attribute(cx: &mut Errors, input: ParseStream) -> Result<Vec<Der
                 continue;
             }
         }
-        cx.error(path, "unsupported derive");
+        ok = false;
     }
-    Ok(derives)
+    Ok((derives, ok))
 }
 
 fn parse_repr_attribute(input: ParseStream) -> Result<Atom> {

arvid220u avatar Jun 16 '22 03:06 arvid220u

@Timmmm Did you ever figure out a workaround for this?

casimcdaniels avatar Oct 26 '22 17:10 casimcdaniels

I don't think so, sorry. :-/

Timmmm avatar Oct 27 '22 10:10 Timmmm

The discussion under #1077 lays out the supported options for reusing simple structs in the bridge interface.

dtolnay avatar Nov 03 '22 17:11 dtolnay