blob: 8ed957221ddc3a24babcb77b18491755865b5412 [file] [log] [blame]
David Tolnay7db73692019-10-20 14:51:12 -04001//! This library provides a **safe** mechanism for calling C++ code from Rust
2//! and Rust code from C++, not subject to the many ways that things can go
3//! wrong when using bindgen or cbindgen to generate unsafe C-style bindings.
4//!
5//! <br>
6//!
7//! *Compiler support: requires rustc 1.42+ (beta on January 30, stable on March
8//! 12)*
9//!
10//! <br>
11//!
12//! # Overview
13//!
14//! The idea is that we define the signatures of both sides of our FFI boundary
15//! embedded together in one Rust module (the next section shows an example).
16//! From this, CXX receives a complete picture of the boundary to perform static
17//! analyses against the types and function signatures to uphold both Rust's and
18//! C++'s invariants and requirements.
19//!
20//! If everything checks out statically, then CXX uses a pair of code generators
21//! to emit the relevant `extern "C"` signatures on both sides together with any
22//! necessary static assertions for later in the build process to verify
23//! correctness. On the Rust side this code generator is simply an attribute
24//! procedural macro. On the C++ side it can be a small Cargo build script if
25//! your build is managed by Cargo, or for other build systems like Bazel or
26//! Buck we provide a command line tool which generates the header and source
27//! file and should be easy to integrate.
28//!
29//! The resulting FFI bridge operates at zero or negligible overhead, i.e. no
30//! copying, no serialization, no memory allocation, no runtime checks needed.
31//!
32//! The FFI signatures are able to use native types from whichever side they
33//! please, such as Rust's `String` or C++'s `std::string`, Rust's `Box` or
34//! C++'s `std::unique_ptr`, Rust's `Vec` or C++'s `std::vector`, etc in any
35//! combination. CXX guarantees an ABI-compatible signature that both sides
36//! understand, based on builtin bindings for key standard library types to
37//! expose an idiomatic API on those types to the other language. For example
38//! when manipulating a C++ string from Rust, its `len()` method becomes a call
39//! of the `size()` member function defined by C++; when manipulation a Rust
40//! string from C++, its `size()` member function calls Rust's `len()`.
41//!
42//! <br>
43//!
44//! # Example
45//!
46//! A runnable version of this example is provided under the *demo-rs* directory
47//! of https://github.com/dtolnay/cxx (with the C++ side of the implementation
48//! in the *demo-cxx* directory). To try it out, jump into demo-rs and run
49//! `cargo run`.
50//!
51//! ```no_run
52//! #[cxx::bridge]
53//! mod ffi {
54//! // Any shared structs, whose fields will be visible to both languages.
55//! struct SharedThing {
56//! z: i32,
57//! y: Box<ThingR>,
58//! x: UniquePtr<ThingC>,
59//! }
60//!
61//! extern "C" {
62//! // One or more headers with the matching C++ declarations. Our code
63//! // generators don't read it but it gets #include'd and used in static
64//! // assertions to ensure our picture of the FFI boundary is accurate.
65//! include!("demo-cxx/demo.h");
66//!
67//! // Zero or more opaque types which both languages can pass around but
68//! // only C++ can see the fields.
69//! type ThingC;
70//!
71//! // Functions implemented in C++.
72//! fn make_demo(appname: &str) -> UniquePtr<ThingC>;
73//! fn get_name(thing: &ThingC) -> &CxxString;
74//! fn do_thing(state: SharedThing);
75//! }
76//!
77//! extern "Rust" {
78//! // Zero or more opaque types which both languages can pass around but
79//! // only Rust can see the fields.
80//! type ThingR;
81//!
82//! // Functions implemented in Rust.
83//! fn print_r(r: &ThingR);
84//! }
85//! }
86//! #
87//! # pub struct ThingR(usize);
88//! #
89//! # fn print_r(r: &ThingR) {
90//! # println!("called back with r={}", r.0);
91//! # }
92//! #
93//! # fn main() {}
94//! ```
95//!
96//! Now we simply provide C++ definitions of all the things in the `extern "C"`
97//! block and Rust definitions of all the things in the `extern "Rust"` block,
98//! and get to call back and forth safely.
99//!
100//! Here are links to the complete set of source files involved in the demo:
101//!
102//! - [demo-rs/src/main.rs](https://github.com/dtolnay/cxx/blob/master/demo-rs/src/main.rs)
103//! - [demo-rs/build.rs](https://github.com/dtolnay/cxx/blob/master/demo-rs/build.rs)
104//! - [demo-cxx/demo.h](https://github.com/dtolnay/cxx/blob/master/demo-cxx/demo.h)
105//! - [demo-cxx/demo.cc](https://github.com/dtolnay/cxx/blob/master/demo-cxx/demo.cc)
106//!
107//! To look at the code generated in both languages for the example by the CXX
108//! code generators:
109//!
110//! ```console
111//! # run Rust code generator and print to stdout
112//! # (requires https://github.com/dtolnay/cargo-expand)
113//! $ cargo expand --manifest-path demo-rs/Cargo.toml
114//!
115//! # run C++ code generator and print to stdout
116//! $ cargo run --manifest-path cmd/Cargo.toml -- demo-rs/src/main.rs
117//! ```
118//!
119//! <br>
120//!
121//! # Details
122//!
123//! As seen in the example, the language of the FFI boundary involves 3 kinds of
124//! items:
125//!
126//! - **Shared structs** &mdash; their fields are made visible to both
127//! languages. The definition written within cxx::bridge is the single source
128//! of truth.
129//!
130//! - **Opaque types** &mdash; their fields are secret from the other language.
131//! These cannot be passed across the FFI by value but only behind an
132//! indirection, such as a reference `&`, a Rust `Box`, or a `UniquePtr`. Can
133//! be a type alias for an arbitrarily complicated generic language-specific
134//! type depending on your use case.
135//!
136//! - **Functions** &mdash; implemented in either language, callable from the
137//! other language.
138//!
139//! Within the `extern "C"` part of the CXX bridge we list the types and
140//! functions for which C++ is the source of truth, as well as the header(s)
141//! that declare those APIs. In the future it's possible that this section could
142//! be generated bindgen-style from the headers but for now we need the
143//! signatures written out; static assertions will verify that they are
144//! accurate.
145//!
146//! Within the `extern "Rust"` part, we list types and functions for which Rust
147//! is the source of truth. These all implicitly refer to the `super` module,
148//! the parent module of the CXX bridge. You can think of the two items listed
149//! in the example above as being like `use super::ThingR` and `use
150//! super::print_r` except re-exported to C++. The parent module will either
151//! contain the definitions directly for simple things, or contain the relevant
152//! `use` statements to bring them into scope from elsewhere.
153//!
154//! Your function implementations themselves, whether in C++ or Rust, *do not*
155//! need to be defined as `extern "C"` ABI or no\_mangle. CXX will put in the
156//! right shims where necessary to make it all work.
157//!
158//! <br>
159//!
160//! # Comparison vs bindgen and cbindgen
161//!
162//! Notice that with CXX there is repetition of all the function signatures:
163//! they are typed out once where the implementation is defined (in C++ or Rust)
164//! and again inside the cxx::bridge module, though compile-time assertions
165//! guarantee these are kept in sync. This is different from [bindgen] and
166//! [cbindgen] where function signatures are typed by a human once and the tool
167//! consumes them in one language and emits them in the other language.
168//!
169//! [bindgen]: https://github.com/rust-lang/rust-bindgen
170//! [cbindgen]: https://github.com/eqrion/cbindgen/
171//!
172//! This is because CXX fills a somewhat different role. It is a lower level
173//! tool than bindgen or cbindgen in a sense; you can think of it as being a
174//! replacement for the concept of `extern "C"` signatures as we know them,
175//! rather than a replacement for a bindgen. It would be reasonable to build a
176//! higher level bindgen-like tool on top of CXX which consumes a C++ header
177//! and/or Rust module (and/or IDL like Thrift) as source of truth and generates
178//! the cxx::bridge, eliminating the repetition while leveraging the static
179//! analysis safety guarantees of CXX.
180//!
181//! But note in other ways CXX is higher level than the bindgens, with rich
182//! support for common standard library types. Frequently with bindgen when we
183//! are dealing with an idiomatic C++ API we would end up manually wrapping that
184//! API in C-style raw pointer functions, applying bindgen to get unsafe raw
185//! pointer Rust functions, and replicating the API again to expose those
186//! idiomatically in Rust. That's a much worse form of repetition because it is
187//! unsafe all the way through.
188//!
189//! By using a CXX bridge as the shared understanding between the languages,
190//! rather than `extern "C"` C-style signatures as the shared understanding,
191//! common FFI use cases become expressible using 100% safe code.
192//!
193//! It would also be reasonable to mix and match, using CXX bridge for the 95%
194//! of your FFI that is straightforward and doing the remaining few oddball
195//! signatures the old fashioned way with bindgen and cbindgen, if for some
196//! reason CXX's static restrictions get in the way. Please file an issue if you
197//! end up taking this approach so that we know what ways it would be worthwhile
198//! to make the tool more expressive.
199//!
200//! <br>
201//!
202//! # Cargo-based setup
203//!
204//! For builds that are orchestrated by Cargo, you will use a build script that
205//! runs CXX's C++ code generator and compiles the resulting C++ code along with
206//! any other C++ code for your crate.
207//!
208//! The canonical build script is as follows. The indicated line returns a
209//! [`cc::Build`] instance (from the usual widely used `cc` crate) on which you
210//! can set up any additional source files and compiler flags as normal.
211//!
212//! [`cc::Build`]: https://docs.rs/cc/1.0/cc/struct.Build.html
213//!
214//! ```no_run
215//! // build.rs
216//!
217//! fn main() {
218//! cxx::Build::new()
219//! .bridge("src/main.rs") // returns a cc::Build
220//! .file("../demo-cxx/demo.cc")
221//! .flag("-std=c++11")
222//! .compile("cxxbridge-demo");
223//!
224//! println!("cargo:rerun-if-changed=src/main.rs");
225//! println!("cargo:rerun-if-changed=../demo-cxx/demo.h");
226//! println!("cargo:rerun-if-changed=../demo-cxx/demo.cc");
227//! }
228//! ```
229//!
230//! <br><br>
231//!
232//! # Non-Cargo setup
233//!
234//! For use in non-Cargo builds like Bazel or Buck, CXX provides an alternate
235//! way of invoking the C++ code generator as a standalone command line tool.
236//! The tool is packaged as the `cxxbridge-cmd` crate on crates.io or can be
237//! built from the *cmd* directory of https://github.com/dtolnay/cxx.
238//!
239//! ```bash
240//! $ cargo install cxxbridge-cmd
241//!
242//! $ cxxbridge src/main.rs --header > path/to/mybridge.h
243//! $ cxxbridge src/main.rs > path/to/mybridge.cc
244//! ```
245//!
246//! <br>
247//!
248//! # Safety
249//!
250//! Be aware that the design of this library is intentionally restrictive and
251//! opinionated! It isn't a goal to be powerful enough to handle arbitrary
252//! signatures in either language. Instead this project is about carving out a
253//! reasonably expressive set of functionality about which we can make useful
254//! safety guarantees today and maybe extend over time. You may find that it
255//! takes some practice to use CXX bridge effectively as it won't work in all
256//! the ways that you are used to.
257//!
258//! Some of the considerations that go into ensuring safety are:
259//!
260//! - By design, our paired code generators work together to control both sides
261//! of the FFI boundary. Ordinarily in Rust writing your own `extern "C"`
262//! blocks is unsafe because the Rust compiler has no way to know whether the
263//! signatures you've written actually match the signatures implemented in the
264//! other language. With CXX we achieve that visibility and know what's on the
265//! other side.
266//!
267//! - Our static analysis detects and prevents passing types by value that
268//! shouldn't be passed by value from C++ to Rust, for example because they
269//! may contain internal pointers that would be screwed up by Rust's move
270//! behavior.
271//!
272//! - To many people's surprise, it is possible to have a struct in Rust and a
273//! struct in C++ with exactly the same layout / fields / alignment /
274//! everything, and still not the same ABI when passed by value. This is a
275//! longstanding bindgen bug that leads to segfaults in absolutely
276//! correct-looking code ([rust-lang/rust-bindgen#778]). CXX knows about this
277//! and can insert the necessary zero-cost workaround transparently where
278//! needed, so go ahead and pass your structs by value without worries. This
279//! is made possible by owning both sides of the boundary rather than just
280//! one.
281//!
282//! - Template instantiations: for example in order to expose a UniquePtr\<T\>
283//! type in Rust backed by a real C++ unique\_ptr, we have a way of using a
284//! Rust trait to connect the behavior back to the template instantiations
285//! performed by the other language.
286//!
287//! [rust-lang/rust-bindgen#778]: https://github.com/rust-lang/rust-bindgen/issues/778
288//!
289//! <br>
290//!
291//! # Builtin types
292//!
293//! In addition to all the primitive types (i32 ⟷ int32_t), the following common
294//! types may be used in the fields of shared structs and the arguments and
295//! returns of functions.
296//!
297//! <table>
298//! <tr><th>name in Rust</th><th>name in C++</th><th>restrictions</th></tr>
299//! <tr><td>String</td><td>cxxbridge::RustString</td><td></td></tr>
300//! <tr><td>&amp;str</td><td>cxxbridge::RustStr</td><td></td></tr>
301//! <tr><td><a href="https://docs.rs/cxx/0.0/cxx/struct.CxxString.html">CxxString</a></td><td>std::string</td><td><sup><i>cannot be passed by value</i></sup></td></tr>
302//! <tr><td>Box&lt;T&gt;</td><td>cxxbridge::RustBox&lt;T&gt;</td><td><sup><i>cannot hold opaque C++ type</i></sup></td></tr>
303//! <tr><td><a href="https://docs.rs/cxx/0.0/cxx/struct.UniquePtr.html">UniquePtr&lt;T&gt;</a></td><td>std::unique_ptr&lt;T&gt;</td><td><sup><i>cannot hold opaque Rust type</i></sup></td></tr>
304//! <tr><td></td><td></td><td></td></tr>
305//! </table>
306//!
307//! The C++ API of the `cxxbridge` namespace is defined by the
308//! *include/cxxbridge.h* file in https://github.com/dtolnay/cxx. You will need
309//! to include this header in your C++ code when working with those types.
310//!
311//! The following types are intended to be supported "soon" but are just not
312//! implemented yet. I don't expect any of these to be hard to make work but
313//! it's a matter of designing a nice API for each in its non-native language.
314//!
315//! <table>
316//! <tr><th>name in Rust</th><th>name in C++</th></tr>
317//! <tr><td>&amp;[T]</td><td></td></tr>
318//! <tr><td>Vec&lt;T&gt;</td><td></td></tr>
319//! <tr><td>BTreeMap&lt;K, V&gt;</td><td></td></tr>
320//! <tr><td>HashMap&lt;K, V&gt;</td><td></td></tr>
321//! <tr><td></td><td>std::vector&lt;T&gt;</td></tr>
322//! <tr><td></td><td>std::map&lt;K, V&gt;</td></tr>
323//! <tr><td></td><td>std::unordered_map&lt;K, V&gt;</td></tr>
324//! </table>
325
326#![deny(improper_ctypes)]
327#![allow(
328 clippy::large_enum_variant,
329 clippy::missing_safety_doc,
330 clippy::module_inception,
331 clippy::new_without_default,
332 clippy::or_fun_call,
333 clippy::ptr_arg,
334 clippy::toplevel_ref_arg,
335 clippy::transmute_ptr_to_ptr,
336 clippy::useless_let_if_seq
337)]
338
339mod cxx_string;
340mod error;
341mod gen;
342mod opaque;
343mod paths;
344mod rust_str;
345mod rust_string;
346mod syntax;
347mod unique_ptr;
348mod unwind;
349
350pub use crate::cxx_string::CxxString;
351pub use crate::unique_ptr::UniquePtr;
352pub use cxxbridge_macro::bridge;
353
354// Not public API.
355#[doc(hidden)]
356pub mod private {
357 pub use crate::opaque::Opaque;
358 pub use crate::rust_str::RustStr;
359 pub use crate::rust_string::RustString;
360 pub use crate::unique_ptr::UniquePtrTarget;
361 pub use crate::unwind::catch_unwind;
362}
363
364use crate::error::Result;
365use std::fs;
366use std::io::{self, Write};
367use std::path::Path;
368use std::process;
369
370/// The CXX code generator for constructing and compiling C++ code.
371///
372/// This is intended to be used from Cargo build scripts to execute CXX's
373/// C++ code generator, set up any additional compiler flags depending on
374/// the use case, and make the C++ compiler invocation.
375///
376/// <br>
377///
378/// # Example
379///
380/// Example of a canonical Cargo build script that builds a CXX bridge:
381///
382/// ```no_run
383/// // build.rs
384///
385/// fn main() {
386/// cxx::Build::new()
387/// .bridge("src/main.rs")
388/// .file("../demo-cxx/demo.cc")
389/// .flag("-std=c++11")
390/// .compile("cxxbridge-demo");
391///
392/// println!("cargo:rerun-if-changed=src/main.rs");
393/// println!("cargo:rerun-if-changed=../demo-cxx/demo.h");
394/// println!("cargo:rerun-if-changed=../demo-cxx/demo.cc");
395/// }
396/// ```
397///
398/// A runnable working setup with this build script is shown in the
399/// *demo-rs* and *demo-cxx* directories of
400/// [https://github.com/dtolnay/cxx](https://github.com/dtolnay/cxx).
401///
402/// <br>
403///
404/// # Alternatives
405///
406/// For use in non-Cargo builds like Bazel or Buck, CXX provides an
407/// alternate way of invoking the C++ code generator as a standalone command
408/// line tool. The tool is packaged as the `cxxbridge-cmd` crate.
409///
410/// ```bash
411/// $ cargo install cxxbridge-cmd # or build it from the repo
412///
413/// $ cxxbridge src/main.rs --header > path/to/mybridge.h
414/// $ cxxbridge src/main.rs > path/to/mybridge.cc
415/// ```
416#[must_use]
417pub struct Build {
418 _private: (),
419}
420
421impl Build {
422 /// Begin with a [`cc::Build`] in its default configuration.
423 pub fn new() -> Self {
424 Build { _private: () }
425 }
426
427 /// This returns a [`cc::Build`] on which you should continue to set up
428 /// any additional source files or compiler flags, and lastly call its
429 /// [`compile`] method to execute the C++ build.
430 ///
431 /// [`compile`]: https://docs.rs/cc/1.0.49/cc/struct.Build.html#method.compile
432 #[must_use]
433 pub fn bridge(&self, rust_source_file: impl AsRef<Path>) -> cc::Build {
434 match try_generate_bridge(rust_source_file.as_ref()) {
435 Ok(build) => build,
436 Err(err) => {
437 let _ = writeln!(io::stderr(), "\n\ncxxbridge error: {}\n\n", err);
438 process::exit(1);
439 }
440 }
441 }
442}
443
444fn try_generate_bridge(rust_source_file: &Path) -> Result<cc::Build> {
445 let header = gen::do_generate_header(rust_source_file);
446 let header_path = paths::out_with_extension(rust_source_file, ".h")?;
447 fs::create_dir_all(header_path.parent().unwrap())?;
448 fs::write(&header_path, header)?;
449 paths::symlink_header(&header_path, rust_source_file);
450
451 let bridge = gen::do_generate_bridge(rust_source_file);
452 let bridge_path = paths::out_with_extension(rust_source_file, ".cc")?;
453 fs::write(&bridge_path, bridge)?;
454 let mut build = paths::cc_build();
455 build.file(&bridge_path);
456
457 Ok(build)
458}