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