cxx

repository·master·Indexed 27 days ago

https://github.com/dtolnay/cxx

A safe Foreign Function Interface (FFI) mechanism for interoperability between Rust and C++. It uses paired code generators to ensure synchronization between languages, providing strong safety guarantees, ABI correctness, and support for shared structs, opaque types, and complex type passing. Version 1.0.198.

Tokens
23.2K
Snippets
64
Records
101
Agent score
91%

What's inside cxx

  1. Core concepts of CXX interop

    master

    CXX provides safe FFI between Rust and C++ by allowing developers to write idiomatic code on both sides of the boundary.

    Key features include:

    • Idiomatic code: Rust code looks like normal Rust, and C++ code looks like normal C++, avoiding the need for manual, error-prone C-style FFI glue.
    • Type Safety: Uses an expressive system of opaque types, shared types, and standard library type bindings.
    • Ownership and Borrowing: Enables API design that captures the proper ownership and borrowing contracts of the interface across the language boundary.
  2. Compare CXX with bindgen and cbindgen

    master

    When deciding between CXX and other FFI tools, consider the nature of your API:

    • Use bindgen or cbindgen if your code is "effectively C". These tools are designed to work with C-compatible signatures (extern "C", primitives, raw pointers, and C-compatible structs).
    • Use CXX if you want to interact with idiomatic Rust and C++ APIs. CXX allows you to bridge languages using their native strengths (ownership, vectors, strings, etc.) rather than forcing both sides down to a C-compatible layer.

    Key Differences:

    • bindgen/cbindgen: Often require building a C-compatible wrapper around C++ or Rust code. This involves manual translation of signatures and often results in using unsafe code to manage raw pointers and primitives.
    • CXX: Acts as a replacement for extern "C" with higher fidelity. It captures the language boundary more accurately, supporting common standard library types and reducing the need to drop down to C-style primitives.
  3. Use the CXX C++ code generator

    master

    The CXX C++ code generator provides two primary public frontends for integrating C++ code generation into your workflow:

    1. Command-line application: A binary located in the cmd directory for direct execution.
    2. Build script library: A library located in the build directory intended to be used within a build.rs file for automated integration during the Rust build process.

    Note: There is also a lib frontend for embedding CXX into higher-level code generators, but its use is not yet recommended for general purposes.

  4. Understand CXX safety guarantees and design philosophy

    master

    CXX is designed to be a restrictive and opinionated FFI library to provide strong safety guarantees. Unlike manual extern "C" blocks in Rust, which are prone to signature mismatches, CXX uses paired code generators to ensure both the Rust and C++ sides of the FFI boundary are synchronized.

    Key safety features include:

    • Signature Visibility: The generators ensure Rust knows exactly what is on the C++ side.
    • Static Analysis: Prevents passing types by value from C++ to Rust if they contain internal pointers that would be invalidated by Rust's move semantics.
    • ABI Correctness: Automatically handles zero-cost workarounds for cases where Rust and C++ structs have identical layouts but different ABIs (addressing known issues in tools like bindgen).
    • Template Support: Uses Rust traits to connect Rust types to C++ template instantiations (e.g., UniquePtr<T>).
  5. Use the CXX Rust code generator via the `cxx` crate

    master
    The cxxbridge-macro crate is the internal procedural macro used for Rust code generation. End-users should not depend on this crate directly. Instead, you should use the re-exported macros provided by the main cxx crate.
  6. Initialize a CXX project with Cargo

    master

    To start a new project using CXX, create a standard Cargo project and add cxx to your dependencies in Cargo.toml.

    # Cargo.toml
    [package]
    name = "cxx-demo"
    version = "0.1.0"
    edition = "2024"
    
    [dependencies]
    cxx = "1.0"
    mkdir cxx-demo
    cd cxx-demo
    cargo init
  7. Bind C++ types and functions to Rust using `extern "C++"`

    master

    Use the extern "C++" block within a #[cxx::bridge] module to bind C++ functionality to Rust. This allows you to:

    • Bind opaque C++ types
    • Bind C++ functions
    • Bind C++ member functions
    • Share opaque type definitions across multiple bridge modules or different crates
    • Use bindgen-generated data structures across a CXX bridge
    • Request specific glue code emission in a specific bridge module in a way that is compatible with Rust orphan rules
  8. Define shared structs and enums in CXX

    master

    Shared types allow both Rust and C++ to access the internals of a type. Unlike opaque types, shared types can be passed and returned by value.

    Constraints:

    • For enums, only C-like (unit) variants are supported.
    • CXX automatically handles topological sorting and forward declarations, so the order of definition in the #[cxx::bridge] module does not matter.
    • If a shared struct has generic lifetime parameters, they are not represented on the C++ side; C++ code must manually manage borrowed data safety.
    #[cxx::bridge]
    mod ffi {
        struct PlayingCard {
            suit: Suit,
            value: u8,  // A=1, J=11, Q=12, K=13
        }
    
        enum Suit {
            Clubs,
            Diamonds,
            Hearts,
            Spades,
        }
    
        unsafe extern "C++" {
            fn deck() -> Vec<PlayingCard>;
            fn sort(cards: &mut Vec<PlayingCard>);
        }
    }
  9. Use opaque types for C++ classes

    master
    To avoid issues with C++ move-constructors or internal references that don't match Rust's memory model, treat C++ classes as opaque types. In the #[cxx::bridge] block, declare the type using type Name;. Opaque types can only be manipulated in Rust via indirection, such as a reference &, a Box, or a UniquePtr (the Rust binding for std::unique_ptr).
  10. Inspect generated code

    master

    You can inspect the code generated by CXX for both languages using the following commands:

    • Rust code: Requires cargo-expand. Run cargo expand --manifest-path <path-to-toml>.
    • C++ code: Use the cxxbridge-cmd tool via cargo run from the bridge command directory.
    # run Rust code generator and print to stdout
    # (requires https://github.com/dtolnay/cargo-expand)
    $ cargo expand --manifest-path demo/Cargo.toml
    
    # run C++ code generator and print to stdout
    $ cargo run --manifest-path bridge/cmd/Cargo.toml -- demo/src/main.rs
  11. Compile C++ code using build.rs

    master

    Use a build.rs script to invoke the cxx_build code generator. You must specify the Rust file containing the #[cxx::bridge] definition and the paths to the C++ source files to be compiled.

    // build.rs
    fn main() {
        cxx_build::bridge("src/main.rs")
            .file("src/blobstore.cc")
            .std("c++14")
            .compile("cxx-demo");
    
        println!("cargo:rerun-if-changed=src/blobstore.cc");
        println!("cargo:rerun-if-changed=include/blobstore.h");
    }
  12. Handle errors across the language boundary

    master

    Manage fallibility between Rust and C++ using the bridge module. Capabilities include:

    • Representing fallibility on the language boundary
    • Accessing Rust error messages from C++
    • Customizing the set of caught C++ exceptions and their conversion into Rust error messages