C++ std::vector<T> and Rust std::vec::Vec<T> support
Add basic std::vector and std::vec::Vec support across FFI boundary.
diff --git a/Cargo.toml b/Cargo.toml
index f497482..95cd87d 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -17,6 +17,7 @@
[dependencies]
anyhow = "1.0"
cc = "1.0.49"
+codespan = "0.7"
codespan-reporting = "0.9"
cxxbridge-macro = { version = "=0.2.10", path = "macro" }
link-cplusplus = "1.0"
diff --git a/demo-cxx/demo.cc b/demo-cxx/demo.cc
index cd447ea..387c56a 100644
--- a/demo-cxx/demo.cc
+++ b/demo-cxx/demo.cc
@@ -9,13 +9,41 @@
ThingC::~ThingC() { std::cout << "done with ThingC" << std::endl; }
-std::unique_ptr<ThingC> make_demo(rust::Str appname) {
+std::unique_ptr<ThingC> make_demo(::rust::Str appname) {
return std::unique_ptr<ThingC>(new ThingC(std::string(appname)));
}
const std::string &get_name(const ThingC &thing) { return thing.appname; }
-void do_thing(SharedThing state) { print_r(*state.y); }
+std::unique_ptr<std::vector<uint8_t>> do_thing(SharedThing state) {
+ print_r(*state.y);
+ auto vec = std::unique_ptr<std::vector<uint8_t>>(new std::vector<uint8_t>());
+ for (uint8_t i = 0; i < 10; i++) {
+ vec->push_back(i * i);
+ }
+ return vec;
+}
+
+JsonBlob get_jb(const ::rust::Vec<uint8_t>& vec) {
+ JsonBlob retval;
+
+ std::cout << "incoming vec length is " << vec.size() << "\n";
+ auto vec_copy = static_cast<std::vector<uint8_t>>(vec);
+ std::cout << "vec_copy length is " << vec_copy.size() << "\n";
+ std::cout << "vec_copy[0] is " << (int)vec_copy[0] << "\n";
+
+ auto blob = std::unique_ptr<std::vector<uint8_t>>(new std::vector<uint8_t>());
+ for (uint8_t i = 0; i < 10; i++) {
+ blob->push_back(i * 2);
+ }
+
+ auto json = std::unique_ptr<std::string>(new std::string("{\"demo\": 23}"));
+
+ retval.json = std::move(json);
+ retval.blob = std::move(blob);
+
+ return retval;
+}
} // namespace example
} // namespace org
diff --git a/demo-cxx/demo.h b/demo-cxx/demo.h
index fafc474..eea0af7 100644
--- a/demo-cxx/demo.h
+++ b/demo-cxx/demo.h
@@ -15,10 +15,12 @@
};
struct SharedThing;
+struct JsonBlob;
-std::unique_ptr<ThingC> make_demo(rust::Str appname);
+std::unique_ptr<ThingC> make_demo(::rust::Str appname);
const std::string &get_name(const ThingC &thing);
-void do_thing(SharedThing state);
+std::unique_ptr<std::vector<uint8_t>> do_thing(SharedThing state);
+JsonBlob get_jb(const ::rust::Vec<uint8_t>& vec);
} // namespace example
} // namespace org
diff --git a/demo-rs/src/main.rs b/demo-rs/src/main.rs
index 66dfc79..8bf9926 100644
--- a/demo-rs/src/main.rs
+++ b/demo-rs/src/main.rs
@@ -6,13 +6,19 @@
x: UniquePtr<ThingC>,
}
+ struct JsonBlob {
+ json: UniquePtr<CxxString>,
+ blob: UniquePtr<Vector<u8>>,
+ }
+
extern "C" {
include!("demo-cxx/demo.h");
type ThingC;
fn make_demo(appname: &str) -> UniquePtr<ThingC>;
fn get_name(thing: &ThingC) -> &CxxString;
- fn do_thing(state: SharedThing);
+ fn do_thing(state: SharedThing) -> UniquePtr<Vector<u8>>;
+ fn get_jb(v: &Vec<u8>) -> JsonBlob;
}
extern "Rust" {
@@ -31,9 +37,24 @@
let x = ffi::make_demo("demo of cxx::bridge");
println!("this is a {}", ffi::get_name(x.as_ref().unwrap()));
- ffi::do_thing(ffi::SharedThing {
+ let vec = ffi::do_thing(ffi::SharedThing {
z: 222,
y: Box::new(ThingR(333)),
x,
});
+
+ println!("vec length = {}", vec.as_ref().unwrap().size());
+ for (i, v) in vec.as_ref().unwrap().into_iter().enumerate() {
+ println!("vec[{}] = {}", i, v);
+ }
+
+ let mut rv: Vec<u8> = Vec::new();
+ for _ in 0..1000 {
+ rv.push(33);
+ }
+ let jb = ffi::get_jb(&rv);
+ println!("json: {}", jb.json.as_ref().unwrap());
+ for (i, v) in jb.blob.as_ref().unwrap().into_iter().enumerate() {
+ println!("jb.blob[{}] = {}", i, v);
+ }
}
diff --git a/gen/include.rs b/gen/include.rs
index 8f38fe3..077d03e 100644
--- a/gen/include.rs
+++ b/gen/include.rs
@@ -36,6 +36,7 @@
pub exception: bool,
pub memory: bool,
pub string: bool,
+ pub vector: bool,
pub type_traits: bool,
pub utility: bool,
pub base_tsd: bool,
diff --git a/gen/write.rs b/gen/write.rs
index a8e3fd4..a74833f 100644
--- a/gen/write.rs
+++ b/gen/write.rs
@@ -1,8 +1,10 @@
use crate::gen::out::OutFile;
use crate::gen::{include, Opt};
use crate::syntax::atom::Atom::{self, *};
+use crate::syntax::mangled::ToMangled;
use crate::syntax::namespace::Namespace;
use crate::syntax::symbol::Symbol;
+use crate::syntax::typename::ToTypename;
use crate::syntax::{mangle, Api, ExternFn, ExternType, Signature, Struct, Type, Types, Var};
use proc_macro2::Ident;
use std::collections::HashMap;
@@ -42,7 +44,7 @@
Api::Struct(strct) => write_struct_decl(out, &strct.ident),
Api::CxxType(ety) => write_struct_using(out, &ety.ident),
Api::RustType(ety) => write_struct_decl(out, &ety.ident),
- _ => {}
+ _ => (),
}
}
@@ -123,8 +125,9 @@
},
Type::RustBox(_) => out.include.type_traits = true,
Type::UniquePtr(_) => out.include.memory = true,
+ Type::Vector(_) => out.include.vector = true,
Type::SliceRefU8(_) => out.include.cstdint = true,
- _ => {}
+ _ => (),
}
}
}
@@ -134,14 +137,18 @@
let mut needs_rust_str = false;
let mut needs_rust_slice = false;
let mut needs_rust_box = false;
+ let mut needs_rust_vec = false;
let mut needs_rust_fn = false;
- let mut needs_rust_isize = false;
for ty in types {
match ty {
Type::RustBox(_) => {
out.include.type_traits = true;
needs_rust_box = true;
}
+ Type::RustVec(_) => {
+ out.include.type_traits = true;
+ needs_rust_vec = true;
+ }
Type::Str(_) => {
out.include.cstdint = true;
out.include.string = true;
@@ -153,10 +160,6 @@
Type::Slice(_) | Type::SliceRefU8(_) => {
needs_rust_slice = true;
}
- ty if ty == Isize => {
- out.include.base_tsd = true;
- needs_rust_isize = true;
- }
ty if ty == RustString => {
out.include.array = true;
out.include.cstdint = true;
@@ -213,9 +216,9 @@
|| needs_rust_str
|| needs_rust_slice
|| needs_rust_box
+ || needs_rust_vec
|| needs_rust_fn
|| needs_rust_error
- || needs_rust_isize
|| needs_unsafe_bitcopy
|| needs_manually_drop
|| needs_maybe_uninit
@@ -233,9 +236,9 @@
write_header_section(out, needs_rust_str, "CXXBRIDGE02_RUST_STR");
write_header_section(out, needs_rust_slice, "CXXBRIDGE02_RUST_SLICE");
write_header_section(out, needs_rust_box, "CXXBRIDGE02_RUST_BOX");
+ write_header_section(out, needs_rust_vec, "CXXBRIDGE02_RUST_VEC");
write_header_section(out, needs_rust_fn, "CXXBRIDGE02_RUST_FN");
write_header_section(out, needs_rust_error, "CXXBRIDGE02_RUST_ERROR");
- write_header_section(out, needs_rust_isize, "CXXBRIDGE02_RUST_ISIZE");
write_header_section(out, needs_unsafe_bitcopy, "CXXBRIDGE02_RUST_BITCOPY");
if needs_manually_drop {
@@ -469,6 +472,10 @@
match &efn.ret {
Some(Type::RustBox(_)) => write!(out, ".into_raw()"),
Some(Type::UniquePtr(_)) => write!(out, ".release()"),
+ Some(Type::Vector(_)) => write!(
+ out,
+ " /* Use RVO to convert to r-value and move construct */"
+ ),
Some(Type::Str(_)) | Some(Type::SliceRefU8(_)) if !indirect_return => write!(out, ")"),
_ => {}
}
@@ -779,7 +786,7 @@
fn write_extern_arg(out: &mut OutFile, arg: &Var, types: &Types) {
match &arg.ty {
- Type::RustBox(ty) | Type::UniquePtr(ty) => {
+ Type::RustBox(ty) | Type::UniquePtr(ty) | Type::Vector(ty) => {
write_type_space(out, &ty.inner);
write!(out, "*");
}
@@ -796,21 +803,7 @@
fn write_type(out: &mut OutFile, ty: &Type) {
match ty {
Type::Ident(ident) => match Atom::from(ident) {
- Some(Bool) => write!(out, "bool"),
- Some(U8) => write!(out, "uint8_t"),
- Some(U16) => write!(out, "uint16_t"),
- Some(U32) => write!(out, "uint32_t"),
- Some(U64) => write!(out, "uint64_t"),
- Some(Usize) => write!(out, "size_t"),
- Some(I8) => write!(out, "int8_t"),
- Some(I16) => write!(out, "int16_t"),
- Some(I32) => write!(out, "int32_t"),
- Some(I64) => write!(out, "int64_t"),
- Some(Isize) => write!(out, "::rust::isize"),
- Some(F32) => write!(out, "float"),
- Some(F64) => write!(out, "double"),
- Some(CxxString) => write!(out, "::std::string"),
- Some(RustString) => write!(out, "::rust::String"),
+ Some(a) => write!(out, "{}", a.to_cxx()),
None => write!(out, "{}", ident),
},
Type::RustBox(ty) => {
@@ -818,11 +811,21 @@
write_type(out, &ty.inner);
write!(out, ">");
}
+ Type::RustVec(ty) => {
+ write!(out, "::rust::Vec<");
+ write_type(out, &ty.inner);
+ write!(out, ">");
+ }
Type::UniquePtr(ptr) => {
write!(out, "::std::unique_ptr<");
write_type(out, &ptr.inner);
write!(out, ">");
}
+ Type::Vector(ty) => {
+ write!(out, "::std::vector<");
+ write_type(out, &ty.inner);
+ write!(out, ">");
+ }
Type::Ref(r) => {
if r.mutability.is_none() {
write!(out, "const ");
@@ -870,6 +873,8 @@
| Type::RustBox(_)
| Type::UniquePtr(_)
| Type::Str(_)
+ | Type::Vector(_)
+ | Type::RustVec(_)
| Type::SliceRefU8(_)
| Type::Fn(_) => write!(out, " "),
Type::Ref(_) => {}
@@ -882,6 +887,11 @@
Atom::from(ident).is_none()
}
+ fn allow_vector(ident: &Ident) -> bool {
+ // Note: built-in types such as u8 are already defined in cxx.cc
+ Atom::from(ident).is_none()
+ }
+
out.begin_block("extern \"C\"");
for ty in types {
if let Type::RustBox(ty) = ty {
@@ -889,11 +899,30 @@
out.next_section();
write_rust_box_extern(out, inner);
}
+ } else if let Type::RustVec(ty) = ty {
+ if let Type::Ident(_) = &ty.inner {
+ out.next_section();
+ write_rust_vec_extern(out, &ty.inner);
+ }
} else if let Type::UniquePtr(ptr) = ty {
if let Type::Ident(inner) = &ptr.inner {
if allow_unique_ptr(inner) {
out.next_section();
- write_unique_ptr(out, inner, types);
+ write_unique_ptr(out, &ptr.inner, types);
+ }
+ } else if let Type::Vector(ptr1) = &ptr.inner {
+ if let Type::Ident(inner) = &ptr1.inner {
+ if allow_vector(inner) {
+ out.next_section();
+ write_unique_ptr(out, &ptr.inner, types);
+ }
+ }
+ }
+ } else if let Type::Vector(ptr) = ty {
+ if let Type::Ident(inner) = &ptr.inner {
+ if allow_vector(inner) {
+ out.next_section();
+ write_vector(out, inner);
}
}
}
@@ -907,6 +936,10 @@
if let Type::Ident(inner) = &ty.inner {
write_rust_box_impl(out, inner);
}
+ } else if let Type::RustVec(ty) = ty {
+ if let Type::Ident(_) = &ty.inner {
+ write_rust_vec_impl(out, &ty.inner);
+ }
}
}
out.end_block("namespace cxxbridge02");
@@ -937,6 +970,31 @@
writeln!(out, "#endif // CXXBRIDGE02_RUST_BOX_{}", instance);
}
+fn write_rust_vec_extern(out: &mut OutFile, ty: &Type) {
+ let namespace = out.namespace.iter().cloned().collect::<Vec<String>>();
+ let inner = ty.to_typename(&namespace);
+ let instance = ty.to_mangled(&namespace);
+
+ writeln!(out, "#ifndef CXXBRIDGE02_RUST_VEC_{}", instance);
+ writeln!(out, "#define CXXBRIDGE02_RUST_VEC_{}", instance);
+ writeln!(
+ out,
+ "void cxxbridge02$rust_vec${}$drop(::rust::Vec<{}> *ptr) noexcept;",
+ instance, inner,
+ );
+ writeln!(
+ out,
+ "void cxxbridge02$rust_vec${}$vector_from(const ::rust::Vec<{}> *ptr, const std::vector<{}> &vector) noexcept;",
+ instance, inner, inner
+ );
+ writeln!(
+ out,
+ "size_t cxxbridge02$rust_vec${}$len(const ::rust::Vec<{}> *ptr) noexcept;",
+ instance, inner,
+ );
+ writeln!(out, "#endif // CXXBRIDGE02_RUST_VEC_{}", instance);
+}
+
fn write_rust_box_impl(out: &mut OutFile, ident: &Ident) {
let mut inner = String::new();
for name in &out.namespace {
@@ -957,16 +1015,44 @@
writeln!(out, "}}");
}
-fn write_unique_ptr(out: &mut OutFile, ident: &Ident, types: &Types) {
- out.include.utility = true;
+fn write_rust_vec_impl(out: &mut OutFile, ty: &Type) {
+ let namespace = out.namespace.iter().cloned().collect::<Vec<String>>();
+ let inner = ty.to_typename(&namespace);
+ let instance = ty.to_mangled(&namespace);
- let mut inner = String::new();
- for name in &out.namespace {
- inner += name;
- inner += "::";
- }
- inner += &ident.to_string();
- let instance = inner.replace("::", "$");
+ writeln!(out, "template <>");
+ writeln!(out, "void Vec<{}>::drop() noexcept {{", inner);
+ writeln!(
+ out,
+ " return cxxbridge02$rust_vec${}$drop(this);",
+ instance
+ );
+ writeln!(out, "}}");
+
+ writeln!(out, "template <>");
+ writeln!(out, "size_t Vec<{}>::size() const noexcept {{", inner);
+ writeln!(out, " return cxxbridge02$rust_vec${}$len(this);", instance);
+ writeln!(out, "}}");
+
+ writeln!(out, "template <>");
+ writeln!(
+ out,
+ "Vec<{}>::operator std::vector<{}>() const noexcept {{",
+ inner, inner
+ );
+ writeln!(
+ out,
+ " std::vector<{}> v; v.reserve(this->size()); cxxbridge02$rust_vec${}$vector_from(this, v); return v;",
+ inner, instance,
+ );
+ writeln!(out, "}}");
+}
+
+fn write_unique_ptr(out: &mut OutFile, ty: &Type, types: &Types) {
+ out.include.utility = true;
+ let namespace = out.namespace.iter().cloned().collect::<Vec<String>>();
+ let inner = ty.to_typename(&namespace);
+ let instance = ty.to_mangled(&namespace);
writeln!(out, "#ifndef CXXBRIDGE02_UNIQUE_PTR_{}", instance);
writeln!(out, "#define CXXBRIDGE02_UNIQUE_PTR_{}", instance);
@@ -987,18 +1073,21 @@
);
writeln!(out, " new (ptr) ::std::unique_ptr<{}>();", inner);
writeln!(out, "}}");
- if types.structs.contains_key(ident) {
- writeln!(
+ match ty {
+ Type::Ident(ident) if types.structs.contains_key(ident) => {
+ writeln!(
out,
"void cxxbridge02$unique_ptr${}$new(::std::unique_ptr<{}> *ptr, {} *value) noexcept {{",
instance, inner, inner,
);
- writeln!(
- out,
- " new (ptr) ::std::unique_ptr<{}>(new {}(::std::move(*value)));",
- inner, inner,
- );
- writeln!(out, "}}");
+ writeln!(
+ out,
+ " new (ptr) ::std::unique_ptr<{}>(new {}(::std::move(*value)));",
+ inner, inner,
+ );
+ writeln!(out, "}}");
+ }
+ _ => (),
}
writeln!(
out,
@@ -1030,3 +1119,50 @@
writeln!(out, "}}");
writeln!(out, "#endif // CXXBRIDGE02_UNIQUE_PTR_{}", instance);
}
+
+fn write_vector(out: &mut OutFile, ident: &Ident) {
+ let mut inner = String::new();
+ // Do not apply namespace to built-in type
+ let is_user_type = Atom::from(ident).is_none();
+ if is_user_type {
+ for name in &out.namespace {
+ inner += name;
+ inner += "::";
+ }
+ }
+ let mut instance = inner.clone();
+ if let Some(ti) = Atom::from(ident) {
+ inner += ti.to_cxx();
+ } else {
+ inner += &ident.to_string();
+ };
+ instance += &ident.to_string();
+ let instance = instance.replace("::", "$");
+
+ writeln!(out, "#ifndef CXXBRIDGE02_vector_{}", instance);
+ writeln!(out, "#define CXXBRIDGE02_vector_{}", instance);
+ writeln!(
+ out,
+ "size_t cxxbridge02$std$vector${}$length(const std::vector<{}> &s) noexcept {{",
+ instance, inner,
+ );
+ writeln!(out, " return s.size();");
+ writeln!(out, "}}");
+
+ writeln!(
+ out,
+ "void cxxbridge02$std$vector${}$push_back(std::vector<{}> &s, const {} &item) noexcept {{",
+ instance, inner, inner
+ );
+ writeln!(out, " s.push_back(item);");
+ writeln!(out, "}}");
+
+ writeln!(
+ out,
+ "const {} *cxxbridge02$std$vector${}$get_unchecked(const std::vector<{}> &s, size_t pos) noexcept {{",
+ inner, instance, inner,
+ );
+ writeln!(out, " return &s[pos];");
+ writeln!(out, "}}");
+ writeln!(out, "#endif // CXXBRIDGE02_vector_{}", instance);
+}
diff --git a/include/cxx.h b/include/cxx.h
index 637372a..35e9059 100644
--- a/include/cxx.h
+++ b/include/cxx.h
@@ -5,6 +5,7 @@
#include <exception>
#include <iosfwd>
#include <string>
+#include <vector>
#include <type_traits>
#include <utility>
#if defined(_WIN32)
@@ -83,6 +84,27 @@
};
#endif // CXXBRIDGE02_RUST_STR
+#ifndef CXXBRIDGE02_RUST_VEC
+#define CXXBRIDGE02_RUST_VEC
+template <typename T>
+class Vec final {
+public:
+ size_t size() const noexcept;
+ explicit operator std::vector<T>() const noexcept;
+
+private:
+ Vec() noexcept;
+ Vec(const Vec &other) noexcept;
+ Vec &operator=(Vec other) noexcept;
+ void drop() noexcept;
+
+ // Repr
+ const T *ptr;
+ size_t len;
+ size_t capacity;
+};
+#endif // CXXBRIDGE02_RUST_VEC
+
#ifndef CXXBRIDGE02_RUST_SLICE
#define CXXBRIDGE02_RUST_SLICE
template <typename T>
@@ -234,14 +256,11 @@
};
#endif // CXXBRIDGE02_RUST_ERROR
-#ifndef CXXBRIDGE02_RUST_ISIZE
-#define CXXBRIDGE02_RUST_ISIZE
#if defined(_WIN32)
using isize = SSIZE_T;
#else
using isize = ssize_t;
#endif
-#endif // CXXBRIDGE02_RUST_ISIZE
std::ostream &operator<<(std::ostream &, const String &);
std::ostream &operator<<(std::ostream &, const Str &);
diff --git a/macro/Cargo.toml b/macro/Cargo.toml
index ca0e326..99ae923 100644
--- a/macro/Cargo.toml
+++ b/macro/Cargo.toml
@@ -19,7 +19,7 @@
syn = { version = "1.0", features = ["full"] }
[dev-dependencies]
-cxx = { version = "0.2", path = ".." }
+cxx = { version = "0.2.7-alpha-1", path = ".." }
[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]
diff --git a/macro/src/expand.rs b/macro/src/expand.rs
index 8b3602a..b329276 100644
--- a/macro/src/expand.rs
+++ b/macro/src/expand.rs
@@ -1,12 +1,14 @@
use crate::syntax::atom::Atom::{self, *};
+use crate::syntax::mangled::ToMangled;
use crate::syntax::namespace::Namespace;
use crate::syntax::symbol::Symbol;
+use crate::syntax::typename::ToTypename;
use crate::syntax::{
self, check, mangle, Api, ExternFn, ExternType, Signature, Struct, Type, Types,
};
use proc_macro2::{Ident, Span, TokenStream};
use quote::{format_ident, quote, quote_spanned, ToTokens};
-use syn::{parse_quote, Error, ItemMod, Result, Token};
+use syn::{parse_quote, spanned::Spanned, Error, ItemMod, Result, Token};
pub fn bridge(namespace: &Namespace, ffi: ItemMod) -> Result<TokenStream> {
let ident = &ffi.ident;
@@ -22,6 +24,38 @@
let mut hidden = TokenStream::new();
let mut has_rust_type = false;
+ // "Header" to define newtypes locally so we can implement
+ // traits on them.
+ expanded.extend(quote! {
+ pub struct Vector<T>(pub ::cxx::RealVector<T>);
+ impl<T: cxx::private::VectorTarget<T>> Vector<T> {
+ pub fn size(&self) -> usize {
+ self.0.size()
+ }
+ pub fn get(&self, pos: usize) -> Option<&T> {
+ self.0.get(pos)
+ }
+ pub fn get_unchecked(&self, pos: usize) -> &T {
+ self.0.get_unchecked(pos)
+ }
+ pub fn is_empty(&self) -> bool {
+ self.0.is_empty()
+ }
+ pub fn push_back(&mut self, item: &T) {
+ self.0.push_back(item)
+ }
+ }
+ impl<'a, T: cxx::private::VectorTarget<T>> IntoIterator for &'a Vector<T> {
+ type Item = &'a T;
+ type IntoIter = <&'a ::cxx::RealVector<T> as IntoIterator>::IntoIter;
+
+ fn into_iter(self) -> Self::IntoIter {
+ self.0.into_iter()
+ }
+ }
+ unsafe impl<T> Send for Vector<T> where T: Send + cxx::private::VectorTarget<T> {}
+ });
+
for api in &apis {
if let Api::RustType(ety) = api {
expanded.extend(expand_rust_type(ety));
@@ -58,10 +92,34 @@
hidden.extend(expand_rust_box(namespace, ident));
}
}
+ } else if let Type::RustVec(ty) = ty {
+ if let Type::Ident(ident) = &ty.inner {
+ hidden.extend(expand_rust_vec(namespace, &ty.inner, ident));
+ }
} else if let Type::UniquePtr(ptr) = ty {
if let Type::Ident(ident) = &ptr.inner {
if Atom::from(ident).is_none() {
- expanded.extend(expand_unique_ptr(namespace, ident, types));
+ expanded.extend(expand_unique_ptr(namespace, &ptr.inner, types));
+ }
+ } else if let Type::Vector(_) = &ptr.inner {
+ // Generate code for unique_ptr<vector<T>> if T is not an atom
+ // or if T is a primitive.
+ // Code for primitives is already generated
+ match Atom::from(ident) {
+ None => expanded.extend(expand_unique_ptr(namespace, &ptr.inner, types)),
+ Some(atom) => {
+ if atom.is_valid_vector_target() {
+ expanded.extend(expand_unique_ptr(namespace, &ptr.inner, types));
+ }
+ }
+ }
+ }
+ } else if let Type::Vector(ptr) = ty {
+ if let Type::Ident(ident) = &ptr.inner {
+ if Atom::from(ident).is_none() {
+ // Generate code for Vector<T> if T is not an atom
+ // Code for atoms is already generated
+ expanded.extend(expand_vector(namespace, &ptr.inner));
}
}
}
@@ -197,10 +255,12 @@
}
Type::RustBox(_) => quote!(::std::boxed::Box::into_raw(#var)),
Type::UniquePtr(_) => quote!(::cxx::UniquePtr::into_raw(#var)),
+ Type::RustVec(_) => quote!(::cxx::RustVec::from(#var)),
Type::Ref(ty) => match &ty.inner {
Type::Ident(ident) if ident == RustString => {
quote!(::cxx::private::RustString::from_ref(#var))
}
+ Type::RustVec(_) => quote!(::cxx::RustVec::from_ref(#var)),
_ => quote!(#var),
},
Type::Str(_) => quote!(::cxx::private::RustStr::from(#var)),
@@ -523,9 +583,42 @@
}
}
-fn expand_unique_ptr(namespace: &Namespace, ident: &Ident, types: &Types) -> TokenStream {
- let name = ident.to_string();
- let prefix = format!("cxxbridge02$unique_ptr${}{}$", namespace, ident);
+fn expand_rust_vec(namespace: &Namespace, ty: &Type, ident: &Ident) -> TokenStream {
+ let inner = ty;
+ let mangled = ty.to_mangled(&namespace.segments) + "$";
+ let link_prefix = format!("cxxbridge02$rust_vec${}", mangled);
+ let link_drop = format!("{}drop", link_prefix);
+ let link_vector_from = format!("{}vector_from", link_prefix);
+ let link_len = format!("{}len", link_prefix);
+
+ let local_prefix = format_ident!("{}__vec_", ident);
+ let local_drop = format_ident!("{}drop", local_prefix);
+ let local_vector_from = format_ident!("{}vector_from", local_prefix);
+ let local_len = format_ident!("{}len", local_prefix);
+
+ let span = ty.span();
+ quote_spanned! {span=>
+ #[doc(hidden)]
+ #[export_name = #link_drop]
+ unsafe extern "C" fn #local_drop(this: *mut ::cxx::RustVec<#inner>) {
+ std::ptr::drop_in_place(this);
+ }
+ #[export_name = #link_vector_from]
+ unsafe extern "C" fn #local_vector_from(this: *mut ::cxx::RustVec<#inner>, vector: *mut ::cxx::RealVector<#inner>) {
+ this.as_ref().unwrap().into_vector(vector.as_mut().unwrap());
+ }
+ #[export_name = #link_len]
+ unsafe extern "C" fn #local_len(this: *const ::cxx::RustVec<#inner>) -> usize {
+ this.as_ref().unwrap().len()
+ }
+ }
+}
+
+fn expand_unique_ptr(namespace: &Namespace, ty: &Type, types: &Types) -> TokenStream {
+ let name = ty.to_typename(&namespace.segments);
+ let inner = ty;
+ let mangled = ty.to_mangled(&namespace.segments) + "$";
+ let prefix = format!("cxxbridge02$unique_ptr${}", mangled);
let link_null = format!("{}null", prefix);
let link_new = format!("{}new", prefix);
let link_raw = format!("{}raw", prefix);
@@ -533,8 +626,8 @@
let link_release = format!("{}release", prefix);
let link_drop = format!("{}drop", prefix);
- let new_method = if types.structs.contains_key(ident) {
- Some(quote! {
+ let new_method = match ty {
+ Type::Ident(ident) if types.structs.contains_key(ident) => Some(quote! {
fn __new(mut value: Self) -> *mut ::std::ffi::c_void {
extern "C" {
#[link_name = #link_new]
@@ -544,13 +637,12 @@
unsafe { __new(&mut repr, &mut value) }
repr
}
- })
- } else {
- None
+ }),
+ _ => None,
};
quote! {
- unsafe impl ::cxx::private::UniquePtrTarget for #ident {
+ unsafe impl ::cxx::private::UniquePtrTarget for #inner {
const __NAME: &'static str = #name;
fn __null() -> *mut ::std::ffi::c_void {
extern "C" {
@@ -565,7 +657,7 @@
unsafe fn __raw(raw: *mut Self) -> *mut ::std::ffi::c_void {
extern "C" {
#[link_name = #link_raw]
- fn __raw(this: *mut *mut ::std::ffi::c_void, raw: *mut #ident);
+ fn __raw(this: *mut *mut ::std::ffi::c_void, raw: *mut #inner);
}
let mut repr = ::std::ptr::null_mut::<::std::ffi::c_void>();
__raw(&mut repr, raw);
@@ -574,14 +666,14 @@
unsafe fn __get(repr: *mut ::std::ffi::c_void) -> *const Self {
extern "C" {
#[link_name = #link_get]
- fn __get(this: *const *mut ::std::ffi::c_void) -> *const #ident;
+ fn __get(this: *const *mut ::std::ffi::c_void) -> *const #inner;
}
__get(&repr)
}
unsafe fn __release(mut repr: *mut ::std::ffi::c_void) -> *mut Self {
extern "C" {
#[link_name = #link_release]
- fn __release(this: *mut *mut ::std::ffi::c_void) -> *mut #ident;
+ fn __release(this: *mut *mut ::std::ffi::c_void) -> *mut #inner;
}
__release(&mut repr)
}
@@ -596,6 +688,90 @@
}
}
+fn expand_vector(namespace: &Namespace, ty: &Type) -> TokenStream {
+ let inner = ty;
+ let mangled = ty.to_mangled(&namespace.segments) + "$";
+ let prefix = format!("cxxbridge02$std$vector${}", mangled);
+ let link_length = format!("{}length", prefix);
+ let link_get_unchecked = format!("{}get_unchecked", prefix);
+ let link_push_back = format!("{}push_back", prefix);
+
+ quote! {
+ impl ::cxx::private::VectorTarget<#inner> for #inner {
+ fn get_unchecked(v: &::cxx::RealVector<#inner>, pos: usize) -> &#inner {
+ extern "C" {
+ #[link_name = #link_get_unchecked]
+ fn __get_unchecked(_: &::cxx::RealVector<#inner>, _: usize) -> &#inner;
+ }
+ unsafe {
+ __get_unchecked(v, pos)
+ }
+ }
+ fn vector_length(v: &::cxx::RealVector<#inner>) -> usize {
+ unsafe {
+ extern "C" {
+ #[link_name = #link_length]
+ fn __vector_length(_: &::cxx::RealVector<#inner>) -> usize;
+ }
+ __vector_length(v)
+ }
+ }
+ fn push_back(v: &::cxx::RealVector<#inner>, item: &#inner) {
+ unsafe {
+ extern "C" {
+ #[link_name = #link_push_back]
+ fn __push_back(_: &::cxx::RealVector<#inner>, _: &#inner) -> usize;
+ }
+ __push_back(v, item);
+ }
+ }
+ }
+ }
+}
+
+pub fn expand_vector_builtin(ident: Ident) -> TokenStream {
+ let ty = Type::Ident(ident);
+ let inner = &ty;
+ let namespace = Namespace { segments: vec![] };
+ let mangled = ty.to_mangled(&namespace.segments) + "$";
+ let prefix = format!("cxxbridge02$std$vector${}", mangled);
+ let link_length = format!("{}length", prefix);
+ let link_get_unchecked = format!("{}get_unchecked", prefix);
+ let link_push_back = format!("{}push_back", prefix);
+
+ quote! {
+ impl VectorTarget<#inner> for #inner {
+ fn get_unchecked(v: &RealVector<#inner>, pos: usize) -> &#inner {
+ extern "C" {
+ #[link_name = #link_get_unchecked]
+ fn __get_unchecked(_: &RealVector<#inner>, _: usize) -> &#inner;
+ }
+ unsafe {
+ __get_unchecked(v, pos)
+ }
+ }
+ fn vector_length(v: &RealVector<#inner>) -> usize {
+ unsafe {
+ extern "C" {
+ #[link_name = #link_length]
+ fn __vector_length(_: &RealVector<#inner>) -> usize;
+ }
+ __vector_length(v)
+ }
+ }
+ fn push_back(v: &RealVector<#inner>, item: &#inner) {
+ unsafe {
+ extern "C" {
+ #[link_name = #link_push_back]
+ fn __push_back(_: &RealVector<#inner>, _: &#inner) -> usize;
+ }
+ __push_back(v, item);
+ }
+ }
+ }
+ }
+}
+
fn expand_return_type(ret: &Option<Type>) -> TokenStream {
match ret {
Some(ret) => quote!(-> #ret),
@@ -613,11 +789,16 @@
match ty {
Type::Ident(ident) if ident == RustString => quote!(::cxx::private::RustString),
Type::RustBox(ty) | Type::UniquePtr(ty) => {
- let inner = &ty.inner;
+ let inner = expand_extern_type(&ty.inner);
quote!(*mut #inner)
}
+ Type::RustVec(ty) => quote!(::cxx::RustVec<#ty>),
Type::Ref(ty) => match &ty.inner {
Type::Ident(ident) if ident == RustString => quote!(&::cxx::private::RustString),
+ Type::RustVec(ty) => {
+ let inner = expand_extern_type(&ty.inner);
+ quote!(&::cxx::RustVec<#inner>)
+ }
_ => quote!(#ty),
},
Type::Str(_) => quote!(::cxx::private::RustStr),
diff --git a/macro/src/lib.rs b/macro/src/lib.rs
index b56f58e..87fe405 100644
--- a/macro/src/lib.rs
+++ b/macro/src/lib.rs
@@ -14,7 +14,7 @@
use crate::syntax::namespace::Namespace;
use proc_macro::TokenStream;
-use syn::{parse_macro_input, ItemMod};
+use syn::{parse_macro_input, Ident, ItemMod};
/// `#[cxx::bridge] mod ffi { ... }`
///
@@ -44,3 +44,9 @@
.unwrap_or_else(|err| err.to_compile_error())
.into()
}
+
+#[proc_macro]
+pub fn vector_builtin(input: TokenStream) -> TokenStream {
+ let ident = parse_macro_input!(input as Ident);
+ expand::expand_vector_builtin(ident).into()
+}
diff --git a/src/cxx.cc b/src/cxx.cc
index b64333c..916f525 100644
--- a/src/cxx.cc
+++ b/src/cxx.cc
@@ -3,6 +3,7 @@
#include <exception>
#include <iostream>
#include <memory>
+#include <vector>
#include <stdexcept>
template <typename Exception>
@@ -197,3 +198,50 @@
ptr->~unique_ptr();
}
} // extern "C"
+
+#define STD_VECTOR_OPS(RUST_TYPE, CXX_TYPE) \
+extern "C" { \
+size_t cxxbridge02$std$vector$##RUST_TYPE##$length(const std::vector<CXX_TYPE> &s) noexcept { \
+ return s.size(); \
+} \
+void cxxbridge02$std$vector$##RUST_TYPE##$push_back(std::vector<CXX_TYPE> &s, const CXX_TYPE &item) noexcept { \
+ s.push_back(item); \
+} \
+const CXX_TYPE *cxxbridge02$std$vector$##RUST_TYPE##$get_unchecked(const std::vector<CXX_TYPE> &s, size_t pos) noexcept { \
+ return &s[pos]; \
+} \
+static_assert(sizeof(::std::unique_ptr<std::vector<CXX_TYPE>>) == sizeof(void *), ""); \
+static_assert(alignof(::std::unique_ptr<std::vector<CXX_TYPE>>) == alignof(void *), ""); \
+void cxxbridge02$unique_ptr$std$vector$##RUST_TYPE##$null(::std::unique_ptr<std::vector<CXX_TYPE>> *ptr) noexcept { \
+ new (ptr) ::std::unique_ptr<std::vector<CXX_TYPE>>(); \
+} \
+void cxxbridge02$unique_ptr$std$vector$##RUST_TYPE##$new(::std::unique_ptr<std::vector<CXX_TYPE>> *ptr, std::vector<CXX_TYPE> *value) noexcept { \
+ new (ptr) ::std::unique_ptr<std::vector<CXX_TYPE>>(new std::vector<CXX_TYPE>(::std::move(*value))); \
+} \
+void cxxbridge02$unique_ptr$std$vector$##RUST_TYPE##$raw(::std::unique_ptr<std::vector<CXX_TYPE>> *ptr, std::vector<CXX_TYPE> *raw) noexcept { \
+ new (ptr) ::std::unique_ptr<std::vector<CXX_TYPE>>(raw); \
+} \
+const std::vector<CXX_TYPE> *cxxbridge02$unique_ptr$std$vector$##RUST_TYPE##$get(const ::std::unique_ptr<std::vector<CXX_TYPE>>& ptr) noexcept { \
+ return ptr.get(); \
+} \
+std::vector<CXX_TYPE> *cxxbridge02$unique_ptr$std$vector$##RUST_TYPE##$release(::std::unique_ptr<std::vector<CXX_TYPE>>& ptr) noexcept { \
+ return ptr.release(); \
+} \
+void cxxbridge02$unique_ptr$std$vector$##RUST_TYPE##$drop(::std::unique_ptr<std::vector<CXX_TYPE>> *ptr) noexcept { \
+ ptr->~unique_ptr(); \
+} \
+} // extern "C"
+
+STD_VECTOR_OPS(u8, uint8_t);
+STD_VECTOR_OPS(u16, uint16_t);
+STD_VECTOR_OPS(u32, uint32_t);
+STD_VECTOR_OPS(u64, uint64_t);
+STD_VECTOR_OPS(usize, size_t);
+STD_VECTOR_OPS(i8, int8_t);
+STD_VECTOR_OPS(i16, int16_t);
+STD_VECTOR_OPS(i32, int32_t);
+STD_VECTOR_OPS(i64, int64_t);
+STD_VECTOR_OPS(isize, rust::isize);
+STD_VECTOR_OPS(f32, float);
+STD_VECTOR_OPS(f64, double);
+
diff --git a/src/lib.rs b/src/lib.rs
index 74e103a..994c430 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -370,13 +370,18 @@
mod rust_sliceu8;
mod rust_str;
mod rust_string;
+mod rust_vec;
mod syntax;
mod unique_ptr;
mod unwind;
+mod vector;
pub use crate::cxx_string::CxxString;
pub use crate::exception::Exception;
+pub use crate::rust_vec::RustVec;
pub use crate::unique_ptr::UniquePtr;
+pub use crate::vector::RealVector;
+pub use crate::vector::VectorIntoIterator;
pub use cxxbridge_macro::bridge;
// Not public API.
@@ -390,6 +395,7 @@
pub use crate::rust_string::RustString;
pub use crate::unique_ptr::UniquePtrTarget;
pub use crate::unwind::catch_unwind;
+ pub use crate::vector::VectorTarget;
}
use crate::error::Result;
diff --git a/src/rust_vec.rs b/src/rust_vec.rs
new file mode 100644
index 0000000..a28570d
--- /dev/null
+++ b/src/rust_vec.rs
@@ -0,0 +1,39 @@
+use crate::vector::RealVector;
+use crate::vector::VectorTarget;
+
+#[repr(C)]
+pub struct RustVec<T: VectorTarget<T>> {
+ repr: Vec<T>,
+}
+
+impl<T: VectorTarget<T>> RustVec<T> {
+ pub fn from(v: Vec<T>) -> Self {
+ RustVec { repr: v }
+ }
+
+ pub fn from_ref(v: &Vec<T>) -> &Self {
+ unsafe { std::mem::transmute::<&Vec<T>, &RustVec<T>>(v) }
+ }
+
+ pub fn into_vec(self) -> Vec<T> {
+ self.repr
+ }
+
+ pub fn as_vec(&self) -> &Vec<T> {
+ &self.repr
+ }
+
+ pub fn as_mut_vec(&mut self) -> &mut Vec<T> {
+ &mut self.repr
+ }
+
+ pub fn len(&self) -> usize {
+ self.repr.len()
+ }
+
+ pub fn into_vector(&self, vec: &mut RealVector<T>) {
+ for item in &self.repr {
+ vec.push_back(item);
+ }
+ }
+}
diff --git a/src/vector.rs b/src/vector.rs
new file mode 100644
index 0000000..805bdbb
--- /dev/null
+++ b/src/vector.rs
@@ -0,0 +1,91 @@
+pub trait VectorTarget<T> {
+ fn get_unchecked(v: &RealVector<T>, pos: usize) -> &T
+ where
+ Self: Sized;
+ fn vector_length(v: &RealVector<T>) -> usize
+ where
+ Self: Sized;
+ fn push_back(v: &RealVector<T>, item: &T)
+ where
+ Self: Sized;
+}
+
+/// Binding to C++ `std::vector`.
+///
+/// # Invariants
+///
+/// As an invariant of this API and the static analysis of the cxx::bridge
+/// macro, in Rust code we can never obtain a `Vector` by value. C++'s vector
+/// requires a move constructor and may hold internal pointers, which is not
+/// compatible with Rust's move behavior. Instead in Rust code we will only ever
+/// look at a Vector through a reference or smart pointer, as in `&Vector`
+/// or `UniquePtr<Vector>`.
+#[repr(C)]
+pub struct RealVector<T> {
+ _private: [T; 0],
+}
+
+impl<T: VectorTarget<T>> RealVector<T> {
+ /// Returns the length of the vector in bytes.
+ pub fn size(&self) -> usize {
+ T::vector_length(self)
+ }
+
+ pub fn get_unchecked(&self, pos: usize) -> &T {
+ T::get_unchecked(self, pos)
+ }
+
+ /// Returns true if `self` has a length of zero bytes.
+ pub fn is_empty(&self) -> bool {
+ self.size() == 0
+ }
+
+ pub fn get(&self, pos: usize) -> Option<&T> {
+ if pos < self.size() {
+ Some(self.get_unchecked(pos))
+ } else {
+ None
+ }
+ }
+
+ pub fn push_back(&mut self, item: &T) {
+ T::push_back(self, item);
+ }
+}
+
+unsafe impl<T> Send for RealVector<T> where T: Send + VectorTarget<T> {}
+
+pub struct VectorIntoIterator<'a, T> {
+ v: &'a RealVector<T>,
+ index: usize,
+}
+
+impl<'a, T: VectorTarget<T>> IntoIterator for &'a RealVector<T> {
+ type Item = &'a T;
+ type IntoIter = VectorIntoIterator<'a, T>;
+
+ fn into_iter(self) -> Self::IntoIter {
+ VectorIntoIterator { v: self, index: 0 }
+ }
+}
+
+impl<'a, T: VectorTarget<T>> Iterator for VectorIntoIterator<'a, T> {
+ type Item = &'a T;
+ fn next(&mut self) -> Option<Self::Item> {
+ self.index = self.index + 1;
+ self.v.get(self.index - 1)
+ }
+}
+
+cxxbridge_macro::vector_builtin!(u8);
+cxxbridge_macro::vector_builtin!(u16);
+cxxbridge_macro::vector_builtin!(u32);
+cxxbridge_macro::vector_builtin!(u64);
+cxxbridge_macro::vector_builtin!(usize);
+cxxbridge_macro::vector_builtin!(i8);
+cxxbridge_macro::vector_builtin!(i16);
+cxxbridge_macro::vector_builtin!(i32);
+cxxbridge_macro::vector_builtin!(i64);
+cxxbridge_macro::vector_builtin!(isize);
+cxxbridge_macro::vector_builtin!(f32);
+cxxbridge_macro::vector_builtin!(f64);
diff --git a/syntax/atom.rs b/syntax/atom.rs
index eeea831..c68b3fe 100644
--- a/syntax/atom.rs
+++ b/syntax/atom.rs
@@ -42,6 +42,43 @@
_ => None,
}
}
+
+ pub fn to_cxx(&self) -> &'static str {
+ use self::Atom::*;
+ match self {
+ Bool => "bool",
+ U8 => "uint8_t",
+ U16 => "uint16_t",
+ U32 => "uint32_t",
+ U64 => "uint64_t",
+ Usize => "size_t",
+ I8 => "int8_t",
+ I16 => "int16_t",
+ I32 => "int32_t",
+ I64 => "int64_t",
+ Isize => "::rust::isize",
+ F32 => "float",
+ F64 => "double",
+ CxxString => "::std::string",
+ RustString => "::rust::String",
+ }
+ }
+
+ pub fn is_valid_vector_target(&self) -> bool {
+ use self::Atom::*;
+ *self == U8
+ || *self == U16
+ || *self == U32
+ || *self == U64
+ || *self == Usize
+ || *self == I8
+ || *self == I16
+ || *self == I32
+ || *self == I64
+ || *self == Isize
+ || *self == F32
+ || *self == F64
+ }
}
impl PartialEq<Atom> for Ident {
diff --git a/syntax/check.rs b/syntax/check.rs
index 5e8cc09..387f9a6 100644
--- a/syntax/check.rs
+++ b/syntax/check.rs
@@ -29,7 +29,9 @@
match ty {
Type::Ident(ident) => check_type_ident(cx, ident),
Type::RustBox(ptr) => check_type_box(cx, ptr),
+ Type::RustVec(ptr) => check_type_vec(cx, ptr),
Type::UniquePtr(ptr) => check_type_unique_ptr(cx, ptr),
+ Type::Vector(ptr) => check_type_vector(cx, ptr),
Type::Ref(ty) => check_type_ref(cx, ty),
Type::Slice(ty) => check_type_slice(cx, ty),
_ => {}
@@ -87,6 +89,21 @@
cx.error(ptr, "unsupported target type of Box");
}
+fn check_type_vec(cx: &mut Check, ptr: &Ty1) {
+ // Vec can contain either user-defined type or u8
+ if let Type::Ident(ident) = &ptr.inner {
+ if Atom::from(ident).map(|a| a.is_valid_vector_target()) == Some(true) {
+ return;
+ } else if cx.types.cxx.contains(ident) {
+ cx.error(ptr, error::VEC_CXX_TYPE.msg);
+ } else {
+ return;
+ }
+ }
+
+ cx.error(ptr, "unsupported target type of Vec");
+}
+
fn check_type_unique_ptr(cx: &mut Check, ptr: &Ty1) {
if let Type::Ident(ident) = &ptr.inner {
if cx.types.rust.contains(ident) {
@@ -97,11 +114,31 @@
None | Some(CxxString) => return,
_ => {}
}
+ } else if let Type::Vector(_) = &ptr.inner {
+ return;
}
cx.error(ptr, "unsupported unique_ptr target type");
}
+fn check_type_vector(cx: &mut Check, ptr: &Ty1) {
+ if let Type::Ident(ident) = &ptr.inner {
+ if cx.types.rust.contains(ident) {
+ cx.error(ptr, "vector of a Rust type is not supported yet");
+ }
+
+ match Atom::from(ident) {
+ None => return,
+ Some(atom) => {
+ if atom.is_valid_vector_target() {
+ return;
+ }
+ }
+ }
+ }
+ cx.error(ptr, "unsupported vector target type");
+}
+
fn check_type_ref(cx: &mut Check, ty: &Ref) {
if ty.lifetime.is_some() {
cx.error(ty, "references with explicit lifetimes are not supported");
@@ -310,9 +347,11 @@
}
}
Type::RustBox(_) => "Box".to_owned(),
+ Type::RustVec(_) => "Vec".to_owned(),
Type::UniquePtr(_) => "unique_ptr".to_owned(),
Type::Ref(_) => "reference".to_owned(),
Type::Str(_) => "&str".to_owned(),
+ Type::Vector(_) => "vector".to_owned(),
Type::Slice(_) => "slice".to_owned(),
Type::SliceRefU8(_) => "&[u8]".to_owned(),
Type::Fn(_) => "function pointer".to_owned(),
diff --git a/syntax/error.rs b/syntax/error.rs
index f52d651..103a54f 100644
--- a/syntax/error.rs
+++ b/syntax/error.rs
@@ -15,6 +15,7 @@
pub static ERRORS: &[Error] = &[
BOX_CXX_TYPE,
+ VEC_CXX_TYPE,
CXXBRIDGE_RESERVED,
CXX_STRING_BY_VALUE,
CXX_TYPE_BY_VALUE,
@@ -29,6 +30,12 @@
note: Some("hint: use UniquePtr<>"),
};
+pub static VEC_CXX_TYPE: Error = Error {
+ msg: "Vec of a C++ type is not supported yet",
+ label: None,
+ note: Some("hint: use UniquePtr<>"),
+};
+
pub static CXXBRIDGE_RESERVED: Error = Error {
msg: "identifiers starting with cxxbridge are reserved",
label: Some("reserved identifier"),
diff --git a/syntax/impls.rs b/syntax/impls.rs
index a2ce05b..8e94f06 100644
--- a/syntax/impls.rs
+++ b/syntax/impls.rs
@@ -26,6 +26,8 @@
Type::UniquePtr(t) => t.hash(state),
Type::Ref(t) => t.hash(state),
Type::Str(t) => t.hash(state),
+ Type::RustVec(t) => t.hash(state),
+ Type::Vector(t) => t.hash(state),
Type::Fn(t) => t.hash(state),
Type::Slice(t) => t.hash(state),
Type::SliceRefU8(t) => t.hash(state),
@@ -44,6 +46,8 @@
(Type::UniquePtr(lhs), Type::UniquePtr(rhs)) => lhs == rhs,
(Type::Ref(lhs), Type::Ref(rhs)) => lhs == rhs,
(Type::Str(lhs), Type::Str(rhs)) => lhs == rhs,
+ (Type::RustVec(lhs), Type::RustVec(rhs)) => lhs == rhs,
+ (Type::Vector(lhs), Type::Vector(rhs)) => lhs == rhs,
(Type::Fn(lhs), Type::Fn(rhs)) => lhs == rhs,
(Type::Slice(lhs), Type::Slice(rhs)) => lhs == rhs,
(Type::SliceRefU8(lhs), Type::SliceRefU8(rhs)) => lhs == rhs,
diff --git a/syntax/mangled.rs b/syntax/mangled.rs
new file mode 100644
index 0000000..56e8b73
--- /dev/null
+++ b/syntax/mangled.rs
@@ -0,0 +1,30 @@
+use crate::syntax::{Atom, Type};
+
+pub trait ToMangled {
+ fn to_mangled(&self, namespace: &Vec<String>) -> String;
+}
+
+impl ToMangled for Type {
+ fn to_mangled(&self, namespace: &Vec<String>) -> String {
+ match self {
+ Type::Ident(ident) => {
+ let mut instance = String::new();
+ // Do not apply namespace to built-in type
+ let is_user_type = Atom::from(ident).is_none();
+ if is_user_type {
+ for name in namespace {
+ instance += name;
+ instance += "$";
+ }
+ }
+ instance += &ident.to_string();
+ instance
+ }
+ Type::RustBox(ptr) => format!("rust_box${}", ptr.inner.to_mangled(namespace)),
+ Type::RustVec(ptr) => format!("rust_vec${}", ptr.inner.to_mangled(namespace)),
+ Type::UniquePtr(ptr) => format!("std$unique_ptr${}", ptr.inner.to_mangled(namespace)),
+ Type::Vector(ptr) => format!("std$vector${}", ptr.inner.to_mangled(namespace)),
+ _ => unimplemented!(),
+ }
+ }
+}
diff --git a/syntax/mod.rs b/syntax/mod.rs
index ab5cecc..4e0b908 100644
--- a/syntax/mod.rs
+++ b/syntax/mod.rs
@@ -8,11 +8,13 @@
pub mod ident;
mod impls;
pub mod mangle;
+pub mod mangled;
pub mod namespace;
mod parse;
pub mod set;
pub mod symbol;
mod tokens;
+pub mod typename;
pub mod types;
use self::parse::kw;
@@ -86,9 +88,11 @@
pub enum Type {
Ident(Ident),
RustBox(Box<Ty1>),
+ RustVec(Box<Ty1>),
UniquePtr(Box<Ty1>),
Ref(Box<Ref>),
Str(Box<Ref>),
+ Vector(Box<Ty1>),
Fn(Box<Signature>),
Void(Span),
Slice(Box<Slice>),
diff --git a/syntax/namespace.rs b/syntax/namespace.rs
index d26bb9e..a4e972b 100644
--- a/syntax/namespace.rs
+++ b/syntax/namespace.rs
@@ -11,7 +11,7 @@
#[derive(Clone)]
pub struct Namespace {
- segments: Vec<String>,
+ pub segments: Vec<String>,
}
impl Namespace {
diff --git a/syntax/parse.rs b/syntax/parse.rs
index 7d92b1b..f5c598d 100644
--- a/syntax/parse.rs
+++ b/syntax/parse.rs
@@ -300,6 +300,16 @@
rangle: generic.gt_token,
})));
}
+ } else if ident == "Vector" && generic.args.len() == 1 {
+ if let GenericArgument::Type(arg) = &generic.args[0] {
+ let inner = parse_type(arg)?;
+ return Ok(Type::Vector(Box::new(Ty1 {
+ name: ident,
+ langle: generic.lt_token,
+ inner,
+ rangle: generic.gt_token,
+ })));
+ }
} else if ident == "Box" && generic.args.len() == 1 {
if let GenericArgument::Type(arg) = &generic.args[0] {
let inner = parse_type(arg)?;
@@ -310,6 +320,16 @@
rangle: generic.gt_token,
})));
}
+ } else if ident == "Vec" && generic.args.len() == 1 {
+ if let GenericArgument::Type(arg) = &generic.args[0] {
+ let inner = parse_type(arg)?;
+ return Ok(Type::RustVec(Box::new(Ty1 {
+ name: ident,
+ langle: generic.lt_token,
+ inner,
+ rangle: generic.gt_token,
+ })));
+ }
}
}
PathArguments::Parenthesized(_) => {}
diff --git a/syntax/tokens.rs b/syntax/tokens.rs
index 26bb3d1..3d67a0a 100644
--- a/syntax/tokens.rs
+++ b/syntax/tokens.rs
@@ -14,7 +14,9 @@
}
ident.to_tokens(tokens);
}
- Type::RustBox(ty) | Type::UniquePtr(ty) => ty.to_tokens(tokens),
+ Type::RustBox(ty) | Type::UniquePtr(ty) | Type::Vector(ty) | Type::RustVec(ty) => {
+ ty.to_tokens(tokens)
+ }
Type::Ref(r) | Type::Str(r) | Type::SliceRefU8(r) => r.to_tokens(tokens),
Type::Slice(s) => s.to_tokens(tokens),
Type::Fn(f) => f.to_tokens(tokens),
@@ -33,7 +35,8 @@
impl ToTokens for Ty1 {
fn to_tokens(&self, tokens: &mut TokenStream) {
- if self.name == "UniquePtr" {
+ // Do not add cxx namespace to Vector since we're defining it in the user crate
+ if self.name == "UniquePtr" || self.name == "RustVec" {
let span = self.name.span();
tokens.extend(quote_spanned!(span=> ::cxx::));
}
diff --git a/syntax/typename.rs b/syntax/typename.rs
new file mode 100644
index 0000000..883e1fb
--- /dev/null
+++ b/syntax/typename.rs
@@ -0,0 +1,36 @@
+use crate::syntax::{Atom, Type};
+
+pub trait ToTypename {
+ fn to_typename(&self, namespace: &Vec<String>) -> String;
+}
+
+impl ToTypename for Type {
+ fn to_typename(&self, namespace: &Vec<String>) -> String {
+ match self {
+ Type::Ident(ident) => {
+ let mut inner = String::new();
+ // Do not apply namespace to built-in type
+ let is_user_type = Atom::from(ident).is_none();
+ if is_user_type {
+ for name in namespace {
+ inner += name;
+ inner += "::";
+ }
+ }
+ if let Some(ti) = Atom::from(ident) {
+ inner += ti.to_cxx();
+ } else {
+ inner += &ident.to_string();
+ };
+ inner
+ }
+ Type::RustBox(ptr) => format!("rust_box<{}>", ptr.inner.to_typename(namespace)),
+ Type::RustVec(ptr) => format!("rust_vec<{}>", ptr.inner.to_typename(namespace)),
+ Type::UniquePtr(ptr) => {
+ format!("std::unique_ptr<{}>", ptr.inner.to_typename(namespace))
+ }
+ Type::Vector(ptr) => format!("std::vector<{}>", ptr.inner.to_typename(namespace)),
+ _ => unimplemented!(),
+ }
+ }
+}
diff --git a/syntax/types.rs b/syntax/types.rs
index 6f3af09..7bce154 100644
--- a/syntax/types.rs
+++ b/syntax/types.rs
@@ -24,7 +24,9 @@
all.insert(ty);
match ty {
Type::Ident(_) | Type::Str(_) | Type::Void(_) | Type::SliceRefU8(_) => {}
- Type::RustBox(ty) | Type::UniquePtr(ty) => visit(all, &ty.inner),
+ Type::RustBox(ty) | Type::UniquePtr(ty) | Type::Vector(ty) | Type::RustVec(ty) => {
+ visit(all, &ty.inner)
+ }
Type::Ref(r) => visit(all, &r.inner),
Type::Slice(s) => visit(all, &s.inner),
Type::Fn(f) => {
diff --git a/tests/ffi/lib.rs b/tests/ffi/lib.rs
index efdd1fa..27f5f6d 100644
--- a/tests/ffi/lib.rs
+++ b/tests/ffi/lib.rs
@@ -27,6 +27,9 @@
fn c_return_sliceu8(shared: &Shared) -> &[u8];
fn c_return_rust_string() -> String;
fn c_return_unique_ptr_string() -> UniquePtr<CxxString>;
+ fn c_return_unique_ptr_vector_u8() -> UniquePtr<Vector<u8>>;
+ fn c_return_unique_ptr_vector_f64() -> UniquePtr<Vector<f64>>;
+ fn c_return_unique_ptr_vector_shared() -> UniquePtr<Vector<Shared>>;
fn c_take_primitive(n: usize);
fn c_take_shared(shared: Shared);
@@ -38,11 +41,19 @@
fn c_take_sliceu8(s: &[u8]);
fn c_take_rust_string(s: String);
fn c_take_unique_ptr_string(s: UniquePtr<CxxString>);
+ fn c_take_unique_ptr_vector_u8(s: UniquePtr<Vector<u8>>);
+ fn c_take_unique_ptr_vector_f64(s: UniquePtr<Vector<f64>>);
+ fn c_take_unique_ptr_vector_shared(s: UniquePtr<Vector<Shared>>);
+
+ fn c_take_vec_u8(v: &Vec<u8>);
+ fn c_take_vec_shared(v: &Vec<Shared>);
fn c_take_callback(callback: fn(String) -> usize);
fn c_try_return_void() -> Result<()>;
fn c_try_return_primitive() -> Result<usize>;
fn c_fail_return_primitive() -> Result<usize>;
+ fn c_try_return_string() -> Result<UniquePtr<CxxString>>;
+ fn c_fail_return_string() -> Result<UniquePtr<CxxString>>;
fn c_try_return_box() -> Result<Box<R>>;
fn c_try_return_ref(s: &String) -> Result<&String>;
fn c_try_return_str(s: &str) -> Result<&str>;
diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc
index 619485a..9f11d9f 100644
--- a/tests/ffi/tests.cc
+++ b/tests/ffi/tests.cc
@@ -1,3 +1,4 @@
+#include <numeric>
#include "tests/ffi/tests.h"
#include "tests/ffi/lib.rs.h"
#include <cstring>
@@ -57,6 +58,31 @@
return std::unique_ptr<std::string>(new std::string("2020"));
}
+std::unique_ptr<std::vector<uint8_t>> c_return_unique_ptr_vector_u8() {
+ auto retval = std::unique_ptr<std::vector<uint8_t>>(new std::vector<uint8_t>());
+ retval->push_back(86);
+ retval->push_back(75);
+ retval->push_back(30);
+ retval->push_back(9);
+ return retval;
+}
+
+std::unique_ptr<std::vector<double>> c_return_unique_ptr_vector_f64() {
+ auto retval = std::unique_ptr<std::vector<double>>(new std::vector<double>());
+ retval->push_back(86.0);
+ retval->push_back(75.0);
+ retval->push_back(30.0);
+ retval->push_back(9.5);
+ return retval;
+}
+
+std::unique_ptr<std::vector<Shared>> c_return_unique_ptr_vector_shared() {
+ auto retval = std::unique_ptr<std::vector<Shared>>(new std::vector<Shared>());
+ retval->push_back(Shared{1010});
+ retval->push_back(Shared{1011});
+ return retval;
+}
+
void c_take_primitive(size_t n) {
if (n == 2020) {
cxx_test_suite_set_correct();
@@ -118,6 +144,43 @@
}
}
+void c_take_unique_ptr_vector_u8(std::unique_ptr<std::vector<uint8_t>> v) {
+ if (v->size() == 4) {
+ cxx_test_suite_set_correct();
+ }
+}
+
+void c_take_unique_ptr_vector_f64(std::unique_ptr<std::vector<double>> v) {
+ if (v->size() == 4) {
+ cxx_test_suite_set_correct();
+ }
+}
+
+void c_take_unique_ptr_vector_shared(std::unique_ptr<std::vector<Shared>> v) {
+ if (v->size() == 2) {
+ cxx_test_suite_set_correct();
+ }
+}
+
+void c_take_vec_u8(const ::rust::Vec<uint8_t>& v) {
+ auto cv = static_cast<std::vector<uint8_t>>(v);
+ uint8_t sum = std::accumulate(cv.begin(), cv.end(), 0);
+ if (sum == 200) {
+ cxx_test_suite_set_correct();
+ }
+}
+
+void c_take_vec_shared(const ::rust::Vec<Shared>& v) {
+ auto cv = static_cast<std::vector<Shared>>(v);
+ uint32_t sum = 0;
+ for (auto i: cv) {
+ sum += i.z;
+ }
+ if (sum == 2021) {
+ cxx_test_suite_set_correct();
+ }
+}
+
void c_take_callback(rust::Fn<size_t(rust::String)> callback) {
callback("2020");
}
@@ -128,6 +191,14 @@
size_t c_fail_return_primitive() { throw std::logic_error("logic error"); }
+std::unique_ptr<std::string> c_try_return_string() {
+ return std::unique_ptr<std::string>(new std::string("ok"));
+}
+
+std::unique_ptr<std::string> c_fail_return_string() {
+ throw std::logic_error("logic error getting string");
+}
+
rust::Box<R> c_try_return_box() { return c_return_box(); }
const rust::String &c_try_return_ref(const rust::String &s) { return s; }
diff --git a/tests/ffi/tests.h b/tests/ffi/tests.h
index ed6d541..9e75c37 100644
--- a/tests/ffi/tests.h
+++ b/tests/ffi/tests.h
@@ -29,6 +29,9 @@
rust::Slice<uint8_t> c_return_sliceu8(const Shared &shared);
rust::String c_return_rust_string();
std::unique_ptr<std::string> c_return_unique_ptr_string();
+std::unique_ptr<std::vector<uint8_t>> c_return_unique_ptr_vector_u8();
+std::unique_ptr<std::vector<double>> c_return_unique_ptr_vector_f64();
+std::unique_ptr<std::vector<Shared>> c_return_unique_ptr_vector_shared();
void c_take_primitive(size_t n);
void c_take_shared(Shared shared);
@@ -40,11 +43,19 @@
void c_take_sliceu8(rust::Slice<uint8_t> s);
void c_take_rust_string(rust::String s);
void c_take_unique_ptr_string(std::unique_ptr<std::string> s);
+void c_take_unique_ptr_vector_u8(std::unique_ptr<std::vector<uint8_t>> v);
+void c_take_unique_ptr_vector_f64(std::unique_ptr<std::vector<double>> v);
+void c_take_unique_ptr_vector_shared(std::unique_ptr<std::vector<Shared>> v);
+
+void c_take_vec_u8(const ::rust::Vec<uint8_t>& v);
+void c_take_vec_shared(const ::rust::Vec<Shared>& v);
void c_take_callback(rust::Fn<size_t(rust::String)> callback);
void c_try_return_void();
size_t c_try_return_primitive();
size_t c_fail_return_primitive();
+std::unique_ptr<std::string> c_try_return_string();
+std::unique_ptr<std::string> c_fail_return_string();
rust::Box<R> c_try_return_box();
const rust::String &c_try_return_ref(const rust::String &);
rust::Str c_try_return_str(rust::Str);
diff --git a/tests/test.rs b/tests/test.rs
index 1cd85df..17fcfca 100644
--- a/tests/test.rs
+++ b/tests/test.rs
@@ -32,6 +32,45 @@
assert_eq!(b"2020\0", ffi::c_return_sliceu8(&shared));
assert_eq!("2020", ffi::c_return_rust_string());
assert_eq!("2020", ffi::c_return_unique_ptr_string().to_str().unwrap());
+ assert_eq!(
+ 4,
+ ffi::c_return_unique_ptr_vector_u8()
+ .as_ref()
+ .unwrap()
+ .size()
+ );
+ assert_eq!(
+ 200_u8,
+ ffi::c_return_unique_ptr_vector_u8()
+ .as_ref()
+ .unwrap()
+ .into_iter()
+ .sum()
+ );
+ assert_eq!(
+ 200.5_f64,
+ ffi::c_return_unique_ptr_vector_f64()
+ .as_ref()
+ .unwrap()
+ .into_iter()
+ .sum()
+ );
+ assert_eq!(
+ 2,
+ ffi::c_return_unique_ptr_vector_shared()
+ .as_ref()
+ .unwrap()
+ .size()
+ );
+ assert_eq!(
+ 2021_usize,
+ ffi::c_return_unique_ptr_vector_shared()
+ .as_ref()
+ .unwrap()
+ .into_iter()
+ .map(|o| o.z)
+ .sum()
+ );
}
#[test]
@@ -42,6 +81,18 @@
"logic error",
ffi::c_fail_return_primitive().unwrap_err().what(),
);
+ assert_eq!(
+ "ok",
+ ffi::c_try_return_string()
+ .unwrap()
+ .as_ref()
+ .unwrap()
+ .to_string()
+ );
+ assert_eq!(
+ "logic error getting string",
+ ffi::c_fail_return_string().unwrap_err().what(),
+ );
assert_eq!(2020, *ffi::c_try_return_box().unwrap());
assert_eq!("2020", *ffi::c_try_return_ref(&"2020".to_owned()).unwrap());
assert_eq!("2020", ffi::c_try_return_str("2020").unwrap());
@@ -65,6 +116,22 @@
check!(ffi::c_take_unique_ptr_string(
ffi::c_return_unique_ptr_string()
));
+ check!(ffi::c_take_unique_ptr_vector_u8(
+ ffi::c_return_unique_ptr_vector_u8()
+ ));
+ check!(ffi::c_take_unique_ptr_vector_f64(
+ ffi::c_return_unique_ptr_vector_f64()
+ ));
+ check!(ffi::c_take_unique_ptr_vector_shared(
+ ffi::c_return_unique_ptr_vector_shared()
+ ));
+
+ check!(ffi::c_take_vec_u8(&[86_u8, 75_u8, 30_u8, 9_u8].to_vec()));
+
+ check!(ffi::c_take_vec_shared(&vec![
+ ffi::Shared { z: 1010 },
+ ffi::Shared { z: 1011 }
+ ]));
}
#[test]
diff --git a/third-party/Cargo.lock b/third-party/Cargo.lock
index 2e70499..4552f46 100644
--- a/third-party/Cargo.lock
+++ b/third-party/Cargo.lock
@@ -54,6 +54,15 @@
]
[[package]]
+name = "codespan"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "21094c000d5db8035900662bbfddec754e79f795324254ac0817f36e5ccfc3f5"
+dependencies = [
+ "unicode-segmentation",
+]
+
+[[package]]
name = "codespan-reporting"
version = "0.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -69,6 +78,7 @@
dependencies = [
"anyhow",
"cc",
+ "codespan",
"codespan-reporting",
"cxx-test-suite",
"cxxbridge-macro",