mirror of
https://github.com/google/comprehensive-rust.git
synced 2025-04-20 15:08:02 +02:00
Chromium: expanding CXX error handling section. (#1539)
This commit is contained in:
parent
1fa6b7438b
commit
24b132b67a
@ -256,6 +256,8 @@
|
|||||||
- [Example bindings](chromium/interoperability-with-cpp/example-bindings.md)
|
- [Example bindings](chromium/interoperability-with-cpp/example-bindings.md)
|
||||||
- [Limitations of CXX](chromium/interoperability-with-cpp/limitations-of-cxx.md)
|
- [Limitations of CXX](chromium/interoperability-with-cpp/limitations-of-cxx.md)
|
||||||
- [CXX error handling](chromium/interoperability-with-cpp/error-handling.md)
|
- [CXX error handling](chromium/interoperability-with-cpp/error-handling.md)
|
||||||
|
- [Error handling: QR example](chromium/interoperability-with-cpp/error-handling-qr.md)
|
||||||
|
- [Error handling: PNG example](chromium/interoperability-with-cpp/error-handling-png.md)
|
||||||
- [Using CXX in Chromium](chromium/interoperability-with-cpp/using-cxx-in-chromium.md)
|
- [Using CXX in Chromium](chromium/interoperability-with-cpp/using-cxx-in-chromium.md)
|
||||||
- [Exercise](exercises/chromium/interoperability-with-cpp.md)
|
- [Exercise](exercises/chromium/interoperability-with-cpp.md)
|
||||||
- [Adding third party crates](chromium/adding-third-party-crates.md)
|
- [Adding third party crates](chromium/adding-third-party-crates.md)
|
||||||
|
43
src/chromium/interoperability-with-cpp/error-handling-png.md
Normal file
43
src/chromium/interoperability-with-cpp/error-handling-png.md
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
# CXX error handling: PNG example
|
||||||
|
|
||||||
|
A prototype of a PNG decoder illustrates what can be done when the successful
|
||||||
|
result cannot be passed across the FFI boundary:
|
||||||
|
|
||||||
|
```rust,ignore
|
||||||
|
#[cxx::bridge(namespace = "gfx::rust_bindings")]
|
||||||
|
mod ffi {
|
||||||
|
extern "Rust" {
|
||||||
|
/// This returns an FFI-friendly equivalent of `Result<PngReader<'a>, ()>`.
|
||||||
|
fn new_png_reader<'a>(input: &'a [u8]) -> Box<ResultOfPngReader<'a>>;
|
||||||
|
|
||||||
|
/// C++ bindings for the `crate::png::ResultOfPngReader` type.
|
||||||
|
type ResultOfPngReader<'a>;
|
||||||
|
fn is_err(self: &ResultOfPngReader) -> bool;
|
||||||
|
fn unwrap_as_mut<'a, 'b>(
|
||||||
|
self: &'b mut ResultOfPngReader<'a>,
|
||||||
|
) -> &'b mut PngReader<'a>;
|
||||||
|
|
||||||
|
/// C++ bindings for the `crate::png::PngReader` type.
|
||||||
|
type PngReader<'a>;
|
||||||
|
fn height(self: &PngReader) -> u32;
|
||||||
|
fn width(self: &PngReader) -> u32;
|
||||||
|
fn read_rgba8(self: &mut PngReader, output: &mut [u8]) -> bool;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
<details>
|
||||||
|
|
||||||
|
`PngReader` and `ResultOfPngReader` are Rust types --- objects of these types
|
||||||
|
cannot cross the FFI boundary without indirection of a `Box<T>`. We can't have
|
||||||
|
an `out_parameter: &mut PngReader`, because CXX doesn't allow C++ to store Rust
|
||||||
|
objects by value.
|
||||||
|
|
||||||
|
This example illustrates that even though CXX doesn't support arbitrary generics
|
||||||
|
nor templates, we can still pass them across the FFI boundary by manually
|
||||||
|
specializing / monomorphizing them into a non-generic type. In the example
|
||||||
|
`ResultOfPngReader` is a non-generic type that forwards into appropriate methods
|
||||||
|
of `Result<T, E>` (e.g. into `is_err`, `unwrap`, and/or `as_mut`).
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
39
src/chromium/interoperability-with-cpp/error-handling-qr.md
Normal file
39
src/chromium/interoperability-with-cpp/error-handling-qr.md
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
# CXX error handling: QR example
|
||||||
|
|
||||||
|
The QR code generator is [an example][0] where a boolean is used to communicate
|
||||||
|
success vs failure, and where the successful result can be passed across the FFI
|
||||||
|
boundary:
|
||||||
|
|
||||||
|
```rust,ignore
|
||||||
|
#[cxx::bridge(namespace = "qr_code_generator")]
|
||||||
|
mod ffi {
|
||||||
|
extern "Rust" {
|
||||||
|
fn generate_qr_code_using_rust(
|
||||||
|
data: &[u8],
|
||||||
|
min_version: i16,
|
||||||
|
out_pixels: Pin<&mut CxxVector<u8>>,
|
||||||
|
out_qr_size: &mut usize,
|
||||||
|
) -> bool;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
<details>
|
||||||
|
|
||||||
|
Students may be curious about the semantics of the `out_qr_size` output. This
|
||||||
|
is not the size of the vector, but the size of the QR code (and admittedly it is
|
||||||
|
a bit redundant - this is the square root of the size of the vector).
|
||||||
|
|
||||||
|
It may be worth pointing out the importance of initializing `out_qr_size` before
|
||||||
|
calling into the Rust function. Creation of a Rust reference that points to
|
||||||
|
uninitialized memory results in Undefined Behavior (unlike in C++, when only the
|
||||||
|
act of dereferencing such memory results in UB).
|
||||||
|
|
||||||
|
If students ask about `Pin`, then explain why CXX needs it for mutable
|
||||||
|
references to C++ data: the answer is that C++ data can’t be moved around like
|
||||||
|
Rust data, because it may contain self-referential pointers.
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
[0]: https://source.chromium.org/chromium/chromium/src/+/main:components/qr_code_generator/qr_code_generator_ffi_glue.rs;l=13-18;drc=7bf1b75b910ca430501b9c6a74c1d18a0223ecca
|
||||||
|
|
@ -1,41 +1,22 @@
|
|||||||
# CXX error handling
|
# CXX error handling
|
||||||
|
|
||||||
CXX's support for `Result<T,E>` relies on C++ exceptions, so we can't use that
|
CXX's [support for `Result<T,E>`][0] relies on C++ exceptions, so we can't use
|
||||||
in Chromium. Alternatives:
|
that in Chromium. Alternatives:
|
||||||
|
|
||||||
* Where success can be represented as a simple Boolean, as done in our [QR code generator][1]:
|
* The `T` part of `Result<T, E>` can be:
|
||||||
Return a Boolean representing success, and record results using out-parameters:
|
- Returned via out parameters (e.g. via `&mut T`). This requires that `T`
|
||||||
```rust,ignore
|
can be passed across the FFI boundary - for example `T` has to be:
|
||||||
#[cxx::bridge(namespace = "qr_code_generator")]
|
- A primitive type (like `u32` or `usize`)
|
||||||
mod ffi {
|
- A type natively supported by `cxx` (like `UniquePtr<T>`) that has a
|
||||||
extern "Rust" {
|
suitable default value to use in a failure case (*unlike* `Box<T>`).
|
||||||
fn generate_qr_code_using_rust(
|
- Retained on the Rust side, and exposed via reference. This may be needed
|
||||||
data: &[u8],
|
when `T` is a Rust type, which cannot be passed across the FFI boundary,
|
||||||
min_version: i16,
|
and cannot be stored in `UniquePtr<T>`.
|
||||||
out_pixels: Pin<&mut CxxVector<u8>>,
|
|
||||||
out_qr_size: &mut usize,
|
|
||||||
) -> bool;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
* Where success is more complex, provide a Rust
|
|
||||||
object which can be queried for details of success or failure:
|
|
||||||
```rust,ignore
|
|
||||||
#[cxx::bridge]
|
|
||||||
mod ffi {
|
|
||||||
extern "Rust" {
|
|
||||||
type PngDecoder;
|
|
||||||
fn create_png_decoder() -> Box<PngDecoder>;
|
|
||||||
fn decode(self: &PngDecoder, png: &[u8]) -> bool; // whether successful
|
|
||||||
fn get_err_code(self: &PngDecoder) -> u32; // or some more complex error type
|
|
||||||
fn get_decoded_image(self: &PngDecoder) -> &[u8];
|
|
||||||
// or some more complex success type
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
|
* The `E` part of `Result<T, E>` can be:
|
||||||
The best way to learn CXX is by doing, so, another exercise!
|
- Returned as a boolean (e.g. `true` representing success, and `false`
|
||||||
|
representing failure)
|
||||||
|
- Preserving error details is in theory possible, but so far hasn't been
|
||||||
|
needed in practice.
|
||||||
|
|
||||||
[0]: https://cxx.rs/binding/result.html
|
[0]: https://cxx.rs/binding/result.html
|
||||||
[1]: https://source.chromium.org/chromium/chromium/src/+/main:components/qr_code_generator/qr_code_generator_ffi_glue.rs;l=10
|
|
Loading…
x
Reference in New Issue
Block a user