diff --git a/src/bindgen/config.rs b/src/bindgen/config.rs index 8d11eae..27067cd 100644 --- a/src/bindgen/config.rs +++ b/src/bindgen/config.rs @@ -185,6 +185,9 @@ impl FunctionConfig { pub struct StructConfig { /// The rename rule to apply to the name of struct fields pub rename_fields: Option, + /// Whether to generate a constructor for the struct (which takes + /// arguments to initialize all the members) + pub derive_constructor: bool, /// Whether to generate a piecewise equality operator pub derive_eq: bool, /// Whether to generate a piecewise inequality operator @@ -203,6 +206,7 @@ impl Default for StructConfig { fn default() -> StructConfig { StructConfig { rename_fields: None, + derive_constructor: false, derive_eq: false, derive_neq: false, derive_lt: false, @@ -214,6 +218,12 @@ impl Default for StructConfig { } impl StructConfig { + pub(crate) fn derive_constructor(&self, annotations: &AnnotationSet) -> bool { + if let Some(x) = annotations.bool("derive-constructor") { + return x; + } + self.derive_constructor + } pub(crate) fn derive_eq(&self, annotations: &AnnotationSet) -> bool { if let Some(x) = annotations.bool("derive-eq") { return x; diff --git a/src/bindgen/ir/structure.rs b/src/bindgen/ir/structure.rs index 99c46ad..eb0cd2f 100644 --- a/src/bindgen/ir/structure.rs +++ b/src/bindgen/ir/structure.rs @@ -232,6 +232,48 @@ impl Source for Struct { if config.language == Language::Cxx { let mut wrote_start_newline = false; + if config.structure.derive_constructor(&self.annotations) + && !self.fields.is_empty() + { + if !wrote_start_newline { + wrote_start_newline = true; + out.new_line(); + } + + out.new_line(); + + let arg_renamer = |name: &str| { + config.function.rename_args + .as_ref() + .unwrap_or(&RenameRule::GeckoCase) + .apply_to_snake_case(name, IdentifierType::FunctionArg) + }; + write!(out, "{}(", self.name); + out.write_vertical_source_list( + &self.fields + .iter() + .map(|&(ref name, ref ty, _)| { + // const-ref args to constructor + (format!("const& {}", arg_renamer(name)), ty.clone()) + }) + .collect(), + ListType::Join(","), + ); + write!(out, ")"); + out.new_line(); + write!(out, " : "); + out.write_vertical_source_list( + &self.fields + .iter() + .map(|x| format!("{}({})", x.0, arg_renamer(&x.0))) + .collect(), + ListType::Join(","), + ); + out.new_line(); + write!(out, "{{}}"); + out.new_line(); + } + let other = if let Some(r) = config.function.rename_args { r.apply_to_snake_case("other", IdentifierType::FunctionArg) } else { diff --git a/tests/expectations/annotation.cpp b/tests/expectations/annotation.cpp index 0e272ce..bf6defd 100644 --- a/tests/expectations/annotation.cpp +++ b/tests/expectations/annotation.cpp @@ -9,6 +9,10 @@ enum class C : uint32_t { struct A { int32_t m0; + A(int32_t const& aM0) + : m0(aM0) + {} + bool operator<(const A& other) const { return m0 < other.m0; } diff --git a/tests/rust/annotation.rs b/tests/rust/annotation.rs index a4c08ea..15b67b7 100644 --- a/tests/rust/annotation.rs +++ b/tests/rust/annotation.rs @@ -1,5 +1,6 @@ /// cbindgen:derive-lt=true /// cbindgen:derive-lte=true +/// cbindgen:derive-constructor=true /// cbindgen:rename-all=GeckoCase #[repr(C)] struct A(i32);