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)),
    }
}