1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114
// Copyright 2020 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 autocxx_engine::IncludeCpp; use proc_macro::TokenStream; use proc_macro_error::{abort_call_site, proc_macro_error}; use syn::parse_macro_input; /// Include some C++ headers in your Rust project. /// /// This macro allows you to include one or more C++ headers within /// your Rust code, and call their functions fairly naturally. /// /// # Examples /// /// C++ header (`input.h`): /// ```cpp /// #include <cstdint> /// /// uint32_t do_math(uint32_t a); /// ``` /// /// Rust code: /// ``` /// # use autocxx::include_cxx; /// include_cxx!( /// # ParseOnly, /// Header("input.h"), /// Allow("do_math"), /// ); /// /// # mod ffi { pub mod cxxbridge { pub fn do_math(a: u32) -> u32 { a+3 } } } /// # fn main() { /// ffi::cxxbridge::do_math(3); /// # } /// ``` /// /// # Configuring the build /// /// To build this, you'll need to: /// * Educate the procedural macro about where to find the C++ headers. Set the /// `AUTOCXX_INC` environment variable to a list of directories to search. /// * Build the C++ side of the bindings. You'll need to use the `autocxx-gen` /// crate (or similar) to process the same .rs code into C++ header and /// implementation files. /// /// # Syntax /// /// Within the brackets of the `include_cpp!(...)` macro, you should provide /// a list of the following: /// /// * *Header(filename)*: a header filename to parse and include /// * *Allow(type or function name)*: a type or function name whose declaration /// should be made available to C++. /// * *AllowPOD(type name)*: a struct name whose declaration /// should be made available to C++, and whose fields are sufficient simple /// that they can allow being passed back and forth by value (POD = 'plain old /// data'). /// /// # How to allow structs /// /// A C++ struct can be listed under 'Allow' or 'AllowPOD' (or may be implicitly /// 'Allowed' because it's a type referenced by something else you've allowed.) /// /// The current plan is to use 'Allow' under normal circumstances, but /// 'AllowPOD' only for structs where you absolutely do need to pass them /// truly by value and have direct field access. /// Some structs can't be represented as POD, e.g. those containing `std::string` /// due to self-referential pointers. We will always handle such things using /// `UniquePtr` to an opaque type in Rust, but still allow calling existing C++ /// APIs which take such things by value - we'll aim to generate automatic /// unwrappers. This won't work in all cases. /// The majority of this paragraph doesn't work at all yet, and it will never work /// for some cases. It remains to be seen whether this is good enough in practice. /// /// # Generated code /// /// You will find that this macro expands to the equivalent of: /// /// ``` /// mod ffi { /// pub mod cxxbridge { /// pub fn do_math(a: u32) -> u32 /// # { a+3 } /// } /// /// pub const kMyCxxConst: i32 = 3; /// /// pub mod defs { /// pub const MY_PREPROCESSOR_DEFINITION: i64 = 3i64; /// } /// } /// ``` #[proc_macro_error] #[proc_macro] pub fn include_cxx(input: TokenStream) -> TokenStream { let include_cpp = parse_macro_input!(input as IncludeCpp); match include_cpp.generate_rs() { Ok(ts) => TokenStream::from(ts), Err(e) => abort_call_site!(format!("{:?}", e)), } }