From b4f5a59bee975dbd39e592ff30c7a243feb4280b Mon Sep 17 00:00:00 2001 From: Adrian Taylor Date: Fri, 7 Jan 2022 14:35:00 -0800 Subject: [PATCH] Regularize bindgen annotations. Previously autocxx_bindgen added annotations which were not valid Rust code. It now generates annotations which correspond to a real autocxx macro, and thus can more plausibly be upstreamed to the real bindgen. This also extracts a better encapsulated framework for parsing these attributes. --- engine/Cargo.toml | 2 +- engine/src/conversion/analysis/mod.rs | 40 ---- engine/src/conversion/analysis/pod/mod.rs | 7 +- engine/src/conversion/analysis/tdef.rs | 7 +- .../parse/bindgen_semantic_attributes.rs | 193 ++++++++++++++++++ engine/src/conversion/parse/mod.rs | 2 + engine/src/conversion/parse/parse_bindgen.rs | 86 ++------ .../src/conversion/parse/parse_foreign_mod.rs | 77 +------ engine/src/lib.rs | 1 + macro/src/lib.rs | 15 ++ src/lib.rs | 4 + 11 files changed, 255 insertions(+), 179 deletions(-) create mode 100644 engine/src/conversion/parse/bindgen_semantic_attributes.rs diff --git a/engine/Cargo.toml b/engine/Cargo.toml index 2dd3f47b2..129a2629b 100644 --- a/engine/Cargo.toml +++ b/engine/Cargo.toml @@ -36,7 +36,7 @@ log = "0.4" proc-macro2 = "1.0.11" quote = "1.0" indoc = "1.0" -autocxx-bindgen = "=0.59.9" +autocxx-bindgen = "=0.59.10" #autocxx-bindgen = { git = "https://github.com/adetaylor/rust-bindgen", branch = "rvalue-references" } itertools = "0.10" cc = { version = "1.0", optional = true } diff --git a/engine/src/conversion/analysis/mod.rs b/engine/src/conversion/analysis/mod.rs index 23b28cb24..157b13bf3 100644 --- a/engine/src/conversion/analysis/mod.rs +++ b/engine/src/conversion/analysis/mod.rs @@ -12,12 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -use syn::{Attribute, Ident}; - -use crate::conversion::convert_error::ErrorContext; - -use super::{convert_error::ConvertErrorWithContext, ConvertError}; - pub(crate) mod abstract_types; pub(crate) mod casts; pub(crate) mod ctypes; @@ -30,37 +24,3 @@ pub(crate) mod tdef; mod type_converter; pub(crate) use name_check::check_names; - -// Remove `bindgen_` attributes. They don't have a corresponding macro defined anywhere, -// so they will cause compilation errors if we leave them in. -// We may return an error if one of the bindgen attributes shows that the -// item can't be processed. -fn remove_bindgen_attrs( - attrs: &mut Vec, - id: Ident, -) -> Result<(), ConvertErrorWithContext> { - if has_attr(attrs, "bindgen_unused_template_param") { - return Err(ConvertErrorWithContext( - ConvertError::UnusedTemplateParam, - Some(ErrorContext::Item(id)), - )); - } - - fn is_bindgen_attr(attr: &Attribute) -> bool { - let segments = &attr.path.segments; - segments.len() == 1 - && segments - .first() - .unwrap() - .ident - .to_string() - .starts_with("bindgen_") - } - - attrs.retain(|a| !is_bindgen_attr(a)); - Ok(()) -} - -fn has_attr(attrs: &[Attribute], attr_name: &str) -> bool { - attrs.iter().any(|at| at.path.is_ident(attr_name)) -} diff --git a/engine/src/conversion/analysis/pod/mod.rs b/engine/src/conversion/analysis/pod/mod.rs index fa269ad54..0fc81fc36 100644 --- a/engine/src/conversion/analysis/pod/mod.rs +++ b/engine/src/conversion/analysis/pod/mod.rs @@ -26,6 +26,7 @@ use crate::{ api::{AnalysisPhase, Api, ApiName, CppVisibility, StructDetails, TypeKind, UnanalyzedApi}, convert_error::{ConvertErrorWithContext, ErrorContext}, error_reporter::convert_apis, + parse::BindgenSemanticAttributes, ConvertError, }, types::{Namespace, QualifiedName}, @@ -115,7 +116,8 @@ fn analyze_enum( name: ApiName, mut item: ItemEnum, ) -> Result>>, ConvertErrorWithContext> { - super::remove_bindgen_attrs(&mut item.attrs, name.name.get_final_ident())?; + let metadata = BindgenSemanticAttributes::new_retaining_others(&mut item.attrs); + metadata.check_for_fatal_attrs(&name.name.get_final_ident())?; Ok(Box::new(std::iter::once(Api::Enum { name, item }))) } @@ -134,7 +136,8 @@ fn analyze_struct( Some(ErrorContext::Item(id)), )); } - super::remove_bindgen_attrs(&mut details.item.attrs, id.clone())?; + let metadata = BindgenSemanticAttributes::new_retaining_others(&mut details.item.attrs); + metadata.check_for_fatal_attrs(&id)?; let bases = get_bases(&details.item); let mut field_deps = HashSet::new(); let type_kind = if byvalue_checker.is_pod(&name.name) { diff --git a/engine/src/conversion/analysis/tdef.rs b/engine/src/conversion/analysis/tdef.rs index 9ea07526a..1cef4fa47 100644 --- a/engine/src/conversion/analysis/tdef.rs +++ b/engine/src/conversion/analysis/tdef.rs @@ -23,13 +23,12 @@ use crate::{ api::{AnalysisPhase, Api, ApiName, TypedefKind, UnanalyzedApi}, convert_error::{ConvertErrorWithContext, ErrorContext}, error_reporter::convert_apis, + parse::BindgenSemanticAttributes, ConvertError, }, types::QualifiedName, }; -use super::remove_bindgen_attrs; - pub(crate) struct TypedefAnalysis { pub(crate) kind: TypedefKind, pub(crate) deps: HashSet, @@ -92,8 +91,8 @@ fn get_replacement_typedef( extra_apis: &mut Vec, ) -> Result, ConvertErrorWithContext> { let mut converted_type = ity.clone(); - let id = ity.ident.clone(); - remove_bindgen_attrs(&mut converted_type.attrs, id)?; + let metadata = BindgenSemanticAttributes::new_retaining_others(&mut converted_type.attrs); + metadata.check_for_fatal_attrs(&ity.ident)?; let type_conversion_results = type_converter.convert_type( (*ity.ty).clone(), name.name.get_namespace(), diff --git a/engine/src/conversion/parse/bindgen_semantic_attributes.rs b/engine/src/conversion/parse/bindgen_semantic_attributes.rs new file mode 100644 index 000000000..958ff2086 --- /dev/null +++ b/engine/src/conversion/parse/bindgen_semantic_attributes.rs @@ -0,0 +1,193 @@ +// Copyright 2022 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use proc_macro2::{Ident, TokenStream}; +use syn::{ + parenthesized, + parse::{Parse, Parser}, + Attribute, LitStr, +}; + +use crate::conversion::{ + api::{CppVisibility, Layout, References, Virtualness}, + convert_error::{ConvertErrorWithContext, ErrorContext}, + ConvertError, +}; + +/// The set of all annotations that autocxx_bindgen has added +/// for our benefit. +#[derive(Debug)] +pub(crate) struct BindgenSemanticAttributes(Vec); + +impl BindgenSemanticAttributes { + // Remove `bindgen_` attributes. They don't have a corresponding macro defined anywhere, + // so they will cause compilation errors if we leave them in. + // We may return an error if one of the bindgen attributes shows that the + // item can't be processed. + pub(crate) fn new_retaining_others(attrs: &mut Vec) -> Self { + let metadata = Self::new(attrs); + attrs.retain(|a| !(a.path.segments.last().unwrap().ident == "cpp_semantics")); + metadata + } + + pub(crate) fn new(attrs: &[Attribute]) -> Self { + Self( + attrs + .iter() + .filter_map(|attr| { + if attr.path.segments.last().unwrap().ident == "cpp_semantics" { + let r: Result = attr.parse_args(); + r.ok() + } else { + None + } + }) + .collect(), + ) + } + + /// Some attributes indicate we can never handle a given item. Check for those. + pub(crate) fn check_for_fatal_attrs( + &self, + id_for_context: &Ident, + ) -> Result<(), ConvertErrorWithContext> { + if self.has_attr("unused_template_param") { + Err(ConvertErrorWithContext( + ConvertError::UnusedTemplateParam, + Some(ErrorContext::Item(id_for_context.clone())), + )) + } else { + Ok(()) + } + } + + /// Whether the given attribute is present. + pub(super) fn has_attr(&self, attr_name: &str) -> bool { + self.0.iter().any(|a| a.is_ident(attr_name)) + } + + /// The C++ visibility of the item. + pub(super) fn get_cpp_visibility(&self) -> CppVisibility { + if self.has_attr("visibility_private") { + CppVisibility::Private + } else if self.has_attr("visibility_protected") { + CppVisibility::Protected + } else { + CppVisibility::Public + } + } + + /// Whether the item is virtual. + pub(super) fn get_virtualness(&self) -> Virtualness { + if self.has_attr("pure_virtual") { + Virtualness::PureVirtual + } else if self.has_attr("bindgen_virtual") { + Virtualness::Virtual + } else { + Virtualness::None + } + } + + fn parse_if_present(&self, annotation: &str) -> Option { + self.0 + .iter() + .find(|a| a.is_ident(annotation)) + .map(|a| a.parse_args().unwrap()) + } + + fn string_if_present(&self, annotation: &str) -> Option { + let ls: Option = self.parse_if_present(annotation); + ls.map(|ls| ls.value()) + } + + /// The in-memory layout of the item. + pub(super) fn get_layout(&self) -> Option { + self.parse_if_present("layout") + } + + /// The original C++ name, which bindgen may have changed. + pub(super) fn get_original_name(&self) -> Option { + self.string_if_present("original_name") + } + + fn get_bindgen_special_member_annotation(&self) -> Option { + self.string_if_present("special_member") + } + + /// Whether this is a move constructor. + pub(super) fn is_move_constructor(&self) -> bool { + self.get_bindgen_special_member_annotation() + .map_or(false, |val| val == "move_ctor") + } + + /// Any reference parameters or return values. + pub(super) fn get_reference_parameters_and_return(&self) -> References { + let mut results = References::default(); + for a in &self.0 { + if a.is_ident("ret_type_reference") { + results.ref_return = true; + } else if a.is_ident("ret_type_rvalue_reference") { + results.rvalue_ref_return = true; + } else if a.is_ident("arg_type_reference") { + let r: Result = a.parse_args(); + if let Ok(ls) = r { + results.ref_params.insert(ls); + } + } else if a.is_ident("arg_type_rvalue_reference") { + let r: Result = a.parse_args(); + if let Ok(ls) = r { + results.rvalue_ref_params.insert(ls); + } + } + } + results + } +} + +#[derive(Debug)] +struct BindgenSemanticAttribute { + annotation_name: Ident, + body: Option, +} + +impl BindgenSemanticAttribute { + fn is_ident(&self, name: &str) -> bool { + self.annotation_name == name + } + + fn parse_args(&self) -> Result { + T::parse.parse2(self.body.as_ref().unwrap().clone()) + } +} + +impl Parse for BindgenSemanticAttribute { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + let annotation_name: Ident = input.parse()?; + if input.peek(syn::token::Paren) { + let body_contents; + parenthesized!(body_contents in input); + Ok(Self { + annotation_name, + body: Some(body_contents.parse()?), + }) + } else if !input.is_empty() { + Err(input.error("expected nothing")) + } else { + Ok(Self { + annotation_name, + body: None, + }) + } + } +} diff --git a/engine/src/conversion/parse/mod.rs b/engine/src/conversion/parse/mod.rs index 7a27cc43d..1c85fe40b 100644 --- a/engine/src/conversion/parse/mod.rs +++ b/engine/src/conversion/parse/mod.rs @@ -12,7 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. +mod bindgen_semantic_attributes; mod parse_bindgen; mod parse_foreign_mod; +pub(crate) use bindgen_semantic_attributes::BindgenSemanticAttributes; pub(crate) use parse_bindgen::ParseBindgen; diff --git a/engine/src/conversion/parse/parse_bindgen.rs b/engine/src/conversion/parse/parse_bindgen.rs index 98d9e0d58..801aa4a13 100644 --- a/engine/src/conversion/parse/parse_bindgen.rs +++ b/engine/src/conversion/parse/parse_bindgen.rs @@ -16,10 +16,7 @@ use std::collections::HashSet; use crate::{ conversion::{ - api::{ - Api, ApiName, CppVisibility, Layout, StructDetails, SubclassName, TypedefKind, - UnanalyzedApi, Virtualness, - }, + api::{Api, ApiName, StructDetails, SubclassName, TypedefKind, UnanalyzedApi}, ConvertError, }, types::Namespace, @@ -33,9 +30,11 @@ use crate::{ types::validate_ident_ok_for_cxx, }; use autocxx_parser::IncludeCppConfig; -use syn::{parse_quote, Attribute, Fields, Ident, Item, LitStr, TypePath, UseTree}; +use syn::{parse_quote, Fields, Ident, Item, TypePath, UseTree}; -use super::super::utilities::generate_utilities; +use super::{ + super::utilities::generate_utilities, bindgen_semantic_attributes::BindgenSemanticAttributes, +}; use super::parse_foreign_mod::ParseForeignMod; @@ -45,14 +44,14 @@ pub(crate) struct ParseBindgen<'a> { apis: Vec, } -fn api_name(ns: &Namespace, id: Ident, attrs: &[Attribute]) -> ApiName { - ApiName::new_with_cpp_name(ns, id, get_bindgen_original_name_annotation(attrs)) +fn api_name(ns: &Namespace, id: Ident, attrs: &BindgenSemanticAttributes) -> ApiName { + ApiName::new_with_cpp_name(ns, id, attrs.get_original_name()) } pub(crate) fn api_name_qualified( ns: &Namespace, id: Ident, - attrs: &[Attribute], + attrs: &BindgenSemanticAttributes, ) -> Result { match validate_ident_ok_for_cxx(&id.to_string()) { Err(e) => { @@ -63,56 +62,6 @@ pub(crate) fn api_name_qualified( } } -pub(super) fn get_bindgen_original_name_annotation(attrs: &[Attribute]) -> Option { - attrs - .iter() - .filter_map(|a| { - if a.path.is_ident("bindgen_original_name") { - let r: Result = a.parse_args(); - match r { - Ok(ls) => Some(ls.value()), - Err(_) => None, - } - } else { - None - } - }) - .next() -} - -pub(super) fn has_attr(attrs: &[Attribute], attr_name: &str) -> bool { - attrs.iter().any(|a| a.path.is_ident(attr_name)) -} - -pub(super) fn get_cpp_visibility(attrs: &[Attribute]) -> CppVisibility { - if has_attr(attrs, "bindgen_visibility_private") { - CppVisibility::Private - } else if has_attr(attrs, "bindgen_visibility_protected") { - CppVisibility::Protected - } else { - CppVisibility::Public - } -} - -pub(super) fn get_virtualness(attrs: &[Attribute]) -> Virtualness { - if has_attr(attrs, "bindgen_pure_virtual") { - Virtualness::PureVirtual - } else if has_attr(attrs, "bindgen_virtual") { - Virtualness::Virtual - } else { - Virtualness::None - } -} - -fn parse_layout(attrs: &[Attribute]) -> Option { - for a in attrs { - if a.path.is_ident("bindgen_layout") { - return Some(a.parse_args().unwrap()); - } - } - None -} - impl<'a> ParseBindgen<'a> { pub(crate) fn new(config: &'a IncludeCppConfig) -> Self { ParseBindgen { @@ -214,9 +163,10 @@ impl<'a> ParseBindgen<'a> { return Ok(()); } let is_forward_declaration = Self::spot_forward_declaration(&s.fields); + let annotations = BindgenSemanticAttributes::new(&s.attrs); // cxx::bridge can't cope with type aliases to generic // types at the moment. - let name = api_name_qualified(ns, s.ident.clone(), &s.attrs)?; + let name = api_name_qualified(ns, s.ident.clone(), &annotations)?; let api = if ns.is_empty() && self.config.is_rust_type(&s.ident) { None } else if is_forward_declaration { @@ -225,8 +175,8 @@ impl<'a> ParseBindgen<'a> { Some(UnanalyzedApi::Struct { name, details: Box::new(StructDetails { - vis: get_cpp_visibility(&s.attrs), - layout: parse_layout(&s.attrs), + vis: annotations.get_cpp_visibility(), + layout: annotations.get_layout(), item: s, }), analysis: (), @@ -240,8 +190,9 @@ impl<'a> ParseBindgen<'a> { Ok(()) } Item::Enum(e) => { + let annotations = BindgenSemanticAttributes::new(&e.attrs); let api = UnanalyzedApi::Enum { - name: api_name_qualified(ns, e.ident.clone(), &e.attrs)?, + name: api_name_qualified(ns, e.ident.clone(), &annotations)?, item: e, }; if !self.config.is_on_blocklist(&api.name().to_cpp_name()) { @@ -301,8 +252,9 @@ impl<'a> ParseBindgen<'a> { Some(ErrorContext::Item(new_id.clone())), )); } + let annotations = BindgenSemanticAttributes::new(&use_item.attrs); self.apis.push(UnanalyzedApi::Typedef { - name: api_name(ns, new_id.clone(), &use_item.attrs), + name: api_name(ns, new_id.clone(), &annotations), item: TypedefKind::Use(parse_quote! { pub use #old_path as #new_id; }), @@ -322,15 +274,17 @@ impl<'a> ParseBindgen<'a> { Ok(()) } Item::Const(const_item) => { + let annotations = BindgenSemanticAttributes::new(&const_item.attrs); self.apis.push(UnanalyzedApi::Const { - name: api_name(ns, const_item.ident.clone(), &const_item.attrs), + name: api_name(ns, const_item.ident.clone(), &annotations), const_item, }); Ok(()) } Item::Type(ity) => { + let annotations = BindgenSemanticAttributes::new(&ity.attrs); self.apis.push(UnanalyzedApi::Typedef { - name: api_name(ns, ity.ident.clone(), &ity.attrs), + name: api_name(ns, ity.ident.clone(), &annotations), item: TypedefKind::Type(ity), old_tyname: None, analysis: (), diff --git a/engine/src/conversion/parse/parse_foreign_mod.rs b/engine/src/conversion/parse/parse_foreign_mod.rs index ed068b837..aa3bd2e50 100644 --- a/engine/src/conversion/parse/parse_foreign_mod.rs +++ b/engine/src/conversion/parse/parse_foreign_mod.rs @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -use crate::conversion::api::{ApiName, References}; +use crate::conversion::api::ApiName; use crate::conversion::doc_attr::get_doc_attr; use crate::conversion::error_reporter::report_any_error; use crate::conversion::{ @@ -25,14 +25,9 @@ use crate::{ types::{Namespace, QualifiedName}, }; use std::collections::HashMap; -use syn::{ - Block, Expr, ExprCall, ForeignItem, ForeignItemFn, Ident, ImplItem, ItemImpl, LitStr, Stmt, - Type, -}; +use syn::{Block, Expr, ExprCall, ForeignItem, Ident, ImplItem, ItemImpl, Stmt, Type}; -use super::parse_bindgen::{ - get_bindgen_original_name_annotation, get_cpp_visibility, get_virtualness, has_attr, -}; +use super::bindgen_semantic_attributes::BindgenSemanticAttributes; /// Parses a given bindgen-generated 'mod' into suitable /// [Api]s. In bindgen output, a given mod concerns @@ -77,15 +72,7 @@ impl ParseForeignMod { fn parse_foreign_item(&mut self, i: ForeignItem) -> Result<(), ConvertErrorWithContext> { match i { ForeignItem::Fn(item) => { - let cpp_vis = get_cpp_visibility(&item.attrs); - let virtualness = get_virtualness(&item.attrs); - let unused_template_param = has_attr( - &item.attrs, - "bindgen_unused_template_param_in_arg_or_return", - ); - let is_move_constructor = Self::is_move_constructor(&item); - let references = Self::get_reference_parameters_and_return(&item); - let original_name = get_bindgen_original_name_annotation(&item.attrs); + let annotations = BindgenSemanticAttributes::new(&item.attrs); let doc_attr = get_doc_attr(&item.attrs); self.funcs_to_convert.push(FuncToConvert { self_ty: None, @@ -94,12 +81,13 @@ impl ParseForeignMod { inputs: item.sig.inputs, output: item.sig.output, vis: item.vis, - virtualness, - cpp_vis, - is_move_constructor, - unused_template_param, - references, - original_name, + virtualness: annotations.get_virtualness(), + cpp_vis: annotations.get_cpp_visibility(), + is_move_constructor: annotations.is_move_constructor(), + unused_template_param: annotations + .has_attr("unused_template_param_in_arg_or_return"), + references: annotations.get_reference_parameters_and_return(), + original_name: annotations.get_original_name(), synthesized_this_type: None, synthesis: None, }); @@ -116,49 +104,6 @@ impl ParseForeignMod { } } - fn get_bindgen_special_member_annotation(fun: &ForeignItemFn) -> Option { - fun.attrs - .iter() - .filter_map(|a| { - if a.path.is_ident("bindgen_special_member") { - let r: Result = a.parse_args(); - match r { - Ok(ls) => Some(ls.value()), - Err(_) => None, - } - } else { - None - } - }) - .next() - } - - fn is_move_constructor(fun: &ForeignItemFn) -> bool { - Self::get_bindgen_special_member_annotation(fun).map_or(false, |val| val == "move_ctor") - } - - fn get_reference_parameters_and_return(fun: &ForeignItemFn) -> References { - let mut results = References::default(); - for a in &fun.attrs { - if a.path.is_ident("bindgen_ret_type_reference") { - results.ref_return = true; - } else if a.path.is_ident("bindgen_arg_type_reference") { - let r: Result = a.parse_args(); - if let Ok(ls) = r { - results.ref_params.insert(ls); - } - } else if a.path.is_ident("bindgen_ret_type_rvalue_reference") { - results.rvalue_ref_return = true; - } else if a.path.is_ident("bindgen_arg_type_rvalue_reference") { - let r: Result = a.parse_args(); - if let Ok(ls) = r { - results.rvalue_ref_params.insert(ls); - } - } - } - results - } - /// Record information from impl blocks encountered in bindgen /// output. pub(crate) fn convert_impl_items(&mut self, imp: ItemImpl) { diff --git a/engine/src/lib.rs b/engine/src/lib.rs index 144da44c6..0bb5639ae 100644 --- a/engine/src/lib.rs +++ b/engine/src/lib.rs @@ -298,6 +298,7 @@ impl IncludeCppEngine { .generate_inline_functions(true) .respect_cxx_access_specs(true) .use_specific_virtual_function_receiver(true) + .cpp_semantic_attributes(true) .layout_tests(false); // TODO revisit later for item in known_types().get_initial_blocklist() { builder = builder.blocklist_item(item); diff --git a/macro/src/lib.rs b/macro/src/lib.rs index 0c22e19d5..529476c7c 100644 --- a/macro/src/lib.rs +++ b/macro/src/lib.rs @@ -118,3 +118,18 @@ pub fn extern_rust_function(attr: TokenStream, input: TokenStream) -> TokenStrea } input } + +/// Attribute which should never be encountered in real life. +/// This is something which features in the Rust source code generated +/// by autocxx-bindgen and passed to autocxx-engine, which should never +/// normally be compiled by rustc before it undergoes further processing. +#[proc_macro_error] +#[proc_macro_attribute] +pub fn cpp_semantics(_attr: TokenStream, _input: TokenStream) -> TokenStream { + abort!( + Span::call_site(), + "Please do not attempt to compile this code. \n\ + This code is the output from the autocxx-specific version of bindgen, \n\ + and should be interpreted by autocxx-engine before further usage." + ); +} diff --git a/src/lib.rs b/src/lib.rs index 89f12cfb5..a961e4660 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -685,6 +685,9 @@ macro_rules! usage { #[doc(hidden)] pub use autocxx_macro::include_cpp_impl; +#[doc(hidden)] +pub use autocxx_macro::cpp_semantics; + macro_rules! ctype_wrapper { ($r:ident, $c:expr, $d:expr) => { #[doc=$d] @@ -811,6 +814,7 @@ pub mod prelude { pub use crate::c_ulonglong; pub use crate::c_ushort; pub use crate::c_void; + pub use crate::cpp_semantics; pub use crate::include_cpp; pub use crate::PinMut; pub use moveit::moveit;