blob: a0d1d452c2dc666d6b37842a2ea2a56bfcb0bfcc [file] [log] [blame] [view]
# Overview of Crubit's C++/Rust bindings
## Motivation for automating generation of FFI bindings
To call a C++ function from Rust, one can manually declare the foreign function
interface (FFI) as follows:
```rust
extern "C" {
fn cpp_multiplication_function(x: i32, y: i32) -> i64;
}
```
This works, but has some disadvantages:
* The function signature in Rust's `extern` declaration needs to match the
signature of the actual C++ function (e.g. we can't say `x: i16` when the
C++ parameter is `int32_t x` - doing this would lead to Undefined Behavior).
Keeping those function signatures in sync can be error prone.
* Rust does not support the C++ calling convention or any C++ features not
present in C - this means that bindings for some functions may require an
extra "thunk" (an extra function that exposes C ABI and forwards calls to
the target function).
* It is possible to manually replicate in C++ the memory layout and ABI of
`#[repr(C)]` and `#[repr(transparent)]` structs, but not the
implementation-defined layout of `#[repr(Rust)]` structs.
Similar disadvantages exist when manually setting up FFI bindings for calling a
Rust function from C++. Automating generation of FFI bindings with Crubit can
help alleviate the disadvantages above.
## C++ bindings for Rust APIs
Crubit can take a Rust crate and create a C++ library which exposes Rust
functions and types to C++ callers.
Let's walk through the
[`cc_bindings_from_rs_basics`](../../../examples/cc_bindings_from_rs_basics/README.md)
example. In the example, Bazel is instructed to provide C++ bindings for a Rust
library:
```
load(
"//cc_bindings_from_rs/bazel_support:cc_bindings_from_rust_rule.bzl",
"cc_bindings_from_rust",
)
rust_library(
name = "example_crate",
srcs = ["example.rs"],
)
# This declares an "example_crate_cc_api" target that provides Crubit-generated
# C++ bindings for the Rust crate behind the `":example_crate"` target.
cc_bindings_from_rust(
name = "example_crate_cc_api",
crate = ":example_crate",
)
```
With the above, C++ code (e.g. `cc_binary`, `cc_library`, `cc_test`, etc.) can
just list the bindings in its `deps`:
```
cc_binary(
name = "main",
srcs = ["main.cc"],
# Declaring a dependency on C++ bindings for calling into the Rust
# `example` library:
deps = [ ":example_crate_cc_api" ],
)
```
The scaffolding above will let C++ call into Rust as follows:
```rust
// example.rs:
pub fn add_two_integers(x: i32, y: i32) -> i32 { x + y }
```
```c++
// main.cc:
// The generated bindings are in a header at the same path as the `rust_library`,
// and with the name that follows the `<crate name>_cc_api.h` pattern:
#include "examples/cc_bindings_from_rs_basics/example_crate_cc_api.h"
int main(int argc, char* argv[]) {
// The generated bindings are in a namespace with the same name as the
// target crate:
int32_t sum = example_crate::add_two_integers(2, 2);
// ...
}
```
If needed, one can find and inspect the generated bindings using:
```sh
$ find "$(bazel info bazel-bin)/examples/cc_bindings_from_rs_basics/" \
-name example_crate_cc_api.h
```
Inspecting the generated file may be useful to look at comments that Crubit
leaves behind when it is unable to generate bindings for a given Rust API -
e.g.:
```c++
// Error generating bindings for `reinterpret_cast` defined at path/lib.rs;l=123:
// Error formatting function name:
// `reinterpret_cast` is a C++ reserved keyword and can't be used as a C++ identifier.
```
TODO: Provide integration with Chromium/GN and provide GN-oriented examples.
## Highlights
For a comprehensive description of the generated FFI bindings, please see the
"FFI Bindings Reference". The list below highlights a few aspects of the FFI
bindings that Crubit generates:
* Crubit can generate FFI bindings for C++ and Rust functions even if they
don't follow the `extern "C"` calling convention.
* Crubit can replicate the memory layout of most C++ and Rust structs. This
includes non-`#[repr(C)]` Rust structs and inheritance hierarchy of C++
classes.
* Crubit can generate bindings for APIs that use types from other libraries.
For example, if a function provided by `foo` library returns a struct
defined by a separate `bar` library, then `foo` bindings will automatically
reuse bindings generated for `bar`.
* Crubit supports all the usual ways to pass function parameters or return
values. In particular, structs can be passed by reference or by value.
Values from either language can be moved across stack and heap memory on
either side of the FFI boundary. This includes C++ structs with non-trivial,
user-defined move constructors.
* Crubit supports generating bindings for specific instantiations of arbitrary
C++ class templates. In particular, bindings for `std::string_view` from C++
standard library can be generated even though `rs_bindings_from_cc` has
minimal knowledge about this specific type (except for instructing
`rs_bindings_from_cc` to include `From` trait implementations from
`crubit/support/cc_std/string_view.rs`).
--------------------------------------------------------------------------------
TODO: Help users make an informed decision when to use Crubit rather than other
FFI tools. (This probably should become a separate sub-chapter of the overview.
Some raw information can be currently found in the [design.md](../../design.md)
doc.)