| # Inline C++ |
| |
| Crubit allows Rust code to call C++ libraries directly through generated |
| bindings. However, some C++ interfaces cannot yet be automatically bound by |
| Crubit, such as function templates and preprocessor macros. |
| |
| Crubit provides two macros, `inline_cpp!` and `global_cpp!`, that allow you to |
| embed C++ declarations and expressions directly inside your Rust source files. |
| This lets you call unsupported C++ code without having to write separate C++ |
| wrapper libraries. |
| |
| ## How to use Embedded C++ {#introduction} |
| |
| Embedded C++ uses two macros depending on scope: |
| |
| * **`global_cpp!`**: Used at module scope (outside functions) to declare C++ |
| `#include` headers, structs, namespaces, and helper functions. |
| * **`inline_cpp!`**: Used inside Rust functions to define and call an inline |
| C++ expression. |
| |
| You can use embedded C++ in two ways: |
| |
| 1. **In a `rust_api_from_cpp` target (Recommended)**: When extending the |
| generated Rust bindings of an existing C++ library using custom Rust source |
| files (`additional_rust_srcs_for_crubit_bindings`). |
| 2. **In a `rust_library_with_embedded_cpp` target**: When authoring a |
| standalone Rust library with embedded C++ without an underlying |
| `cc_library`. |
| |
| ### 1. Using embedded C++ in a `rust_api_from_cpp` target {#rust_api_from_cpp} |
| |
| If you maintain a `cc_library` and want to provide hand-written Rust helper |
| methods or trait implementations alongside your generated Crubit API, you can |
| pass custom Rust source files directly to `rust_api_from_cpp` via `srcs`. |
| |
| Given a C++ header containing a template method that cannot be natively bound by |
| Crubit: |
| |
| ```cpp |
| // widget.h |
| namespace widget { |
| class Widget { |
| public: |
| template <typename T = void> |
| static std::unique_ptr<Widget> Create(absl::string_view name); |
| private: |
| Widget(absl::string_view name); |
| }; |
| } // namespace widget |
| ``` |
| |
| You can define the `BUILD` targets as follows: |
| |
| ```python |
| load( |
| "//rs_bindings_from_cc/bazel_support:rust_api_from_cpp.bzl", |
| "rust_api_from_cpp", |
| ) |
| |
| cc_library( |
| name = "widget", |
| hdrs = ["widget.h"], |
| srcs = ["widget.cc"], |
| aspect_hints = [":widget_rust.hint"], |
| deps = [ |
| "@abseil-cpp//absl/strings", |
| ], |
| ) |
| |
| rust_api_from_cpp( |
| name = "widget_rust", |
| cpp_target = ":widget", |
| root_namespaces = ["widget"], |
| srcs = ["widget_custom.rs"], |
| ) |
| ``` |
| |
| Inside `widget_custom.rs`, you can use `inline_cpp!` to bridge the C++ template |
| factory method directly into the crate's public API: |
| |
| ```rust |
| // widget_custom.rs (compiled directly into the widget_rust crate) |
| use crubit_support::inline_cpp; |
| |
| impl Widget { |
| /// Ergonomic Rust wrapper over a C++ factory function template. |
| pub fn new_with_name(name: &str) -> cpp_std::unique_ptr<Widget> { |
| let make_widget = inline_cpp! { |
| (rs_std::StrRef name) -> std::unique_ptr<widget::Widget> { |
| return widget::Widget::Create(name.to_string_view()); |
| } |
| }; |
| make_widget(name) |
| } |
| } |
| ``` |
| |
| ### 2. Using embedded C++ in a standalone `rust_library` target {#build} |
| |
| To write a standalone Rust library that uses embedded C++ without an underlying |
| `cc_library`, define your target with `rust_library_with_embedded_cpp`: |
| |
| ``` |
| {{ #include ../../examples/cpp/inline_cpp/BUILD }} |
| ``` |
| <!-- symbol:greeting_lib --> |
| |
| |
| The `deps_of_cc_library` attribute lists the C++ libraries that provide the |
| headers and symbols used in your embedded C++ snippets. For a complete runnable |
| example, see |
| [examples/cpp/inline_cpp/](https://github.com/google/crubit/tree/main/examples/cpp/inline_cpp/). |
| |
| ## Writing Embedded C++ {#writing_code} |
| |
| ### Declaring Headers with `global_cpp!` {#global_cpp} |
| |
| Use `global_cpp!` at module scope (outside functions, including within nested |
| submodules) to specify `#include` headers and define supporting C++ types: |
| |
| ``` |
| {{ #include ../../examples/cpp/inline_cpp/headers.rs }} |
| ``` |
| <!-- content:^\s*global_cpp!\s*\{[\s\S]*?^\} --> |
| |
| |
| > **NOTE:** All `global_cpp!` declarations across a crate (including those |
| > inside nested submodules) are combined into a single C++ companion header. Any |
| > `inline_cpp!` block in the crate can access types declared in `global_cpp!`, |
| > regardless of which Rust module defined them. Standard Rust visibility (`pub`, |
| > `pub(crate)`) governs access to the wrapping Rust functions. |
| |
| ### Calling C++ with `inline_cpp!` {#inline_cpp} |
| |
| Inside a Rust function, `inline_cpp!` defines an inline C++ expression. The |
| syntax requires a C++ parameter list, a return type arrow `->`, and the C++ |
| function body. |
| |
| For example, calling a C++ standard library math function: |
| |
| ``` |
| {{ #include ../../examples/cpp/inline_cpp/math.rs }} |
| ``` |
| <!-- function:compute_hypotenuse --> |
| |
| |
| Or calling an Abseil string utility: |
| |
| ``` |
| {{ #include ../../examples/cpp/inline_cpp/greeting.rs }} |
| ``` |
| <!-- function:format_greeting --> |
| |
| |
| `inline_cpp!` produces a callable Rust closure. The parameter and return types |
| of the generated closure match the corresponding Rust types generated by Crubit |
| (for example, `i32` for `int`, and `&Point` for `const Point&`). |
| |
| ## Passing types between Rust and C++ {#types} |
| |
| ### Primitive types {#primitives} |
| |
| [Primitive numeric types and booleans](/docs/types/primitive.md) |
| pass directly by value between Rust and C++ (for example, `int` in C++ |
| corresponds to `i32` in Rust, and `double` corresponds to `f64`). |
| |
| ### References and pointers {#references} |
| |
| You can pass Rust references to C++ as `const T&` or raw pointers: |
| |
| ``` |
| {{ #include ../../examples/cpp/inline_cpp/point.rs }} |
| ``` |
| <!-- function:get_distance --> |
| |
| |
| > **WARNING:** Standard Rust lifetime rules apply. C++ code must not store or |
| > keep references or pointers after the `inline_cpp!` call returns. |
| |
| ## Common Use Cases {#use_cases} |
| |
| ### Calling and Defining C++ Templates {#templates} |
| |
| Crubit does not automatically generate Rust bindings for uninstantiated C++ |
| templates. With embedded C++, you can: |
| |
| 1. **Instantiate library templates**: Call templated functions or classes from |
| included C++ headers inside `inline_cpp!`: |
| |
| ```live-snippet |
| cs/file:examples/cpp/inline_cpp/clamp.rs function:clamp_value |
| ``` |
| |
| 2. **Define helper templates in `global_cpp!`**: Define C++ template helpers at |
| module scope, then instantiate them with concrete types in `inline_cpp!`: |
| |
| ```live-snippet |
| cs/file:examples/cpp/inline_cpp/math_helper.rs function:multiply_ints |
| ``` |
| |
| ## Common Errors {#errors} |
| |
| `inline_cpp!` uses Crubit under the hood, so it can produce any error Crubit |
| could produce when bridging types. |
| |
| ### Missing parameter list or return type |
| |
| `inline_cpp!` requires an explicit parameter list and return type arrow. If your |
| C++ snippet takes no arguments or returns nothing, specify `() -> void`: |
| |
| ```rust |
| let no_args_fn = inline_cpp! { |
| () -> void { |
| DoWork(); |
| } |
| }; |
| no_args_fn(); |
| ``` |
| |
| Omitting the parameter list or return type results in an extraction syntax |
| error: |
| |
| ``` |
| error: inline_cpp! block must start with a parameter list `(args)` |
| --> src/lib.rs:10:5 |
| | |
| 10 | inline_cpp! { DoWork(); } |
| | ^^^^^^^^^^^^^^^^^^^^^^^^^ |
| ``` |
| |
| ### Unsupported features and syntax limitations {#unsupported} |
| |
| * **Unmatched braces**: All `{}` braces inside embedded C++ blocks must be |
| properly balanced. If a brace is unmatched (such as in complex preprocessor |
| macros or unclosed string literals), the extraction preprocessor reports an |
| unmatched delimiter error: |
| |
| ``` |
| error: Unmatched delimiter starting at src/lib.rs:10: Context around open brace: |
| inline_cpp! { (int x) -> int { if (x > 0) { return x; } |
| ``` |