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:

1. #[cxx::bridge(namespace = "gfx::rust_bindings")]
2. mod ffi {
3. extern "Rust" {
4. /// This returns an FFI-friendly equivalent of `Result<PngReader<'a>,
5. /// ()>`.
6. fn new_png_reader<'a>(input: &'a [u8]) -> Box<ResultOfPngReader<'a>>;

8. /// C++ bindings for the `crate::png::ResultOfPngReader` type.
9. type ResultOfPngReader<'a>;
10. fn is_err(self: &ResultOfPngReader) -> bool;
11. fn unwrap_as_mut<'a, 'b>(
12. self: &'b mut ResultOfPngReader<'a>,
13. ) -> &'b mut PngReader<'a>;

15. /// C++ bindings for the `crate::png::PngReader` type.
16. type PngReader<'a>;
17. fn height(self: &PngReader) -> u32;
18. fn width(self: &PngReader) -> u32;
19. fn read_rgba8(self: &mut PngReader, output: &mut [u8]) -> bool;
20. }
21. }

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).