mirror of
https://github.com/google/comprehensive-rust.git
synced 2024-12-15 14:27:50 +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)
|
||||
- [Limitations of CXX](chromium/interoperability-with-cpp/limitations-of-cxx.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)
|
||||
- [Exercise](exercises/chromium/interoperability-with-cpp.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's support for `Result<T,E>` relies on C++ exceptions, so we can't use that
|
||||
in Chromium. Alternatives:
|
||||
CXX's [support for `Result<T,E>`][0] relies on C++ exceptions, so we can't use
|
||||
that in Chromium. Alternatives:
|
||||
|
||||
* Where success can be represented as a simple Boolean, as done in our [QR code generator][1]:
|
||||
Return a Boolean representing success, and record results using out-parameters:
|
||||
```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;
|
||||
}
|
||||
}
|
||||
```
|
||||
* 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 `T` part of `Result<T, E>` can be:
|
||||
- Returned via out parameters (e.g. via `&mut T`). This requires that `T`
|
||||
can be passed across the FFI boundary - for example `T` has to be:
|
||||
- A primitive type (like `u32` or `usize`)
|
||||
- A type natively supported by `cxx` (like `UniquePtr<T>`) that has a
|
||||
suitable default value to use in a failure case (*unlike* `Box<T>`).
|
||||
- Retained on the Rust side, and exposed via reference. This may be needed
|
||||
when `T` is a Rust type, which cannot be passed across the FFI boundary,
|
||||
and cannot be stored in `UniquePtr<T>`.
|
||||
|
||||
|
||||
The best way to learn CXX is by doing, so, another exercise!
|
||||
* The `E` part of `Result<T, E>` can be:
|
||||
- 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
|
||||
[1]: https://source.chromium.org/chromium/chromium/src/+/main:components/qr_code_generator/qr_code_generator_ffi_glue.rs;l=10
|
Loading…
Reference in New Issue
Block a user