1
0
mirror of https://github.com/google/comprehensive-rust.git synced 2025-04-25 16:54:32 +02:00

Add CXX tutorial (#1392)

Add a number of slides that cover most of CXX's functionality and
demonstrate how it can be used.

Fixes #823.

---------

Co-authored-by: Martin Geisler <mgeisler@google.com>
This commit is contained in:
Nicole L 2023-11-06 16:34:29 -08:00 committed by GitHub
parent 1720b80e7e
commit ca61ca4f57
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 768 additions and 25 deletions

77
Cargo.lock generated
View File

@ -286,6 +286,16 @@ version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd7cc57abe963c6d3b9d8be5b06ba7c8957a930305ca90304f24ef040aa6f961"
[[package]]
name = "codespan-reporting"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e"
dependencies = [
"termcolor",
"unicode-width",
]
[[package]]
name = "colorchoice"
version = "1.0.0"
@ -389,12 +399,64 @@ dependencies = [
"syn 2.0.37",
]
[[package]]
name = "cxx"
version = "1.0.109"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c390c123d671cc547244943ecad81bdaab756c6ea332d9ca9c1f48d952a24895"
dependencies = [
"cc",
"cxxbridge-flags",
"cxxbridge-macro",
"link-cplusplus",
]
[[package]]
name = "cxx-build"
version = "1.0.109"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "00d3d3ac9ffb900304edf51ca719187c779f4001bb544f26c4511d621de905cf"
dependencies = [
"cc",
"codespan-reporting",
"once_cell",
"proc-macro2",
"quote",
"scratch",
"syn 2.0.37",
]
[[package]]
name = "cxxbridge-flags"
version = "1.0.109"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94415827ecfea0f0c74c8cad7d1a86ddb3f05354d6a6ddeda0adee5e875d2939"
[[package]]
name = "cxxbridge-macro"
version = "1.0.109"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e33dbbe9f5621c9247f97ec14213b04f350bff4b6cebefe834c60055db266ecf"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.37",
]
[[package]]
name = "data-encoding"
version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308"
[[package]]
name = "demo"
version = "0.0.0"
dependencies = [
"cxx",
"cxx-build",
]
[[package]]
name = "derive_more"
version = "0.99.17"
@ -1027,6 +1089,15 @@ version = "0.2.148"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9cdc71e17332e86d2e1d38c1f99edcb6288ee11b815fb1a4b049eaa2114d369b"
[[package]]
name = "link-cplusplus"
version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9d240c6f7e1ba3a28b0249f774e6a9dd0175054b52dfbb61b16eb8505c3785c9"
dependencies = [
"cc",
]
[[package]]
name = "linux-raw-sys"
version = "0.4.8"
@ -1862,6 +1933,12 @@ dependencies = [
"tendril",
]
[[package]]
name = "scratch"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a3cf7c11c38cb994f3d40e8a8cde3bbd1f72a435e4c49e85d6553d8312306152"
[[package]]
name = "security-framework"
version = "2.9.2"

View File

@ -6,5 +6,6 @@ members = [
"src/bare-metal/useful-crates/allocator-example",
"src/bare-metal/useful-crates/zerocopy-example",
"src/exercises/concurrency/chat-async",
"third_party/cxx/blobstore",
]
resolver = "2"

View File

@ -207,7 +207,19 @@
- [With C](android/interoperability/with-c.md)
- [Calling C with Bindgen](android/interoperability/with-c/bindgen.md)
- [Calling Rust from C](android/interoperability/with-c/rust.md)
- [With C++](android/interoperability/cpp.md)
- [With C++](android/interoperability/cpp.md))
- [The Bridge Module](android/interoperability/cpp/bridge.md)
- [Rust Bridge](android/interoperability/cpp/rust-bridge.md)
- [Generated C++](android/interoperability/cpp/generated-cpp.md)
- [C++ Bridge](android/interoperability/cpp/cpp-bridge.md)
- [Shared Types](android/interoperability/cpp/shared-types.md)
- [Shared Enums](android/interoperability/cpp/shared-enums.md)
- [Rust Error Handling](android/interoperability/cpp/rust-result.md)
- [C++ Error Handling](android/interoperability/cpp/cpp-exception.md)
- [Additional Types](android/interoperability/cpp/type-mapping.md)
- [Building for Android: C++](android/interoperability/cpp/android-build-cpp.md)
- [Building for Android: Genrules](android/interoperability/cpp/android-cpp-genrules.md)
- [Building for Android: Rust](android/interoperability/cpp/android-build-rust.md)
- [With Java](android/interoperability/java.md)
- [Exercises](exercises/android/morning.md)

View File

@ -7,28 +7,4 @@ The overall approach looks like this:
<img src="cpp/overview.svg">
See the [CXX tutorial][2] for an full example of using this.
<details>
- At this point, the instructor should switch to the [CXX tutorial][2].
- Walk the students through the tutorial step by step.
- Highlight how CXX presents a clean interface without unsafe code in _both languages_.
- Show the correspondence between [Rust and C++ types](https://cxx.rs/bindings.html):
- Explain how a Rust `String` cannot map to a C++ `std::string`
(the latter does not uphold the UTF-8 invariant). Show that
despite being different types, `rust::String` in C++ can be
easily constructed from a C++ `std::string`, making it very
ergonomic to use.
- Explain that a Rust function returning `Result<T, E>` becomes a
function which throws a `E` exception in C++ (and vice versa).
</details>
[1]: https://cxx.rs/
[2]: https://cxx.rs/tutorial.html

View File

@ -0,0 +1,31 @@
# Building in Android
Create a `cc_library_static` to build the C++ library, including the CXX
generated header and source file.
```javascript
cc_library_static {
name: "libcxx_test_cpp",
srcs: ["cxx_test.cpp"],
generated_headers: [
"cxx-bridge-header",
"libcxx_test_bridge_header"
],
generated_sources: ["libcxx_test_bridge_code"],
}
```
<details>
* Point out that `libcxx_test_bridge_header` and `libcxx_test_bridge_code` are
the dependencies for the CXX-generated C++ bindings. We'll show how these are
setup on the next slide.
* Note that you also need to depend on the `cxx-bridge-header` library in order
to pull in common CXX definitions.
* Full docs for using CXX in Android can be found in [the Android docs]. You may
want to share that link with the class so that students know where they can
find these instructions again in the future.
[the Android docs]: https://source.android.com/docs/setup/build/rust/building-rust-modules/android-rust-patterns#rust-cpp-interop-using-cxx
</details>

View File

@ -0,0 +1,12 @@
# Building in Android
Create a `rust_binary` that depends on `libcxx` and your `cc_library_static`.
```javascript
rust_binary {
name: "cxx_test",
srcs: ["lib.rs"],
rustlibs: ["libcxx"],
static_libs: ["libcxx_test_cpp"],
}
```

View File

@ -0,0 +1,35 @@
# Building in Android
Create two genrules: One to generate the CXX header, and one to generate the CXX
source file. These are then used as inputs to the `cc_library_static`.
```javascript
// Generate a C++ header containing the C++ bindings
// to the Rust exported functions in lib.rs.
genrule {
name: "libcxx_test_bridge_header",
tools: ["cxxbridge"],
cmd: "$(location cxxbridge) $(in) --header > $(out)",
srcs: ["lib.rs"],
out: ["lib.rs.h"],
}
// Generate the C++ code that Rust calls into.
genrule {
name: "libcxx_test_bridge_code",
tools: ["cxxbridge"],
cmd: "$(location cxxbridge) $(in) > $(out)",
srcs: ["lib.rs"],
out: ["lib.rs.cc"],
}
```
<details>
* The `cxxbridge` tool is a standalone tool that generates the C++ side of the
bridge module. It is included in Android and available as a Soong tool.
* By convention, if your Rust source file is `lib.rs` your header file will be
named `lib.rs.h` and your source file will be named `lib.rs.cc`. This naming
convention isn't enforced, though.
</details>

View File

@ -0,0 +1,24 @@
# The Bridge Module
CXX relies on a description of the function signatures that will be exposed from
each language to the other. You provide this description using extern blocks in
a Rust module annotated with the `#[cxx::bridge]` attribute macro.
```rust,ignore
{{#include ../../../../third_party/cxx/blobstore/src/main.rs:bridge}}
```
<details>
* The bridge is generally declared in an `ffi` module within your crate.
* From the declarations made in the bridge module, CXX will generate matching
Rust and C++ type/function definitions in order to expose those items to both
languages.
* To view the generated Rust code, use [cargo-expand] to view the expanded proc
macro. For most of the examples you would use `cargo expand ::ffi` to expand
just the `ffi` module (though this doesn't apply for Android projects).
* To view the generated C++ code, look in `target/cxxbridge`.
[cargo-expand]: https://github.com/dtolnay/cargo-expand
</details>

View File

@ -0,0 +1,52 @@
# C++ Bridge Declarations
```rust,ignore
#[cxx::bridge]
mod ffi {
{{#include ../../../../third_party/cxx/blobstore/src/main.rs:cpp_bridge}}
}
```
Results in (roughly) the following Rust:
```rust,ignore
#[repr(C)]
pub struct BlobstoreClient {
_private: ::cxx::private::Opaque,
}
pub fn new_blobstore_client() -> ::cxx::UniquePtr<BlobstoreClient> {
extern "C" {
#[link_name = "org$blobstore$cxxbridge1$new_blobstore_client"]
fn __new_blobstore_client() -> *mut BlobstoreClient;
}
unsafe { ::cxx::UniquePtr::from_raw(__new_blobstore_client()) }
}
impl BlobstoreClient {
pub fn put(&self, parts: &mut MultiBuf) -> u64 {
extern "C" {
#[link_name = "org$blobstore$cxxbridge1$BlobstoreClient$put"]
fn __put(
_: &BlobstoreClient,
parts: *mut ::cxx::core::ffi::c_void,
) -> u64;
}
unsafe {
__put(self, parts as *mut MultiBuf as *mut ::cxx::core::ffi::c_void)
}
}
}
// ...
```
<details>
* The programmer does not need to promise that the signatures they have typed in
are accurate. CXX performs static assertions that the signatures exactly
correspond with what is declared in C++.
* `unsafe extern` blocks allow you to declare C++ functions that are safe to
call from Rust.
</details>

View File

@ -0,0 +1,16 @@
# C++ Error Handling
```rust,ignore
{{#include ../../../../third_party/cxx/book/snippets.rs:cpp_exception}}
```
<details>
* C++ functions declared to return a `Result` will catch any thrown exception on
the C++ side and return it as an `Err` value to the calling Rust function.
* If an exception is thrown from an extern "C++" function that is not declared
by the CXX bridge to return `Result`, the program calls C++'s
`std::terminate`. The behavior is equivalent to the same exception being
thrown through a `noexcept` C++ function.
</details>

View File

@ -0,0 +1,25 @@
# Generated C++
```rust,ignore
#[cxx::bridge]
mod ffi {
{{#include ../../../../third_party/cxx/blobstore/src/main.rs:rust_bridge}}
}
```
Results in (roughly) the following C++:
```cpp
struct MultiBuf final : public ::rust::Opaque {
~MultiBuf() = delete;
private:
friend ::rust::layout;
struct layout {
static ::std::size_t size() noexcept;
static ::std::size_t align() noexcept;
};
};
::rust::Slice<::std::uint8_t const> next_chunk(::org::blobstore::MultiBuf &buf) noexcept;
```

View File

@ -0,0 +1,16 @@
# Rust Bridge Declarations
```rust,ignore
{{#include ../../../../third_party/cxx/book/snippets.rs:rust_bridge}}
```
<details>
* Items declared in the `extern "Rust"` reference items that are in scope in the
parent module.
* The CXX code generator uses your `extern "Rust"` section(s) to produce a C++
header file containing the corresponding C++ declarations. The generated
header has the same path as the Rust source file containing the bridge, except
with a .rs.h file extension.
</details>

View File

@ -0,0 +1,17 @@
# Rust Error Handling
```rust,ignore
{{#include ../../../../third_party/cxx/book/snippets.rs:rust_result}}
```
<details>
* Rust functions that return `Result` are translated to exceptions on the C++
side.
* The exception thrown will always be of type `rust::Error`, which primarily
exposes a way to get the error message string. The error message will come
from the error type's `Display` impl.
* A panic unwinding from Rust to C++ will always cause the process to
immediately terminate.
</details>

View File

@ -0,0 +1,26 @@
# Shared Enums
```rust,ignore
{{#include ../../../../third_party/cxx/book/snippets.rs:shared_enums_bridge}}
```
Generated Rust:
```rust
{{#include ../../../../third_party/cxx/book/snippets.rs:shared_enums_rust}}
```
Generated C++:
```c++
{{#include ../../../../third_party/cxx/book/snippets.cc:shared_enums_cpp}}
```
<details>
* On the Rust side, the code generated for shared enums is actually a struct
wrapping a numeric value. This is because it is not UB in C++ for an enum
class to hold a value different from all of the listed variants, and our Rust
representation needs to have the same behavior.
</details>

View File

@ -0,0 +1,15 @@
# Shared Types
```rust,ignore
{{#include ../../../../third_party/cxx/book/snippets.rs:shared_types}}
```
<details>
* Only C-like (unit) enums are supported.
* A limited number of traits are supported for `#[derive()]` on shared types.
Corresponding functionality is also generated for the C++ code, e.g. if you
derive `Hash` also generates an implementation of `std::hash` for the
corresponding C++ type.
</details>

View File

@ -0,0 +1,26 @@
# Additional Types
| Rust Type | C++ Type |
|-------------------|----------------------|
| `String` | `rust::String` |
| `&str` | `rust::Str` |
| `CxxString` | `std::string` |
| `&[T]`/`&mut [T]` | `rust::Slice` |
| `Box<T>` | `rust::Box<T>` |
| `UniquePtr<T>` | `std::unique_ptr<T>` |
| `Vec<T>` | `rust::Vec<T>` |
| `CxxVector<T>` | `std::vector<T>` |
<details>
* These types can be used in the fields of shared structs and the arguments and
returns of extern functions.
* Note that Rust's `String` does not map directly to `std::string`. There are a
few reasons for this:
* `std::string` does not uphold the UTF-8 invariant that `String` requires.
* The two types have different layouts in memory and so can't be passed
directly between languages.
* `std::string` requires move constructors that don't match Rust's move
semantics, so a `std::string` can't be passed by value to Rust.
</details>

32
third_party/cxx/blobstore/Android.bp vendored Normal file
View File

@ -0,0 +1,32 @@
cc_library_static {
name: "blobstore_cpp",
srcs: ["src/blobstore.cc"],
generated_headers: [
"cxx-bridge-header",
"blobstore_bridge_header"
],
generated_sources: ["blobstore_bridge_code"],
}
genrule {
name: "blobstore_bridge_header",
tools: ["cxxbridge"],
cmd: "$(location cxxbridge) $(in) --header > $(out)",
srcs: ["src/main.rs"],
out: ["main.rs.h"],
}
genrule {
name: "blobstore_bridge_code",
tools: ["cxxbridge"],
cmd: "$(location cxxbridge) $(in) > $(out)",
srcs: ["src/main.rs"],
out: ["main.rs.cc"],
}
rust_binary {
name: "blobstore",
srcs: ["src/main.rs"],
rustlibs: ["libcxx"],
static_libs: ["blobstore_cpp"],
}

35
third_party/cxx/blobstore/BUILD vendored Normal file
View File

@ -0,0 +1,35 @@
load("@rules_cc//cc:defs.bzl", "cc_library")
load("@rules_rust//rust:defs.bzl", "rust_binary")
load("//tools/bazel:rust_cxx_bridge.bzl", "rust_cxx_bridge")
rust_binary(
name = "demo",
srcs = glob(["src/**/*.rs"]),
edition = "2021",
deps = [
":blobstore-sys",
":bridge",
"//:cxx",
],
)
rust_cxx_bridge(
name = "bridge",
src = "src/main.rs",
deps = [":blobstore-include"],
)
cc_library(
name = "blobstore-sys",
srcs = ["src/blobstore.cc"],
deps = [
":blobstore-include",
":bridge/include",
],
)
cc_library(
name = "blobstore-include",
hdrs = ["include/blobstore.h"],
deps = ["//:core"],
)

15
third_party/cxx/blobstore/Cargo.toml vendored Normal file
View File

@ -0,0 +1,15 @@
[package]
name = "demo"
version = "0.0.0"
authors = ["David Tolnay <dtolnay@gmail.com>"]
description = "Toy project from https://github.com/dtolnay/cxx"
edition = "2021"
license = "MIT OR Apache-2.0"
publish = false
repository = "https://github.com/dtolnay/cxx"
[dependencies]
cxx = "1.0"
[build-dependencies]
cxx-build = "1.0"

12
third_party/cxx/blobstore/build.rs vendored Normal file
View File

@ -0,0 +1,12 @@
fn main() {
cxx_build::bridge("src/main.rs")
.file("src/blobstore.cc")
.flag_if_supported("-std=c++14")
.include(".")
.include("../../../target/cxxbridge/demo/src")
.compile("cxxbridge-demo");
println!("cargo:rerun-if-changed=src/main.rs");
println!("cargo:rerun-if-changed=src/blobstore.cc");
println!("cargo:rerun-if-changed=include/blobstore.h");
}

View File

@ -0,0 +1,32 @@
#pragma once
#include "rust/cxx.h"
#include <memory>
#include <set>
#include <unordered_map>
#include <string>
namespace org {
namespace blobstore {
struct MultiBuf;
struct BlobMetadata;
class BlobstoreClient {
public:
BlobstoreClient();
uint64_t put(MultiBuf &buf);
void tag(uint64_t blobid, rust::Str tag);
BlobMetadata metadata(uint64_t blobid) const;
private:
using Blob = struct {
std::string data;
std::set<std::string> tags;
};
std::unordered_map<uint64_t, Blob> blobs;
};
std::unique_ptr<BlobstoreClient> new_blobstore_client();
} // namespace blobstore
} // namespace org

View File

@ -0,0 +1,55 @@
#include "include/blobstore.h"
#include "main.rs.h"
#include <algorithm>
#include <functional>
namespace org {
namespace blobstore {
BlobstoreClient::BlobstoreClient() {}
// Upload a new blob and return a blobid that serves as a handle to the blob.
uint64_t BlobstoreClient::put(MultiBuf &buf) {
std::string contents;
// Traverse the caller's chunk iterator.
//
// In reality there might be sophisticated batching of chunks and/or parallel
// upload implemented by the blobstore's C++ client.
while (true) {
auto chunk = next_chunk(buf);
if (chunk.size() == 0) {
break;
}
contents.append(reinterpret_cast<const char *>(chunk.data()), chunk.size());
}
// Insert into map and provide caller the handle.
auto blobid = std::hash<std::string>{}(contents);
blobs[blobid] = {std::move(contents), {}};
return blobid;
}
// Add tag to an existing blob.
void BlobstoreClient::tag(uint64_t blobid, rust::Str tag) {
blobs[blobid].tags.emplace(tag);
}
// Retrieve metadata about a blob.
BlobMetadata BlobstoreClient::metadata(uint64_t blobid) const {
BlobMetadata metadata{};
auto blob = blobs.find(blobid);
if (blob != blobs.end()) {
metadata.size = blob->second.data.size();
std::for_each(blob->second.tags.cbegin(), blob->second.tags.cend(),
[&](auto &t) { metadata.tags.emplace_back(t); });
}
return metadata;
}
std::unique_ptr<BlobstoreClient> new_blobstore_client() {
return std::make_unique<BlobstoreClient>();
}
} // namespace blobstore
} // namespace org

69
third_party/cxx/blobstore/src/main.rs vendored Normal file
View File

@ -0,0 +1,69 @@
//! Example project demonstrating usage of CXX.
// ANCHOR: bridge
#[allow(unsafe_op_in_unsafe_fn)]
#[cxx::bridge(namespace = "org::blobstore")]
mod ffi {
// Shared structs with fields visible to both languages.
struct BlobMetadata {
size: usize,
tags: Vec<String>,
}
// ANCHOR: rust_bridge
// Rust types and signatures exposed to C++.
extern "Rust" {
type MultiBuf;
fn next_chunk(buf: &mut MultiBuf) -> &[u8];
}
// ANCHOR_END: rust_bridge
// ANCHOR: cpp_bridge
// C++ types and signatures exposed to Rust.
unsafe extern "C++" {
include!("include/blobstore.h");
type BlobstoreClient;
fn new_blobstore_client() -> UniquePtr<BlobstoreClient>;
fn put(self: Pin<&mut BlobstoreClient>, parts: &mut MultiBuf) -> u64;
fn tag(self: Pin<&mut BlobstoreClient>, blobid: u64, tag: &str);
fn metadata(&self, blobid: u64) -> BlobMetadata;
}
// ANCHOR_END: cpp_bridge
}
// ANCHOR_END: bridge
/// An iterator over contiguous chunks of a discontiguous file object.
///
/// Toy implementation uses a Vec<Vec<u8>> but in reality this might be iterating
/// over some more complex Rust data structure like a rope, or maybe loading
/// chunks lazily from somewhere.
pub struct MultiBuf {
chunks: Vec<Vec<u8>>,
pos: usize,
}
/// Pulls the next chunk from the buffer.
pub fn next_chunk(buf: &mut MultiBuf) -> &[u8] {
let next = buf.chunks.get(buf.pos);
buf.pos += 1;
next.map_or(&[], Vec::as_slice)
}
fn main() {
let mut client = ffi::new_blobstore_client();
// Upload a blob.
let chunks = vec![b"fearless".to_vec(), b"concurrency".to_vec()];
let mut buf = MultiBuf { chunks, pos: 0 };
let blobid = client.pin_mut().put(&mut buf);
println!("blobid = {}", blobid);
// Add a tag.
client.pin_mut().tag(blobid, "rust");
// Read back the tags.
let metadata = client.metadata(blobid);
println!("tags = {:?}", metadata.tags);
}

11
third_party/cxx/book/snippets.cc vendored Normal file
View File

@ -0,0 +1,11 @@
// This file contains various code snippets taken from the CXX book and
// tutorial. Some have been modified to fit the course better.
// ANCHOR: shared_enums_cpp
enum class Suit : uint8_t {
Clubs = 0,
Diamonds = 1,
Hearts = 2,
Spades = 3,
};
// ANCHOR_END: shared_enums_cpp

121
third_party/cxx/book/snippets.rs vendored Normal file
View File

@ -0,0 +1,121 @@
//! This file contains various code snippets taken from the CXX book and
//! tutorial. Some have been modified to fit the course better.
// ANCHOR: rust_bridge
#[cxx::bridge]
mod ffi {
extern "Rust" {
type MyType; // Opaque type
fn foo(&self); // Method on `MyType`
fn bar() -> Box<MyType>; // Free function
}
}
struct MyType(i32);
impl MyType {
fn foo(&self) {
println!("{}", self.0);
}
}
fn bar() -> Box<MyType> {
Box::new(MyType(123))
}
// ANCHOR_END: rust_bridge
// ANCHOR: cpp_bridge
#[cxx::bridge]
mod ffi {
extern "C++" {
include!("demo/include/blobstore.h");
type BlobstoreClient;
fn new_blobstore_client() -> UniquePtr<BlobstoreClient>;
fn put(&self, parts: &mut MultiBuf) -> u64;
}
unsafe extern "C++" {
fn f(); // safe to call
}
}
// ANCHOR_END: cpp_bridge
// ANCHOR: shared_types
#[cxx::bridge]
mod ffi {
#[derive(Clone, Debug, Hash)]
struct PlayingCard {
suit: Suit,
value: u8, // A=1, J=11, Q=12, K=13
}
enum Suit {
Clubs,
Diamonds,
Hearts,
Spades,
}
}
// ANCHOR_END: shared_types
// ANCHOR: shared_enums_bridge
#[cxx::bridge]
mod ffi {
enum Suit {
Clubs,
Diamonds,
Hearts,
Spades,
}
}
// ANCHOR_END: shared_enums_bridge
// ANCHOR: shared_enums_rust
#[derive(Copy, Clone, PartialEq, Eq)]
#[repr(transparent)]
pub struct Suit {
pub repr: u8,
}
#[allow(non_upper_case_globals)]
impl Suit {
pub const Clubs: Self = Suit { repr: 0 };
pub const Diamonds: Self = Suit { repr: 1 };
pub const Hearts: Self = Suit { repr: 2 };
pub const Spades: Self = Suit { repr: 3 };
}
// ANCHOR_END: shared_enums_rust
// ANCHOR: rust_result
#[cxx::bridge]
mod ffi {
extern "Rust" {
fn fallible(depth: usize) -> Result<String>;
}
}
fn fallible(depth: usize) -> anyhow::Result<String> {
if depth == 0 {
return Err(anyhow::Error::msg("fallible1 requires depth > 0"));
}
Ok("Success!".into())
}
// ANCHOR_END: rust_result
// ANCHOR: cpp_exception
#[cxx::bridge]
mod ffi {
unsafe extern "C++" {
include!("example/include/example.h");
fn fallible(depth: usize) -> Result<String>;
}
}
fn main() {
if let Err(err) = ffi::fallible(99) {
eprintln!("Error: {}", err);
process::exit(1);
}
}
// ANCHOR_END: cpp_exception