Skip to content

Support C++ exceptions without undefined behaviour #1208

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

Open
Ekleog opened this issue Jan 3, 2018 · 4 comments
Open

Support C++ exceptions without undefined behaviour #1208

Ekleog opened this issue Jan 3, 2018 · 4 comments

Comments

@Ekleog
Copy link
Contributor

Ekleog commented Jan 3, 2018

Bindgen currently does not (cannot) support C++ exceptions, because C++ won't be able to unwind correctly into the rust code.

An idea to support this would be to emit a C++ wrapper for the C++ functions (see #1172 (comment) for the design idea). Emitting a C++ wrapper for functions would also allow to (by making it extern "C") remove all of bindgen's code for mangling C++ functions and make supporting new C++ compilers much easier.

A C++ wrapper function could look like this (syntax untested esp. the union one, and there are tricks with non-copyable return types, I can never remember the correct syntax for moving c++ stuff):

// Original function
ret_t fun(arg_t arg) {
    // ...
}

// Wrapper function
struct XXX_bindgen_result_ret_t {
    enum { OK, ERROR, UNKNOWN_ERROR } tag;
    union {
        ret_t ok;
        char * error_msg;
    } value;

    static XXX_bindgen_result_ret_t make_ok(ret_t r) {
        XXX_bindgen_result_ret_t ret;
        ret.tag = OK;
        ret.value.ok = r;
        return ret;
    }
    static XXX_bindgen_result_ret_t make_err(char * s) {
        XXX_bindgen_result_ret_t ret;
        ret.tag = ERROR;
        ret.value.error_msg = s;
        return ret;
    }
    static XXX_bindgen_result_ret_t make_unknown_error() {
        XXX_bindgen_result_ret_t ret;
        ret.tag = UNKNOWN_ERROR;
        return ret;
    }
};

extern "C"
XXX_bindgen_result_ret_t XXX_bindgen_function_fun(arg_t arg) {
    try {
        return XXX_bindgen_result_ret_t::make_ok(original_cpp_function(arg));
    } catch (exception e) {
        return XXX_bindgen_result_ret_t::make_err(e.message());
    } catch (...) {
        return XXX_bindgen_result_ret_t::make_unknown_error();
    }
}

Basically, with this C++ wrapper, bindgen takes control of the way FFI is performed, and can almost ignore the fact that it's a C++ compiler on which it's running.

The drawbacks are:

  • A (single) additional function call at each FFI call
  • Requirement to build an additional C++ file in addition to building rust code
  • This requirement is a backward-incompatible change if it's not hidden behind a flag (and I guess if exceptions are supported it should be by opt-out and not opt-in, unless not opting in also means not allowing C++, given it's such an easy way to trigger UB and exceptions are way too rarely documented)
@tmandry
Copy link
Member

tmandry commented Feb 21, 2018

+1. Even though much of the prevailing C++ wisdom says to avoid exceptions, the C++ Core Guidelines actually encourage their use. Especially from constructors, which have no other "standard" way of failing if they cannot establish their invariants.

On a tangential note, it seems like doing this would open the door to a whole host of possibilities, including calling methods on template classes. I know performance may not be ideal with these wrappers, but it would support "plug and play" integration with many existing APIs which use exceptions or templates. I can say as a lead for a decent-sized C++ codebase that this would be a game changer in terms of possible adoption of Rust, which is something I badly want.

@tmandry
Copy link
Member

tmandry commented Feb 21, 2018

I assume this would leave it to the user to invoke the C++ compiler? Presumably, we are already using a C++ compiler in our build system somewhere, and many systems like Bazel already have a nice way of dealing with generated source (genfiles).

Doing some magic from build.rs sounds like a nice way to get started for some folks, but not a general solution.

@Ekleog
Copy link
Contributor Author

Ekleog commented Feb 21, 2018

Yes, I was thinking it would leave it to the user to compile and link the generated C++ source files, in the same way as they would be linking the “original” C++ source files. This means either compiling Rust as a library and then linking it into a C++ executable or compiling C++ as a library and linking it into the Rust executable, just like with the current bindgen.

@alexreg
Copy link

alexreg commented Aug 15, 2018

Any developments with this lately?

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

No branches or pull requests

4 participants