dtolnay / cxx Goto Github PK
View Code? Open in Web Editor NEWSafe interop between Rust and C++
Home Page: https://cxx.rs
License: Apache License 2.0
Safe interop between Rust and C++
Home Page: https://cxx.rs
License: Apache License 2.0
We should support structs that have type parameters and translate them to an ABI-compatible class template in C++.
#[cxx::bridge]
mod ffi {
struct Generic<T> {
wow: T,
}
extern "C" {
fn f(g: Generic<String>);
}
}
It would greatly simplify API development if automatic Option<T>
conversion was supported.
Since C++17 there is std::optional<T>
, which would allow a more or less direct translation of supported types.
I had a look at #67 and the ideas and work done by @myronahn for Vec seems to be similar to what would be needed for supporting Option
so maybe one could build on that...
For example we might have:
// c++
using Obj = void*;
// rust
#[cxx::bridge]
mod ffi {
extern "C" {
type Obj;
fn f(obj: &Obj);
}
}
Currently this fails to compile because we forward declare opaque C types assuming they are structs.
target/debug/build/cxxtest/out/src/main.rs.cc:5:8: error: using typedef-name ‘using Obj = void*’ after ‘struct’
5 | struct Obj;
| ^~~
➜ demo-rs git:(master) cargo run
Compiling proc-macro2 v1.0.8
Compiling unicode-xid v0.2.0
Compiling syn v1.0.14
Compiling cc v1.0.50
Compiling anyhow v1.0.26
Compiling unicode-segmentation v1.6.0
Compiling unicode-width v0.1.7
Compiling termcolor v1.1.0
Compiling codespan v0.7.0
Compiling codespan-reporting v0.7.0
Compiling quote v1.0.2
Compiling link-cplusplus v1.0.1
Compiling cxx v0.1.2 (/home/eh/repos/other/cxx)
Compiling thiserror-impl v1.0.11
Compiling cxxbridge-macro v0.1.2 (/home/eh/repos/other/cxx/macro)
Compiling thiserror v1.0.11
Compiling cxxbridge-demo v0.0.0 (/home/eh/repos/other/cxx/demo-rs)
error[E0658]: custom attributes cannot be applied to modules
--> demo-rs/src/main.rs:1:1
|
1 | #[cxx::bridge(namespace = org::rust)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: for more information, see https://github.com/rust-lang/rust/issues/54727
error: aborting due to previous error
For more information about this error, try `rustc --explain E0658`.
error: could not compile `cxxbridge-demo`.
To learn more, run the command again with --verbose.
It would be great to provide a feature or something for this crate so that it could generate safe C bindings rather than C++. I would be willing to at least take a look at this; advice on how to proceed would be welcome.
Currently, paths::target_dir()
tries to find the target
directory by looking at the name.
This has two problems:
target-dir
(https://doc.rust-lang.org/cargo/reference/config.html)target
- and canonicalize
resolves symlinks.In both cases, paths::target_dir()
fails.
What is the rationale for not putting all the generated code etc. in the OUT_DIR or a subdirectory thereof directly? If the latter is acceptable, I'm happy to submit a pull request changing the behavior...
As mentioned in #5, there is need for better namespace usage. Additionally, I understand the need for ABI compatibility, and quickly hacked up an improved API suggestion on the C++ side. This will of course required additional work on the rust side of things, but wanted to get feedback first.
Below, is a C++17 (though backportable to C++11 with minimal effort) API mockup for an improved API. Among the changes are
inline namespace
named v1
str
actually private. If its needed in generated code, I recommend using the friend
keyword.from_raw
and into_raw
could be turned into construction and explicit casting to a more opaque class (this class could be made into a friend
of box, or vice versa. This reduces the need for unnecessary encapsulation, and also allows the type system to take care of some unsafety for us). For now I've just used a unique_ptr, which will cause a compiler error if an opaque type would be destructed.iosfwd
instead of iostream
. I recommend looking at what happens per translation unit when including iostream
. It's not pretty, but iosfwd
allows us to forward declare the ostream
insertion operators without incurring this overhead.explicit
. This saves more headaches in the long run, I assure you.explicit operator bool
, so it can be used in if()
statements, and is more in line with unique_ptr
, which is the closest thing on the C++ side.std::addressof
and ::new
. Adopting this API would allow #6 to be completed.I also know quite a bit about build systems and the C++ language (and API design) in general, so feel free to ask any questions :)
#ifndef CXXBRIDGE_H
#define CXXBRIDGE_H
#include <cstdint>
#include <cstddef>
#include <type_traits>
#include <utility>
#include <iosfwd>
#include <string>
#include <array>
namespace cxxbridge {
inline namespace v1 {
namespace rust {
struct string final {
string (string const&) noexcept;
string (string&&) noexcept
string () noexcept;
~string () noexcept;
string (std::string const&);
string (std::string&&);
string (char const*);
string& operator = (string const&) noexcept;
string& operator = (string&&) noexcept;
void swap (string&) noexcept;
explicit operator std::string () const;
char const* data () const noexcept; // no null terminator
std::size_t size () const noexcept;
private:
std::array<intptr_t, 3> repr;
};
class str final {
// internal is private and must not be used other than by generated code.
// Not really ABI compatible with &str, codegen will translate to
// cxx::rust_str::RustStr
struct internal {
char const* ptr;
std::size_t len;
};
public:
str (str const&) noexcept;
str (str&&) = delete;
str () noexcept;
str (std::string&&) = delete;
str (std::string const&) noexcept;
str (char const*) noexcept;
str (internal) noexcept;
str& operator = (str const&) noexcept;
void swap (str&) noexcept;
explicit operator std::string () const;
private:
internal repr;
};
template <class T>
struct box final {
using value_type = T;
using const_pointer = std::add_pointer_t<std::add_const_t<value_type>>;
using pointer = std::add_pointer_t<value_type>;
box (box const& that) : box(*that) { }
box (box&& that) : repr(std::exchange(that.repr, 0)) { }
template <class... Args>
box (std::in_place_t, Args&&... args) {
::new (std::addressof(**this)) value_type(std::forward<Args>(args)...);
}
box (T const& value) : box(std::in_place, value) { }
// replace this with a raw<T> wrapper so that we KNOW a type came from box
box (std::unique_ptr<value_type> handle) {
this->raw(handle.release());
}
box& operator = (box const& that) {
box(that).swap(*this);
return *this;
}
box& operator = (box&& that) {
if (*this) { this->drop(); }
this->repr = std::exchange(that.repr, 0);
return *this;
}
void swap (box& that) {
using std::swap;
swap(this.repr, that.repr);
}
explicit operator bool () const noexcept { return this->repr; }
// This should be replaced with a raw<T> wrapper so that we KNOW a type came
// from a box.
explicit operator std::unique_ptr<value_type> () noexcept {
auto ptr = std::unique_ptr { this->get() };
this->repr = 0;
return ptr;
}
const_pointer operator -> () const noexcept { return this->get(); }
pointer operator -> () noexcept { return this->get(); }
decltype(auto) operator * () const noexcept { return *this->get(); }
decltype(auto) operator * () noexcept { return *this->get(); }
private:
void destroy () noexcept;
void uninit() noexcept;
void raw (pointer) noexcept;
pointer raw () noexcept;
const_pointer get () const noexcept;
pointer get () noexcept;
std::uintptr_t repr { };
};
std::ostream& operator << (std::ostream&, string const&);
std::ostream& operator << (std::ostream&, str const&);
}}} /* cxxbridge::v1::rust */
#endif /* CXXBRIDGE_H */
I'm guessing this is just a case of things not being implemented yet, but what is required to let C functions return raw pointers?
For example this code
use std::os::raw::c_char;
#[cxx::bridge]
mod ffi {
extern "C" {
include!("vendor/bzip2/bzlib.h");
fn BZ2_bzlibVersion() -> *const c_char;
}
}
Fails to compile with
Compiling cxx-experiment v0.1.0 (/home/michael/Documents/cxx-experiment)
error: unsupported type
--> src/lib.rs:8:34
|
8 | fn BZ2_bzlibVersion() -> *const c_char;
| ^^^^^^^^^^^^^
warning: unused import: `std::os::raw::c_char`
--> src/lib.rs:1:5
|
1 | use std::os::raw::c_char;
| ^^^^^^^^^^^^^^^^^^^^
|
= note: `#[warn(unused_imports)]` on by default
error: aborting due to previous error
error: could not compile `cxx-experiment`.
To learn more, run the command again with --verbose.
intptr_t
from libc is an alias for isize
(https://docs.rs/libc/0.2.68/libc/type.intptr_t.html), but when I use libc::intptr_t
inside extern "Rust" block, I get the unsupported type
error. When I change it to isize
, keeping intptr_t
outside the block (i.e. in the function declaration), it works.
The C++ side may do arbitrary unsafe things like dereferencing dangling pointers.
Hi there, this library looks interesting.
I was wondering if you had any thoughts on "converting" simple rust errors (say of the ErrKind
flavor) into boilerplated errors from one of the following C++ libraries:
We should detect and reject the following:
mod ffi {
struct Struct {
t: Box<T>,
}
extern "C" {
fn f(x: &mut Struct);
}
}
Rust's Box is not nullable, and the above makes it too easy for innocuous-looking C++ code to std::move out of the Box and hit UB when it's later dropped by Rust.
Instead, for data structures containing Box that ever pass from Rust to C++ by mutable reference, we should enforce that the Box is held in Option so it can be nulled out during moves. Option<Box<T>> has a guaranteed representation for Sized T.
mod ffi {
struct Struct {
t: Option<Box<T>>,
}
...
}
Let me make it clear. I got a project which need to use a 3rd C++ library(with lib and header).
To use it , I have to do so in C++:
#include <"ThirdPartyInterface.h">
class CMyClass: public ThirdPartyInterface{
virtual callbackFrom3rdPartyInterface();// I need to implement it.
};
The current setup exposes our C++ header as:
#include "cxxbridge/cxxbridge.h"
This isn't great because of the repetition, and also because all the symbols in the header are namespaced under rust::
so an include path with rust in the name somewhere may make more sense.
Something like:
#include "cxxbridge/rust.h"
#include "rust/cxxbridge.h"
#include "rust/cxx.h"
https://docs.rs/cxx/0.1.2/cxx/struct.Build.html#method.bridge could automatically do:
println!("cargo:rerun-if-changed={}", rust_source_file);
According to https://doc.rust-lang.org/cargo/reference/build-scripts.html#outputs-of-the-build-script:
rerun-if-changed=PATH
is a path to a file or directory which indicates that the build script should be re-run if it changes (detected by a more-recent last-modified timestamp on the file). Normally build scripts are re-run if any file inside the crate root changes, but this can be used to scope changes to just a small set of files.
As suggested in #8, a reasonable signature for such a constructor would be:
template <typename... Args>
Box(std::in_place_t, Args&&... args) {
/* construct T{std::forward<Args>(args)...} */
}
According to https://en.cppreference.com/w/cpp/utility/in_place in_place_t is available since C++17.
Semantically, C++ types cannot be relocated because they may contain pointers to themselves or chains of objects that eventually point back to themselves. Rust assumes that all types can be relocated (e.g. by memcpy). Pin<T> can help with this problem when working with such FFI, but is there any way in which this crate can assist bridging these semantic differences?
error: linking with `cc` failed: exit code: 1
|
= note: "cc" "-m64" "-L
...
= note: ld: library not found for -lstdc++
clang: error: linker command failed with exit code 1 (use -v to see invocation)
I think it's trying to link libstdc++ (GNU), but macOS doesn't ship it anymore (it ships libc++, the Clang one). So, it should be trying to link with -lc++
instead.
Building on x86_64-pc-windows-msvc currently emits this warning:
warning: cl : Command line warning D9002 : ignoring unknown option '-std=c++11'
According to https://docs.microsoft.com/en-us/cpp/build/reference/std-specify-language-standard-version?view=vs-2019, C++11 is the default with flags /std:c++14
and /std:c++17
for the newer standards.
We should find a way to pass -std=c++11
only when it's required, such as on x86_64-apple-darwin where C++98 is the default. https://opensource.apple.com/source/clang/clang-800.0.42.1/src/tools/clang/www/cxx_status.html
running: "cc" "-O0" "-ffunction-sections" "-fdata-sections" "-fPIC" "-g" "-fno-omit-frame-pointer" "-m64" "-Wall" "-Wextra" "-o" "/Users/travis/build/dtolnay/cxx/target/debug/build/cxx-f198c1b742072022/out/src/cxxbridge.o" "-c" "src/cxxbridge.cc"
In file included from src/cxxbridge.cc:1:
src/../include/cxxbridge.h:9:18: warning: 'final' keyword is a C++11 extension [-Wc++11-extensions]
class RustString final {
^
As suggested in #46 (comment).
some people may want idiomatic C++ naming for the types as well. It wouldn't hurt to create type aliases that are all snake case. (i.e.,
template <class T> using box = Box<T>;
)
Currently only freestanding functions are supported by this crate, not methods.
I like the approach of using self
in the foreign function parameter list to signify a method receiver:
#[cxx::bridge]
mod ffi {
extern "C" {
type C;
fn f(self: &C);
}
extern "Rust" {
type R;
fn g(self: &R);
}
}
struct R {...}
impl R {
fn g(&self) {...}
}
This would expose the methods r.g()
to C++ and c.f()
to Rust.
The above syntax would require rustc 1.43 or newer for rust-lang/rust#68728.
Because RustBox uses new (this->deref_mut()) T(val)
, anyone on the C++ side of things could inject custom code.
To fix this, use ::new
instead of new
.
(There are other API issues on the C++ side of things, but I'll file that as a separate issue)
For example, in C++ it would be better to use something like cxxbridge::rust::String
, and the same for other Rust types, like Str, Vec, Map, etc.
Correspondingly in Rust: cxx::cpp::String
, Vector, Map, UniquePtr, SharedPtr.
Of course, the namespace names could be better - it's just an example against the RustString
and CxxString
type names.
As recommended in #8. String, Str, and Box should have an efficient swap
.
Currently for something like:
mod ffi {
struct Struct {
s: String,
p: UniquePtr<CxxString>,
}
extern "Rust" {
fn f(s: Struct);
}
}
the public function signature we emit on the C++ side would be:
void f(Struct s);
But in some varieties of calling code, the above signature might require doing an unnecessary deep copy of the fields of Struct. It may be better to emit instead:
void f(Struct &&s);
I have two files requiring FFI, json.rs
and basevalue.rs
. I'd like to keep the FFI blocks local to those files, instead of having a central ffi.rs
, but right now this is not possible.
My FFI block in basevalue.rs
.
#[cxx::bridge(namespace = base)]
pub mod ffi {
extern "C" {
include!("base/values_rust.h");
type RawOptionalBaseValue;
fn RustOptionalBaseValueSetString(v: &mut RawOptionalBaseValue, value: &str);
fn RustOptionalBaseValueSetBool(v: &mut RawOptionalBaseValue, val: bool);
// etc.
}
}
My FFI block in json.rs
:
#[cxx::bridge(namespace = base)]
pub mod ffi {
extern "Rust" {
pub fn decode_json(
bv: UniquePtr<RawOptionalBaseValue>,
json: &str
) -> bool;
}
}
Note that the json.rs
block references a type from the basevalue.rs
block.
This gives
error[cxxbridge]: unsupported type
which is unsurprising, and I imagine is really hard to solve.
(The exact code above is significantly simplified so obviously don't try to build it; if this issue isn't as "known" as I expect, let me know and I'll make a minimal test case).
As currently implemented, two invocations of the cli command are required and the output goes to stdout.
$ cxxbridge path/to/lib.rs --header > generated.h
$ cxxbridge path/to/lib.rs > generated.cc
In some Starlark-based environments (or others) it turns out to be desirable to emit both outputs from a single invocation.
genrule(
name = "gen",
src = "...",
cmd = "$(exe //path/to:cxxbridge-cmd) ${SRC}",
outs = {
"header": ["generated.h"],
"source": ["generated.cc"],
},
)
I can't use this crate yet, because of its compiler requirements. But the examples left me wondering how to refer to structs that are not in the ffi module.
Is there a way to make a struct defined in another module, like this example:
pub struct NamedPoint {
name: String,
pub x: i32,
pub y: i32,
}
visible to both languages, in the way that "SharedThing" is used in the example? Maybe a new annotation is needed for that?
This should be a simple extension, just hasn't been implemented yet.
If C++ allows float
and double
to mean something other than f32 and f64, we'll want to emit some static assertions to ensure we're in the case where the meaning is the same as in Rust.
If yes, it should be added to the "tbd" table.
If no, it may also be mentioned explicitly near it.
Sorry if it's a duplicate, but I didn't find anything helpful.
I use cargo build script. I included the generated header using this path: target/cxxbridge/src/lib.rs.h
. But during a clean build this header isn't there when a file with this include compiles.
In the example I see there's #include "demo-rs/src/main.rs.h"
and it's magically works with cargo build
. So I guess I skipped something important, which does this generating of header. But what?
The C++ types should come with operators for ==
, !=
, <
, >
, <=
, >=
.
The demo works but emits a warning.
% cargo run --manifest-path demo-rs/Cargo.toml
Compiling proc-macro2 v1.0.8
Compiling unicode-xid v0.2.0
Compiling cc v1.0.50
Compiling syn v1.0.14
Compiling unicode-segmentation v1.6.0
Compiling anyhow v1.0.26
Compiling unicode-width v0.1.7
Compiling termcolor v1.1.0
Compiling codespan v0.7.0
Compiling codespan-reporting v0.7.0
Compiling link-cplusplus v1.0.1
Compiling cxx v0.1.2
Compiling quote v1.0.2
Compiling thiserror-impl v1.0.9
Compiling cxxbridge-macro v0.1.2
Compiling thiserror v1.0.9
Compiling cxxbridge-demo v0.0.0
warning: target/debug/build/cxxbridge-demo-5144ad694551d3cf/out/demo-rs/src/main.rs.cc:94:20: warning: 'org$rust$cxxbridge01$get_name' has C-linkage specified, but returns user-defined type 'const std::string &' (aka 'const basic_string<char, char_traits<char>, allocator<char> > &') which is incompatible with C [-Wreturn-type-c-linkage]
warning: const std::string &org$rust$cxxbridge01$get_name(const ThingC &thing) noexcept {
warning: ^
warning: 1 warning generated.
Finished dev [unoptimized + debuginfo] target(s) in 16.65s
Running `target/debug/cxxbridge-demo`
this is a demo of cxx::bridge
called back with r=333
done with ThingC
Repro:
// lib.rs
#[cxx::bridge]
mod ffi {
extern "C" {
include!("repro.h");
type Virtual;
fn make() -> UniquePtr<Virtual>;
}
}
// repro.h
#pragma once
struct Virtual {
virtual void function() = 0;
};
std::unique_ptr<Virtual> make();
This fails to compile with:
repro.rs.cc: In function ‘void cxxbridge02$unique_ptr$Virtual$new(std::unique_ptr<Virtual>*, Virtual*)’:
repro.rs.cc:599:85: error: invalid new-expression of abstract class type ‘Virtual’
| new (ptr) ::std::unique_ptr<Virtual>(new Virtual(::std::move(*value)));
| ^
repro.h:11:8: note: because the following virtual functions are pure within ‘Virtual’:
| struct Virtual {
| ^~~~~~~
repro.h:12:16: note: ‘virtual void Virtual::function()’
| virtual void function() = 0;
| ^~~~~~~~
The error is inside of the shim we emit for UniquePtr::new
:
void cxxbridge02$unique_ptr$Virtual$new(::std::unique_ptr<Virtual> *ptr, Virtual *value) noexcept {
new (ptr) ::std::unique_ptr<Virtual>(new Virtual(::std::move(*value)));
}
But we should just not emit that shim for opaque C types. The signature of UniquePtr::new
is fn(T) -> UniquePtr<T>
which is uncallable when T
is an opaque C type because those can never exist by value on the Rust side of the bridge.
A typical Bazel setup will have something like this generated configuration:
alias(
name = "cargo_bin_cxxbridge",
actual = "//rust_bindings/cargo/vendor/cxxbridge-cmd-0.1.2:cargo_bin_cxxbridge",
)
This works fine for the two outputs that cxxbridge makes right now, but it would be nice for it to have an option to output the full cxxbridge.h
header as well. Otherwise, it will require fishing around in vendor directories and adding extra Bazel rules to make the file visible. By using the alias for the command to produce the file, the reference will stay stable across upgrades, and cxxbridge
can make internal changes without breaking builds.
I added this function to include.rs
:
pub fn get_full_cxxbridge() -> &'static str {
return HEADER
}
and that seemed fine, but I wasn't sure what to do about the required input
argument (I also couldn't test anything, because of #19). Here's a strawman:
/// Emit header with declarations only
#[structopt(long)]
header: bool,
+
+ /// Emit full cxxbridge header
+ #[structopt(long)]
+ cxxbridge: bool,
+
}
fn main() {
let opt = Opt::from_args();
+ if opt.cxxbridge {
+ let _ = io::stdout().lock().write_all(gen::include::get_full_cxxbridge().as_ref());
+ return;
+ }
let gen = if opt.header {
gen::do_generate_header
} else {
which works if I call it with a dummy input file:
cargo run -- foo.rs --cxxbridge
#5 and #8 call out that prefixed names like RustString and RustBox are not idiomatic and we should be placing these types in a namespace instead.
Thinking only of the code that I would want downstream to be writing, the style I find most readable is something like:
void f(rust::Str s, rust::Box<Thing> b);
Some questions for @pravic and @slurps-mad-rips or anyone else:
I have the feeling it isn't appropriate for me to declare a top-level rust
namespace with familiar type names like Box
in it. That namespace "belongs" to the official Rust project. Is that feeling correct or is it not so bad?
I can define our types in cxxbridge::rust
and recommend an import downstream to bring rust
in scope when there isn't a collision with something else. Are C++ style guides generally okay with that or would people end up forced to write out cxxbridge::rust::...
all over the place?
For the import, as far as I can tell it would be spelled using rust = cxxbridge::rust;
. Is that right? There doesn't seem to be a way to avoid repeating "rust" such as using cxxbridge::rust;
.
main.cc:11:18: error: using declaration cannot refer to a namespace
using cxxbridge::rust;
~~~~~~~~~~~^
main.cc:11:18: error: namespace ‘cxxbridge01::rust’ not allowed in using-declaration
11 | using cxxbridge::rust;
| ^~~~
Any other thoughts on the namespace scheme? Thanks!
I've hacked in some preliminary std::vector<T>
support to allow passing C++ vectors to Rust.
https://github.com/myronahn/cxx/tree/master
Right now it works for std::vector<uint8_t>
since that was my primary need but it would be very easy to add in support for all the basic types.
Adding support for general types is a bit harder as the orphan rule is keeping me from defining macro-expanded trait impls for both the Vector
class and the UniquePtr
class. I'm currently wondering what the best workaround would be for this.
It would be great to get a once-over from you if you have any spare time to take a look. Is it worth continuing work on this, or are you in the middle of implementing something bigger/better?
Thanks in advance.
Can I use this bridge with -msvc targets?
An outstanding idea for the interop, thank you for it, by the way.
I don't think cargo vendor
is picking up the workspace members in Cargo.toml
.
An approach that does work is a bin
target. This is what cbindgen
does:
https://github.com/eqrion/cbindgen/blob/ac1a7d47e87658cf36cb7e56edad7fa5f935dddd/Cargo.toml#L35
cargo vendor
is something cargo-raze
depends on to generate Bazel build files. If this issue were fixed, I believe the separate command would work well in a Bazel build.
I am currently writing some Rust code that will be called by external C code. This C code will call a Rust function and pass in a void *
that represents a commctrl window. After some processing, this Rust function will call a C++ function that displays a message box (note: I plan to use wxWidgets to display this message box, among other things. Because of the way wxWidgets is programmed (inheritance), I cannot import the required function into Rust.)
Essentially, my use case is this, in terms of code.
gui/about.h
:
void ShowMessageBox(void *parent, int otherData);
gui/about.cpp
:
#include <wx/wx.h>
void ShowMessageBox(void *parent, int otherData) {
wxDialog *dummy = new wxDialog();
dummy->SetHWND(parent);
wxString result;
result.Printf( wxT("The result is %d."), otherData);
wxMessageBox(result, "Result", wxOK | wxICON_INFORMATION, dummy);
}
src/lib.rs
:
use std::ffi::c_void;
#[cxx::bridge]
mod ffi {
extern "C" {
include!("gui/about.h");
fn ShowMessageBox(parent: *const c_void, other_data: i32);
}
}
#[no_mangle]
pub unsafe extern fn function_that_is_called_from_c(parent: *const c_void, other_data: i32) {
// processing and such
ffi::ShowMessageBox(parent, other_data);
}
Considering that raw pointers probably won't be added to this crate, how would a window handle be communicated across the Rust/C++ boundary?
Currently we catch and abort on unwinding across the FFI boundary in either direction; it's left to the user to communicate all failures via ordinary return values or out-parameters.
It would be nice if C++ exceptions were exposed to Rust as an idiomatic Result
error value. For C++ functions that are declared in the bridge as returning a Result, we would not abort on exceptions but instead marshal them across as some kind of cxx::Exception
object.
#[cxx::bridge]
mod ffi {
extern "C" {
fn f() -> Result<()>;
}
}
Conversely, Rust functions that are declared as returning a Result could transmit failures as an exception on the C++ side.
#[cxx::bridge]
mod ffi {
extern "Rust" {
fn g() -> Result<()>;
}
}
In both cases, long term we would want the translation to be customizable because not all codebases use exceptions; some would prefer a mapping of Rust Result
to Leaf or Outcome. Some thoughts in #16.
i.e. function parameters with the type fn(...) -> ...
.
#[cxx::bridge]
mod ffi {
extern "Rust" {
fn f(arg: u8, callback: fn(u8));
}
}
fn f(arg: u8, callback: fn(u8)) {
callback(256 - arg);
}
Maybe std::span
could be used as the translation of a Rust slice
Possibly also the rust::String and rust::Str constructors? I don't have a good grasp of the tradeoffs here. Insights welcome!
Having both causes silly trouble on case insensitive filesystems. 🤦♀
% git status
HEAD detached at origin/master
Changes not staged for commit:
(use "git add/rm <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)
deleted: BUILD
no changes added to commit (use "git add" and/or "git commit -a")
% git checkout -- BUILD
% git status
HEAD detached at origin/master
Changes not staged for commit:
(use "git add/rm <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)
deleted: build/rust.bzl
no changes added to commit (use "git add" and/or "git commit -a")
% git checkout -- build/rust.bzl
% git status
HEAD detached at origin/master
Changes not staged for commit:
(use "git add/rm <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)
deleted: BUILD
no changes added to commit (use "git add" and/or "git commit -a")
In some constructors, cxx uses exceptions to indicate failure. Many codebases specifically disable exception support for C++ for a variety of reasons (Firefox and Chromium amongst them).
In the long term, I would hope we could design a system that would allow these errors to be caught in both exception-based C++ and -fno-exceptions
code, but for now even just printing the error message and abort()
ing would at least be a start.
In summary:
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.