Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Design for custom exceptions #1186

Open
schreter opened this issue Feb 27, 2023 · 1 comment
Open

Design for custom exceptions #1186

schreter opened this issue Feb 27, 2023 · 1 comment

Comments

@schreter
Copy link
Contributor

In our project, we need to control which exceptions get thrown into C++ code from our Rust callbacks (i.e., it can't be rust::Error, not even std::exception, it has to be one of the project-specific exception types).

The PR #1180 already provides some basics to be able to handle custom errors using a callback into C++ to construct the exception (via ToCxxException trait). However, this is not sufficient, since we need to handle also "standard" exceptions like AllocError or std::io::Error to throw respective C++ exceptions.

One possible way would be to add ToCxxException for some standard Rust errors (which is probably desirable on its own), but due to the Rust type system, such implementations must reside in cxx crate. Adding the possibility to register callbacks to generate different exceptions for all these types would bloat the cxx crate unnecessarily.

The proposal is thus to simply leave this to the implementor of the higher layer functionality (i.e., our team in this case) to actually convert errors to C++ exceptions in an application-specific way.

In #1180, the default mapping is done using cxx::map_rust_error_to_cxx_exception macro. This currently only handles CxxException specially to allow for transparent passing of C++ exceptions through Rust frames. As mentioned, this could be extended for some standard exceptions.

However, to make this fully flexible, we can simply add an attribute with the path to the macro on the bridge/extern block/function (typically only on the bridge) to specify this mapping macro explicitly. Then, the bridge will generate the code to map the error to the exception in a user-specific way, avoiding bloat in the cxx crate.

The current working proposal is:

#[cxx::bridge(namespace = "my_crate")]
#[error_mapper = ::my_crate::map_rust_error_to_cxx_exception]
pub mod ffi {
   ...
}

where my_crate::map_rust_error_to_cxx_exception is a macro taking one ident or expr with the value of the Rust error and returning a cxx::CxxException object built for it. Since it's a macro, it can implement the semi-specialization trick via multiple trait impls shadowing each other in a deterministic way to handle some errors explicitly and some by a common fallback path, much like already done in the cxx crate.

We have the implementation already done in mere ~60 LOCs on top of #1180, but cannot publish it yet, since that PR is still pending.

Any further ideas/opinions/suggestions for the attribute name?

Thanks.

@schreter
Copy link
Contributor Author

schreter commented Feb 28, 2023

Note: I found an alternative, but it's not particularly nice (still based on #1180).

Instead of adding an attribute, I can import all items from cxx into a new crate, say rte_cxx, then shadow the macro map_rust_error_to_cxx_exception and the trait ToCxxException there and then import rte_cxx under the name cxx into the crate creating the bridge. This works basically in the same way as specifying an additional error mapper attribute (of course, then for all bridges in that crate, but that's fine in our project). Not sure which way is preferred.

Any other ideas?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant