diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-18 02:49:50 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-18 02:49:50 +0000 |
commit | 9835e2ae736235810b4ea1c162ca5e65c547e770 (patch) | |
tree | 3fcebf40ed70e581d776a8a4c65923e8ec20e026 /vendor/cxx/book/src/binding | |
parent | Releasing progress-linux version 1.70.0+dfsg2-1~progress7.99u1. (diff) | |
download | rustc-9835e2ae736235810b4ea1c162ca5e65c547e770.tar.xz rustc-9835e2ae736235810b4ea1c162ca5e65c547e770.zip |
Merging upstream version 1.71.1+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'vendor/cxx/book/src/binding')
-rw-r--r-- | vendor/cxx/book/src/binding/box.md | 120 | ||||
-rw-r--r-- | vendor/cxx/book/src/binding/cxxstring.md | 140 | ||||
-rw-r--r-- | vendor/cxx/book/src/binding/cxxvector.md | 62 | ||||
-rw-r--r-- | vendor/cxx/book/src/binding/fn.md | 34 | ||||
-rw-r--r-- | vendor/cxx/book/src/binding/rawptr.md | 100 | ||||
-rw-r--r-- | vendor/cxx/book/src/binding/result.md | 148 | ||||
-rw-r--r-- | vendor/cxx/book/src/binding/sharedptr.md | 80 | ||||
-rw-r--r-- | vendor/cxx/book/src/binding/slice.md | 171 | ||||
-rw-r--r-- | vendor/cxx/book/src/binding/str.md | 118 | ||||
-rw-r--r-- | vendor/cxx/book/src/binding/string.md | 132 | ||||
-rw-r--r-- | vendor/cxx/book/src/binding/uniqueptr.md | 63 | ||||
-rw-r--r-- | vendor/cxx/book/src/binding/vec.md | 192 |
12 files changed, 1360 insertions, 0 deletions
diff --git a/vendor/cxx/book/src/binding/box.md b/vendor/cxx/book/src/binding/box.md new file mode 100644 index 000000000..7df195974 --- /dev/null +++ b/vendor/cxx/book/src/binding/box.md @@ -0,0 +1,120 @@ +{{#title rust::Box<T> — Rust ♡ C++}} +# rust::Box\<T\> + +### Public API: + +```cpp,hidelines +// rust/cxx.h +# +# #include <type_traits> +# +# namespace rust { + +template <typename T> +class Box final { +public: + using element_type = T; + using const_pointer = + typename std::add_pointer<typename std::add_const<T>::type>::type; + using pointer = typename std::add_pointer<T>::type; + + Box(Box &&) noexcept; + ~Box() noexcept; + + explicit Box(const T &); + explicit Box(T &&); + + Box &operator=(Box &&) noexcept; + + const T *operator->() const noexcept; + const T &operator*() const noexcept; + T *operator->() noexcept; + T &operator*() noexcept; + + template <typename... Fields> + static Box in_place(Fields &&...); + + void swap(Box &) noexcept; + + // Important: requires that `raw` came from an into_raw call. Do not + // pass a pointer from `new` or any other source. + static Box from_raw(T *) noexcept; + + T *into_raw() noexcept; +}; +# +# } // namespace rust +``` + +### Restrictions: + +Box\<T\> does not support T being an opaque C++ type. You should use +[UniquePtr\<T\>](uniqueptr.md) or [SharedPtr\<T\>](sharedptr.md) instead for +transferring ownership of opaque C++ types on the language boundary. + +If T is an opaque Rust type, the Rust type is required to be [Sized] i.e. size +known at compile time. In the future we may introduce support for dynamically +sized opaque Rust types. + +[Sized]: https://doc.rust-lang.org/std/marker/trait.Sized.html + +## Example + +This program uses a Box to pass ownership of some opaque piece of Rust state +over to C++ and then back to a Rust callback, which is a useful pattern for +implementing [async functions over FFI](../async.md). + +```rust,noplayground +// src/main.rs + +use std::io::Write; + +#[cxx::bridge] +mod ffi { + extern "Rust" { + type File; + } + + unsafe extern "C++" { + include!("example/include/example.h"); + + fn f( + callback: fn(Box<File>, fst: &str, snd: &str), + out: Box<File>, + ); + } +} + +pub struct File(std::fs::File); + +fn main() { + let out = std::fs::File::create("example.log").unwrap(); + + ffi::f( + |mut out, fst, snd| { let _ = write!(out.0, "{}{}\n", fst, snd); }, + Box::new(File(out)), + ); +} +``` + +```cpp +// include/example.h + +#pragma once +#include "example/src/main.rs.h" +#include "rust/cxx.h" + +void f(rust::Fn<void(rust::Box<File>, rust::Str, rust::Str)> callback, + rust::Box<File> out); +``` + +```cpp +// include/example.cc + +#include "example/include/example.h" + +void f(rust::Fn<void(rust::Box<File>, rust::Str, rust::Str)> callback, + rust::Box<File> out) { + callback(std::move(out), "fearless", "concurrency"); +} +``` diff --git a/vendor/cxx/book/src/binding/cxxstring.md b/vendor/cxx/book/src/binding/cxxstring.md new file mode 100644 index 000000000..cfe707f21 --- /dev/null +++ b/vendor/cxx/book/src/binding/cxxstring.md @@ -0,0 +1,140 @@ +{{#title std::string — Rust ♡ C++}} +# std::string + +The Rust binding of std::string is called **[`CxxString`]**. See the link for +documentation of the Rust API. + +[`CxxString`]: https://docs.rs/cxx/*/cxx/struct.CxxString.html + +### Restrictions: + +Rust code can never obtain a CxxString by value. C++'s string 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 CxxString +through a reference or smart pointer, as in &CxxString or Pin\<&mut CxxString\> +or UniquePtr\<CxxString\>. + +In order to construct a CxxString on the stack from Rust, you must use the +[`let_cxx_string!`] macro which will pin the string properly. The code below +uses this in one place, and the link covers the syntax. + +[`let_cxx_string!`]: https://docs.rs/cxx/*/cxx/macro.let_cxx_string.html + +## Example + +This example uses C++17's std::variant to build a toy JSON type. JSON can hold +various types including strings, and JSON's object type is a map with string +keys. The example demonstrates Rust indexing into one of those maps. + +```rust,noplayground +// src/main.rs + +use cxx::let_cxx_string; + +#[cxx::bridge] +mod ffi { + unsafe extern "C++" { + include!("example/include/json.h"); + + #[cxx_name = "json"] + type Json; + #[cxx_name = "object"] + type Object; + + fn isNull(self: &Json) -> bool; + fn isNumber(self: &Json) -> bool; + fn isString(self: &Json) -> bool; + fn isArray(self: &Json) -> bool; + fn isObject(self: &Json) -> bool; + + fn getNumber(self: &Json) -> f64; + fn getString(self: &Json) -> &CxxString; + fn getArray(self: &Json) -> &CxxVector<Json>; + fn getObject(self: &Json) -> &Object; + + #[cxx_name = "at"] + fn get<'a>(self: &'a Object, key: &CxxString) -> &'a Json; + + fn load_config() -> UniquePtr<Json>; + } +} + +fn main() { + let config = ffi::load_config(); + + let_cxx_string!(key = "name"); + println!("{}", config.getObject().get(&key).getString()); +} +``` + +```cpp +// include/json.h + +#pragma once +#include <map> +#include <memory> +#include <string> +#include <variant> +#include <vector> + +class json final { +public: + static const json null; + using number = double; + using string = std::string; + using array = std::vector<json>; + using object = std::map<string, json>; + + json() noexcept = default; + json(const json &) = default; + json(json &&) = default; + template <typename... T> + json(T &&...value) : value(std::forward<T>(value)...) {} + + bool isNull() const; + bool isNumber() const; + bool isString() const; + bool isArray() const; + bool isObject() const; + + number getNumber() const; + const string &getString() const; + const array &getArray() const; + const object &getObject() const; + +private: + std::variant<std::monostate, number, string, array, object> value; +}; + +using object = json::object; + +std::unique_ptr<json> load_config(); +``` + +```cpp +// include/json.cc + +#include "example/include/json.h" +#include <initializer_list> +#include <utility> + +const json json::null{}; +bool json::isNull() const { return std::holds_alternative<std::monostate>(value); } +bool json::isNumber() const { return std::holds_alternative<number>(value); } +bool json::isString() const { return std::holds_alternative<string>(value); } +bool json::isArray() const { return std::holds_alternative<array>(value); } +bool json::isObject() const { return std::holds_alternative<object>(value); } +json::number json::getNumber() const { return std::get<number>(value); } +const json::string &json::getString() const { return std::get<string>(value); } +const json::array &json::getArray() const { return std::get<array>(value); } +const json::object &json::getObject() const { return std::get<object>(value); } + +std::unique_ptr<json> load_config() { + return std::make_unique<json>( + std::in_place_type<json::object>, + std::initializer_list<std::pair<const std::string, json>>{ + {"name", "cxx-example"}, + {"edition", 2018.}, + {"repository", json::null}}); +} +``` diff --git a/vendor/cxx/book/src/binding/cxxvector.md b/vendor/cxx/book/src/binding/cxxvector.md new file mode 100644 index 000000000..fd95a2dbd --- /dev/null +++ b/vendor/cxx/book/src/binding/cxxvector.md @@ -0,0 +1,62 @@ +{{#title std::vector<T> — Rust ♡ C++}} +# std::vector\<T\> + +The Rust binding of std::vector\<T\> is called **[`CxxVector<T>`]**. See the +link for documentation of the Rust API. + +[`CxxVector<T>`]: https://docs.rs/cxx/*/cxx/struct.CxxVector.html + +### Restrictions: + +Rust code can never obtain a CxxVector by value. Instead in Rust code we will +only ever look at a vector behind a reference or smart pointer, as in +&CxxVector\<T\> or UniquePtr\<CxxVector\<T\>\>. + +CxxVector\<T\> does not support T being an opaque Rust type. You should use a +Vec\<T\> (C++ rust::Vec\<T\>) instead for collections of opaque Rust types on +the language boundary. + +## Example + +This program involves Rust code converting a `CxxVector<CxxString>` (i.e. +`std::vector<std::string>`) into a Rust `Vec<String>`. + +```rust,noplayground +// src/main.rs + +#![no_main] // main defined in C++ by main.cc + +use cxx::{CxxString, CxxVector}; + +#[cxx::bridge] +mod ffi { + extern "Rust" { + fn f(vec: &CxxVector<CxxString>); + } +} + +fn f(vec: &CxxVector<CxxString>) { + let vec: Vec<String> = vec + .iter() + .map(|s| s.to_string_lossy().into_owned()) + .collect(); + g(&vec); +} + +fn g(vec: &[String]) { + println!("{:?}", vec); +} +``` + +```cpp +// src/main.cc + +#include "example/src/main.rs.h" +#include <string> +#include <vector> + +int main() { + std::vector<std::string> vec{"fearless", "concurrency"}; + f(vec); +} +``` diff --git a/vendor/cxx/book/src/binding/fn.md b/vendor/cxx/book/src/binding/fn.md new file mode 100644 index 000000000..2934b0695 --- /dev/null +++ b/vendor/cxx/book/src/binding/fn.md @@ -0,0 +1,34 @@ +{{#title Function pointers — Rust ♡ C++}} +# Function pointers + +### Public API: + +```cpp,hidelines +// rust/cxx.h +# +# namespace rust { + +template <typename Signature> +class Fn; + +template <typename Ret, typename... Args> +class Fn<Ret(Args...)> final { +public: + Ret operator()(Args... args) const noexcept; + Fn operator*() const noexcept; +}; +# +# } // namespace rust +``` + +### Restrictions: + +Function pointers with a Result return type are not implemented yet. + +Passing a function pointer from C++ to Rust is not implemented yet, only from +Rust to an `extern "C++"` function is implemented. + +## Example + +Function pointers are commonly useful for implementing [async functions over +FFI](../async.md). See the example code on that page. diff --git a/vendor/cxx/book/src/binding/rawptr.md b/vendor/cxx/book/src/binding/rawptr.md new file mode 100644 index 000000000..179421127 --- /dev/null +++ b/vendor/cxx/book/src/binding/rawptr.md @@ -0,0 +1,100 @@ +{{#title *mut T, *const T — Rust ♡ C++}} +# *mut T, *const T + +Generally you should use references (`&mut T`, `&T`) or [std::unique_ptr\<T\>] +where possible over raw pointers, but raw pointers are available too as an +unsafe fallback option. + +[std::unique_ptr\<T\>]: uniqueptr.md + +### Restrictions: + +Extern functions and function pointers taking a raw pointer as an argument must +be declared `unsafe fn` i.e. unsafe to call. The same does not apply to +functions which only *return* a raw pointer, though presumably doing anything +useful with the returned pointer is going to involve unsafe code elsewhere +anyway. + +## Example + +This example illustrates making a Rust call to a canonical C-style `main` +signature involving `char *argv[]`. + +```cpp +// include/args.h + +#pragma once + +void parseArgs(int argc, char *argv[]); +``` + +```cpp +// src/args.cc + +#include "example/include/args.h" +#include <iostream> + +void parseArgs(int argc, char *argv[]) { + std::cout << argc << std::endl; + for (int i = 0; i < argc; i++) { + std::cout << '"' << argv[i] << '"' << std::endl; + } +} +``` + +```rust,noplayground +// src/main.rs + +use std::env; +use std::ffi::CString; +use std::os::raw::c_char; +use std::os::unix::ffi::OsStrExt; +use std::ptr; + +#[cxx::bridge] +mod ffi { + extern "C++" { + include!("example/include/args.h"); + + unsafe fn parseArgs(argc: i32, argv: *mut *mut c_char); + } +} + +fn main() { + // Convert from OsString to nul-terminated CString, truncating each argument + // at the first inner nul byte if present. + let args: Vec<CString> = env::args_os() + .map(|os_str| { + let bytes = os_str.as_bytes(); + CString::new(bytes).unwrap_or_else(|nul_error| { + let nul_position = nul_error.nul_position(); + let mut bytes = nul_error.into_vec(); + bytes.truncate(nul_position); + CString::new(bytes).unwrap() + }) + }) + .collect(); + + // Convert from Vec<CString> of owned strings to Vec<*mut c_char> of + // borrowed string pointers. + // + // Once extern type stabilizes (https://github.com/rust-lang/rust/issues/43467) + // and https://internals.rust-lang.org/t/pre-rfc-make-cstr-a-thin-pointer/6258 + // is implemented, and CStr pointers become thin, we can sidestep this step + // by accumulating the args as Vec<Box<CStr>> up front, then simply casting + // from *mut [Box<CStr>] to *mut [*mut CStr] to *mut *mut c_char. + let argc = args.len(); + let mut argv: Vec<*mut c_char> = Vec::with_capacity(argc + 1); + for arg in &args { + argv.push(arg.as_ptr() as *mut c_char); + } + argv.push(ptr::null_mut()); // Nul terminator. + + unsafe { + ffi::parseArgs(argc as i32, argv.as_mut_ptr()); + } + + // The CStrings go out of scope here. C function must not have held on to + // the pointers beyond this point. +} +``` diff --git a/vendor/cxx/book/src/binding/result.md b/vendor/cxx/book/src/binding/result.md new file mode 100644 index 000000000..e49dcf4de --- /dev/null +++ b/vendor/cxx/book/src/binding/result.md @@ -0,0 +1,148 @@ +{{#title Result<T> — Rust ♡ C++}} +# Result\<T\> + +Result\<T\> is allowed as the return type of an extern function in either +direction. Its behavior is to translate to/from C++ exceptions. If your codebase +does not use C++ exceptions, or prefers to represent fallibility using something +like outcome\<T\>, leaf::result\<T\>, StatusOr\<T\>, etc then you'll need to +handle the translation of those to Rust Result\<T\> using your own shims for +now. Better support for this is planned. + +If an exception is thrown from an `extern "C++"` function that is *not* declared +by the CXX bridge to return Result, the program calls C++'s `std::terminate`. +The behavior is equivalent to the same exception being thrown through a +`noexcept` C++ function. + +If a panic occurs in *any* `extern "Rust"` function, regardless of whether it is +declared by the CXX bridge to return Result, a message is logged and the program +calls Rust's `std::process::abort`. + +## Returning Result from Rust to C++ + +An `extern "Rust"` function returning a Result turns into a `throw` in C++ if +the Rust side produces an error. + +Note that the return type written inside of cxx::bridge must be written without +a second type parameter. Only the Ok type is specified for the purpose of the +FFI. The Rust *implementation* (outside of the bridge module) may pick any error +type as long as it has a std::fmt::Display impl. + +```rust,noplayground +# use std::io; +# +#[cxx::bridge] +mod ffi { + extern "Rust" { + fn fallible1(depth: usize) -> Result<String>; + fn fallible2() -> Result<()>; + } +} + +fn fallible1(depth: usize) -> anyhow::Result<String> { + if depth == 0 { + return Err(anyhow::Error::msg("fallible1 requires depth > 0")); + } + ... +} + +fn fallible2() -> Result<(), io::Error> { + ... + Ok(()) +} +``` + +The exception that gets thrown by CXX on the C++ side is always of type +`rust::Error` and has the following C++ public API. The `what()` member function +gives the error message according to the Rust error's std::fmt::Display impl. + +```cpp,hidelines +// rust/cxx.h +# +# namespace rust { + +class Error final : public std::exception { +public: + Error(const Error &); + Error(Error &&) noexcept; + ~Error() noexcept; + + Error &operator=(const Error &); + Error &operator=(Error &&) noexcept; + + const char *what() const noexcept override; +}; +# +# } // namespace rust +``` + +## Returning Result from C++ to Rust + +An `extern "C++"` function returning a Result turns into a `catch` in C++ that +converts the exception into an Err for Rust. + +Note that the return type written inside of cxx::bridge must be written without +a second type parameter. Only the Ok type is specified for the purpose of the +FFI. The resulting error type created by CXX when an `extern "C++"` function +throws will always be of type **[`cxx::Exception`]**. + +[`cxx::Exception`]: https://docs.rs/cxx/*/cxx/struct.Exception.html + +```rust,noplayground +# use std::process; +# +#[cxx::bridge] +mod ffi { + unsafe extern "C++" { + include!("example/include/example.h"); + fn fallible1(depth: usize) -> Result<String>; + fn fallible2() -> Result<()>; + } +} + +fn main() { + if let Err(err) = ffi::fallible1(99) { + eprintln!("Error: {}", err); + process::exit(1); + } +} +``` + +The specific set of caught exceptions and the conversion to error message are +both customizable. The way you do this is by defining a template function +`rust::behavior::trycatch` with a suitable signature inside any one of the +headers `include!`'d by your cxx::bridge. + +The template signature is required to be: + +```cpp,hidelines +namespace rust { +namespace behavior { + +template <typename Try, typename Fail> +static void trycatch(Try &&func, Fail &&fail) noexcept; + +} // namespace behavior +} // namespace rust +``` + +The default `trycatch` used by CXX if you have not provided your own is the +following. You must follow the same pattern: invoke `func` with no arguments, +catch whatever exception(s) you want, and invoke `fail` with the error message +you'd like for the Rust error to have. + +```cpp,hidelines +# #include <exception> +# +# namespace rust { +# namespace behavior { +# +template <typename Try, typename Fail> +static void trycatch(Try &&func, Fail &&fail) noexcept try { + func(); +} catch (const std::exception &e) { + fail(e.what()); +} +# +# } // namespace behavior +# } // namespace rust +``` diff --git a/vendor/cxx/book/src/binding/sharedptr.md b/vendor/cxx/book/src/binding/sharedptr.md new file mode 100644 index 000000000..a3b707008 --- /dev/null +++ b/vendor/cxx/book/src/binding/sharedptr.md @@ -0,0 +1,80 @@ +{{#title std::shared_ptr<T> — Rust ♡ C++}} +# std::shared\_ptr\<T\> + +The Rust binding of std::shared\_ptr\<T\> is called **[`SharedPtr<T>`]**. See +the link for documentation of the Rust API. + +[`SharedPtr<T>`]: https://docs.rs/cxx/*/cxx/struct.SharedPtr.html + +### Restrictions: + +SharedPtr\<T\> does not support T being an opaque Rust type. You should use a +Box\<T\> (C++ [rust::Box\<T\>](box.md)) instead for transferring ownership of +opaque Rust types on the language boundary. + +## Example + +```rust,noplayground +// src/main.rs + +use std::ops::Deref; +use std::ptr; + +#[cxx::bridge] +mod ffi { + unsafe extern "C++" { + include!("example/include/example.h"); + + type Object; + + fn create_shared_ptr() -> SharedPtr<Object>; + } +} + +fn main() { + let ptr1 = ffi::create_shared_ptr(); + + { + // Create a second shared_ptr holding shared ownership of the same + // object. There is still only one Object but two SharedPtr<Object>. + // Both pointers point to the same object on the heap. + let ptr2 = ptr1.clone(); + assert!(ptr::eq(ptr1.deref(), ptr2.deref())); + + // ptr2 goes out of scope, but Object is not destroyed yet. + } + + println!("say goodbye to Object"); + + // ptr1 goes out of scope and Object is destroyed. +} +``` + +```cpp +// include/example.h + +#pragma once +#include <memory> + +class Object { +public: + Object(); + ~Object(); +}; + +std::shared_ptr<Object> create_shared_ptr(); +``` + +```cpp +// src/example.cc + +#include "example/include/example.h" +#include <iostream> + +Object::Object() { std::cout << "construct Object" << std::endl; } +Object::~Object() { std::cout << "~Object" << std::endl; } + +std::shared_ptr<Object> create_shared_ptr() { + return std::make_shared<Object>(); +} +``` diff --git a/vendor/cxx/book/src/binding/slice.md b/vendor/cxx/book/src/binding/slice.md new file mode 100644 index 000000000..803277ba9 --- /dev/null +++ b/vendor/cxx/book/src/binding/slice.md @@ -0,0 +1,171 @@ +{{#title rust::Slice<T> — Rust ♡ C++}} +# rust::Slice\<const T\>, rust::Slice\<T\> + +- Rust `&[T]` is written `rust::Slice<const T>` in C++ +- Rust `&mut [T]` is written `rust::Slice<T>` in C++ + +### Public API: + +```cpp,hidelines +// rust/cxx.h +# +# #include <iterator> +# #include <type_traits> +# +# namespace rust { + +template <typename T> +class Slice final { +public: + using value_type = T; + + Slice() noexcept; + Slice(const Slice<T> &) noexcept; + Slice(T *, size_t count) noexcept; + + Slice &operator=(Slice<T> &&) noexcept; + Slice &operator=(const Slice<T> &) noexcept + requires std::is_const_v<T>; + + T *data() const noexcept; + size_t size() const noexcept; + size_t length() const noexcept; + bool empty() const noexcept; + + T &operator[](size_t n) const noexcept; + T &at(size_t n) const; + T &front() const noexcept; + T &back() const noexcept; + + class iterator; + iterator begin() const noexcept; + iterator end() const noexcept; + + void swap(Slice &) noexcept; +}; +# +# template <typename T> +# class Slice<T>::iterator final { +# public: +# using iterator_category = std::random_access_iterator_tag; +# using value_type = T; +# using pointer = T *; +# using reference = T &; +# +# T &operator*() const noexcept; +# T *operator->() const noexcept; +# T &operator[](ptrdiff_t) const noexcept; +# +# iterator &operator++() noexcept; +# iterator operator++(int) noexcept; +# iterator &operator--() noexcept; +# iterator operator--(int) noexcept; +# +# iterator &operator+=(ptrdiff_t) noexcept; +# iterator &operator-=(ptrdiff_t) noexcept; +# iterator operator+(ptrdiff_t) const noexcept; +# iterator operator-(ptrdiff_t) const noexcept; +# ptrdiff_t operator-(const iterator &) const noexcept; +# +# bool operator==(const iterator &) const noexcept; +# bool operator!=(const iterator &) const noexcept; +# bool operator<(const iterator &) const noexcept; +# bool operator>(const iterator &) const noexcept; +# bool operator<=(const iterator &) const noexcept; +# bool operator>=(const iterator &) const noexcept; +# }; +# +# } // namespace rust +``` + +### Restrictions: + +T must not be an opaque Rust type or opaque C++ type. Support for opaque Rust +types in slices is coming. + +Allowed as function argument or return value. Not supported in shared structs. + +Only rust::Slice\<const T\> is copy-assignable, not rust::Slice\<T\>. (Both are +move-assignable.) You'll need to write std::move occasionally as a reminder that +accidentally exposing overlapping &mut \[T\] to Rust is UB. + +## Example + +This example is a C++ program that constructs a slice containing JSON data (by +reading from stdin, but it could be from anywhere), then calls into Rust to +pretty-print that JSON data into a std::string via the [serde_json] and +[serde_transcode] crates. + +[serde_json]: https://github.com/serde-rs/json +[serde_transcode]: https://github.com/sfackler/serde-transcode + +```rust,noplayground +// src/main.rs + +#![no_main] // main defined in C++ by main.cc + +use cxx::CxxString; +use std::io::{self, Write}; +use std::pin::Pin; + +#[cxx::bridge] +mod ffi { + extern "Rust" { + fn prettify_json(input: &[u8], output: Pin<&mut CxxString>) -> Result<()>; + } +} + +struct WriteToCxxString<'a>(Pin<&'a mut CxxString>); + +impl<'a> Write for WriteToCxxString<'a> { + fn write(&mut self, buf: &[u8]) -> io::Result<usize> { + self.0.as_mut().push_bytes(buf); + Ok(buf.len()) + } + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} + +fn prettify_json(input: &[u8], output: Pin<&mut CxxString>) -> serde_json::Result<()> { + let writer = WriteToCxxString(output); + let mut deserializer = serde_json::Deserializer::from_slice(input); + let mut serializer = serde_json::Serializer::pretty(writer); + serde_transcode::transcode(&mut deserializer, &mut serializer) +} +``` + +```cpp +// src/main.cc + +#include "example/src/main.rs.h" +#include <iostream> +#include <iterator> +#include <string> +#include <vector> + +int main() { + // Read json from stdin. + std::istreambuf_iterator<char> begin{std::cin}, end; + std::vector<unsigned char> input{begin, end}; + rust::Slice<const uint8_t> slice{input.data(), input.size()}; + + // Prettify using serde_json and serde_transcode. + std::string output; + prettify_json(slice, output); + + // Write to stdout. + std::cout << output << std::endl; +} +``` + +Testing the example: + +```console +$ echo '{"fearless":"concurrency"}' | cargo run + Finished dev [unoptimized + debuginfo] target(s) in 0.02s + Running `target/debug/example` +{ + "fearless": "concurrency" +} +``` diff --git a/vendor/cxx/book/src/binding/str.md b/vendor/cxx/book/src/binding/str.md new file mode 100644 index 000000000..9c1e0a773 --- /dev/null +++ b/vendor/cxx/book/src/binding/str.md @@ -0,0 +1,118 @@ +{{#title rust::Str — Rust ♡ C++}} +# rust::Str + +### Public API: + +```cpp,hidelines +// rust/cxx.h +# +# #include <iosfwd> +# #include <string> +# +# namespace rust { + +class Str final { +public: + Str() noexcept; + Str(const Str &) noexcept; + Str(const String &) noexcept; + + // Throws std::invalid_argument if not utf-8. + Str(const std::string &); + Str(const char *); + Str(const char *, size_t); + + Str &operator=(const Str &) noexcept; + + explicit operator std::string() const; + + // Note: no null terminator. + const char *data() const noexcept; + size_t size() const noexcept; + size_t length() const noexcept; + bool empty() const noexcept; + + using iterator = const char *; + using const_iterator = const char *; + const_iterator begin() const noexcept; + const_iterator end() const noexcept; + const_iterator cbegin() const noexcept; + const_iterator cend() const noexcept; + + bool operator==(const Str &) const noexcept; + bool operator!=(const Str &) const noexcept; + bool operator<(const Str &) const noexcept; + bool operator<=(const Str &) const noexcept; + bool operator>(const Str &) const noexcept; + bool operator>=(const Str &) const noexcept; + + void swap(Str &) noexcept; +}; + +std::ostream &operator<<(std::ostream &, const Str &); +# +# } // namespace rust +``` + +### Notes: + +**Be aware that rust::Str behaves like &str i.e. it is a borrow!** C++ +needs to be mindful of the lifetimes at play. + +Just to reiterate: &str is rust::Str. Do not try to write &str as `const +rust::Str &`. A language-level C++ reference is not able to capture the fat +pointer nature of &str. + +### Restrictions: + +Allowed as function argument or return value. Not supported in shared structs +yet. `&mut str` is not supported yet, but is also extremely obscure so this is +fine. + +## Example + +```rust,noplayground +// src/main.rs + +#[cxx::bridge] +mod ffi { + extern "Rust" { + fn r(greeting: &str); + } + + unsafe extern "C++" { + include!("example/include/greeting.h"); + fn c(greeting: &str); + } +} + +fn r(greeting: &str) { + println!("{}", greeting); +} + +fn main() { + ffi::c("hello from Rust"); +} +``` + +```cpp +// include/greeting.h + +#pragma once +#include "example/src/main.rs.h" +#include "rust/cxx.h" + +void c(rust::Str greeting); +``` + +```cpp +// src/greeting.cc + +#include "example/include/greeting.h" +#include <iostream> + +void c(rust::Str greeting) { + std::cout << greeting << std::endl; + r("hello from C++"); +} +``` diff --git a/vendor/cxx/book/src/binding/string.md b/vendor/cxx/book/src/binding/string.md new file mode 100644 index 000000000..1e4827812 --- /dev/null +++ b/vendor/cxx/book/src/binding/string.md @@ -0,0 +1,132 @@ +{{#title rust::String — Rust ♡ C++}} +# rust::String + +### Public API: + +```cpp,hidelines +// rust/cxx.h +# +# #include <iosfwd> +# #include <string> +# +# namespace rust { + +class String final { +public: + String() noexcept; + String(const String &) noexcept; + String(String &&) noexcept; + ~String() noexcept; + + // Throws std::invalid_argument if not UTF-8. + String(const std::string &); + String(const char *); + String(const char *, size_t); + + // Replaces invalid UTF-8 data with the replacement character (U+FFFD). + static String lossy(const std::string &) noexcept; + static String lossy(const char *) noexcept; + static String lossy(const char *, size_t) noexcept; + + // Throws std::invalid_argument if not UTF-16. + String(const char16_t *); + String(const char16_t *, size_t); + + // Replaces invalid UTF-16 data with the replacement character (U+FFFD). + static String lossy(const char16_t *) noexcept; + static String lossy(const char16_t *, size_t) noexcept; + + String &operator=(const String &) noexcept; + String &operator=(String &&) noexcept; + + explicit operator std::string() const; + + // Note: no null terminator. + const char *data() const noexcept; + size_t size() const noexcept; + size_t length() const noexcept; + bool empty() const noexcept; + + const char *c_str() noexcept; + + size_t capacity() const noexcept; + void reserve(size_t new_cap) noexcept; + + using iterator = char *; + iterator begin() noexcept; + iterator end() noexcept; + + using const_iterator = const char *; + const_iterator begin() const noexcept; + const_iterator end() const noexcept; + const_iterator cbegin() const noexcept; + const_iterator cend() const noexcept; + + bool operator==(const String &) const noexcept; + bool operator!=(const String &) const noexcept; + bool operator<(const String &) const noexcept; + bool operator<=(const String &) const noexcept; + bool operator>(const String &) const noexcept; + bool operator>=(const String &) const noexcept; + + void swap(String &) noexcept; +}; + +std::ostream &operator<<(std::ostream &, const String &); +# +# } // namespace rust +``` + +### Restrictions: + +None. Strings may be used as function arguments and function return values, by +value or by reference, as well as fields of shared structs. + +## Example + +```rust,noplayground +// src/main.rs + +#[cxx::bridge] +mod ffi { + struct ConcatRequest { + fst: String, + snd: String, + } + + unsafe extern "C++" { + include!("example/include/concat.h"); + fn concat(r: ConcatRequest) -> String; + } +} + +fn main() { + let concatenated = ffi::concat(ffi::ConcatRequest { + fst: "fearless".to_owned(), + snd: "concurrency".to_owned(), + }); + println!("concatenated: {:?}", concatenated); +} +``` + +```cpp +// include/concat.h + +#pragma once +#include "example/src/main.rs.h" +#include "rust/cxx.h" + +rust::String concat(ConcatRequest r); +``` + +```cpp +// src/concat.cc + +#include "example/include/concat.h" + +rust::String concat(ConcatRequest r) { + // The full suite of operator overloads hasn't been added + // yet on rust::String, but we can get it done like this: + return std::string(r.fst) + std::string(r.snd); +} +``` diff --git a/vendor/cxx/book/src/binding/uniqueptr.md b/vendor/cxx/book/src/binding/uniqueptr.md new file mode 100644 index 000000000..eefbc34a3 --- /dev/null +++ b/vendor/cxx/book/src/binding/uniqueptr.md @@ -0,0 +1,63 @@ +{{#title std::unique_ptr<T> — Rust ♡ C++}} +# std::unique\_ptr\<T\> + +The Rust binding of std::unique\_ptr\<T\> is called **[`UniquePtr<T>`]**. See +the link for documentation of the Rust API. + +[`UniquePtr<T>`]: https://docs.rs/cxx/*/cxx/struct.UniquePtr.html + +### Restrictions: + +Only `std::unique_ptr<T, std::default_delete<T>>` is currently supported. Custom +deleters may be supported in the future. + +UniquePtr\<T\> does not support T being an opaque Rust type. You should use a +Box\<T\> (C++ [rust::Box\<T\>](box.md)) instead for transferring ownership of +opaque Rust types on the language boundary. + +## Example + +UniquePtr is commonly useful for returning opaque C++ objects to Rust. This use +case was featured in the [*blobstore tutorial*](../tutorial.md). + +```rust,noplayground +// src/main.rs + +#[cxx::bridge] +mod ffi { + unsafe extern "C++" { + include!("example/include/blobstore.h"); + + type BlobstoreClient; + + fn new_blobstore_client() -> UniquePtr<BlobstoreClient>; + // ... + } +} + +fn main() { + let client = ffi::new_blobstore_client(); + // ... +} +``` + +```cpp +// include/blobstore.h + +#pragma once +#include <memory> + +class BlobstoreClient; + +std::unique_ptr<BlobstoreClient> new_blobstore_client(); +``` + +```cpp +// src/blobstore.cc + +#include "example/include/blobstore.h" + +std::unique_ptr<BlobstoreClient> new_blobstore_client() { + return std::make_unique<BlobstoreClient>(); +} +``` diff --git a/vendor/cxx/book/src/binding/vec.md b/vendor/cxx/book/src/binding/vec.md new file mode 100644 index 000000000..4d6587ab1 --- /dev/null +++ b/vendor/cxx/book/src/binding/vec.md @@ -0,0 +1,192 @@ +{{#title rust::Vec<T> — Rust ♡ C++}} +# rust::Vec\<T\> + +### Public API: + +```cpp,hidelines +// rust/cxx.h +# +# #include <initializer_list> +# #include <iterator> +# #include <type_traits> +# +# namespace rust { + +template <typename T> +class Vec final { +public: + using value_type = T; + + Vec() noexcept; + Vec(std::initializer_list<T>); + Vec(const Vec &); + Vec(Vec &&) noexcept; + ~Vec() noexcept; + + Vec &operator=(Vec &&) noexcept; + Vec &operator=(const Vec &); + + size_t size() const noexcept; + bool empty() const noexcept; + const T *data() const noexcept; + T *data() noexcept; + size_t capacity() const noexcept; + + const T &operator[](size_t n) const noexcept; + const T &at(size_t n) const; + const T &front() const; + const T &back() const; + + T &operator[](size_t n) noexcept; + T &at(size_t n); + T &front(); + T &back(); + + void reserve(size_t new_cap); + void push_back(const T &value); + void push_back(T &&value); + template <typename... Args> + void emplace_back(Args &&...args); + void truncate(size_t len); + void clear(); + + class iterator; + iterator begin() noexcept; + iterator end() noexcept; + + class const_iterator; + const_iterator begin() const noexcept; + const_iterator end() const noexcept; + const_iterator cbegin() const noexcept; + const_iterator cend() const noexcept; + + void swap(Vec &) noexcept; +}; +# +# template <typename T> +# class Vec<T>::iterator final { +# public: +# using iterator_category = std::random_access_iterator_tag; +# using value_type = T; +# using pointer = T *; +# using reference = T &; +# +# T &operator*() const noexcept; +# T *operator->() const noexcept; +# T &operator[](ptrdiff_t) const noexcept; +# +# iterator &operator++() noexcept; +# iterator operator++(int) noexcept; +# iterator &operator--() noexcept; +# iterator operator--(int) noexcept; +# +# iterator &operator+=(ptrdiff_t) noexcept; +# iterator &operator-=(ptrdiff_t) noexcept; +# iterator operator+(ptrdiff_t) const noexcept; +# iterator operator-(ptrdiff_t) const noexcept; +# ptrdiff_t operator-(const iterator &) const noexcept; +# +# bool operator==(const iterator &) const noexcept; +# bool operator!=(const iterator &) const noexcept; +# bool operator<(const iterator &) const noexcept; +# bool operator<=(const iterator &) const noexcept; +# bool operator>(const iterator &) const noexcept; +# bool operator>=(const iterator &) const noexcept; +# }; +# +# template <typename T> +# class Vec<T>::const_iterator final { +# public: +# using iterator_category = std::random_access_iterator_tag; +# using value_type = const T; +# using pointer = const T *; +# using reference = const T &; +# +# const T &operator*() const noexcept; +# const T *operator->() const noexcept; +# const T &operator[](ptrdiff_t) const noexcept; +# +# const_iterator &operator++() noexcept; +# const_iterator operator++(int) noexcept; +# const_iterator &operator--() noexcept; +# const_iterator operator--(int) noexcept; +# +# const_iterator &operator+=(ptrdiff_t) noexcept; +# const_iterator &operator-=(ptrdiff_t) noexcept; +# const_iterator operator+(ptrdiff_t) const noexcept; +# const_iterator operator-(ptrdiff_t) const noexcept; +# ptrdiff_t operator-(const const_iterator &) const noexcept; +# +# bool operator==(const const_iterator &) const noexcept; +# bool operator!=(const const_iterator &) const noexcept; +# bool operator<(const const_iterator &) const noexcept; +# bool operator<=(const const_iterator &) const noexcept; +# bool operator>(const const_iterator &) const noexcept; +# bool operator>=(const const_iterator &) const noexcept; +# }; +# +# } // namespace rust +``` + +### Restrictions: + +Vec\<T\> does not support T being an opaque C++ type. You should use +CxxVector\<T\> (C++ std::vector\<T\>) instead for collections of opaque C++ +types on the language boundary. + +## Example + +```rust,noplayground +// src/main.rs + +#[cxx::bridge] +mod ffi { + struct Shared { + v: u32, + } + + unsafe extern "C++" { + include!("example/include/example.h"); + + fn f(elements: Vec<Shared>); + } +} + +fn main() { + let shared = |v| ffi::Shared { v }; + let elements = vec![shared(3), shared(2), shared(1)]; + ffi::f(elements); +} +``` + +```cpp +// include/example.h + +#pragma once +#include "example/src/main.rs.h" +#include "rust/cxx.h" + +void f(rust::Vec<Shared> elements); +``` + +```cpp +// src/example.cc + +#include "example/include/example.h" +#include <algorithm> +#include <cassert> +#include <iostream> +#include <iterator> +#include <vector> + +void f(rust::Vec<Shared> v) { + for (auto shared : v) { + std::cout << shared.v << std::endl; + } + + // Copy the elements to a C++ std::vector using STL algorithm. + std::vector<Shared> stdv; + std::copy(v.begin(), v.end(), std::back_inserter(stdv)); + assert(v.size() == stdv.size()); +} +``` |