To call a C++ function from Rust, one can manually declare the foreign function interface (FFI) as follows:
extern "C" { fn cpp_multiplication_function(x: i32, y: i32) -> i64; }
This works, but has some disadvantages:
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.#[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.
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 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:
// example.rs: pub fn add_two_integers(x: i32, y: i32) -> i32 { x + y }
// 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:
$ 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.:
// 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.
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:
extern "C" calling convention.#[repr(C)] Rust structs and inheritance hierarchy of C++ classes.foo library returns a struct defined by a separate bar library, then foo bindings will automatically reuse bindings generated for bar.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 doc.)