mirror of
https://gitee.com/openharmony/third_party_rust_cxx
synced 2024-11-27 01:11:38 +00:00
Switch intro example to blobstore client
This commit is contained in:
parent
7614e77068
commit
4ca366fa57
60
README.md
60
README.md
@ -63,6 +63,12 @@ function calls Rust's `len()`.
|
||||
|
||||
## Example
|
||||
|
||||
In this example we are writing a Rust application that wishes to take advantage
|
||||
of an existing C++ client for a large-file blobstore service. The blobstore
|
||||
supports a `put` operation for a discontiguous buffer upload. For example we
|
||||
might be uploading snapshots of a circular buffer which would tend to consist of
|
||||
2 chunks, or fragments of a file spread across memory for some other reason.
|
||||
|
||||
A runnable version of this example is provided under the *demo* directory of
|
||||
this repo. To try it out, run `cargo run` from that directory.
|
||||
|
||||
@ -70,49 +76,49 @@ this repo. To try it out, run `cargo run` from that directory.
|
||||
#[cxx::bridge]
|
||||
mod ffi {
|
||||
// Any shared structs, whose fields will be visible to both languages.
|
||||
struct SharedThing {
|
||||
z: i32,
|
||||
y: Box<ThingR>,
|
||||
x: UniquePtr<ThingC>,
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
// One or more headers with the matching C++ declarations. Our code
|
||||
// generators don't read it but it gets #include'd and used in static
|
||||
// assertions to ensure our picture of the FFI boundary is accurate.
|
||||
include!("demo/include/demo.h");
|
||||
|
||||
// Zero or more opaque types which both languages can pass around but
|
||||
// only C++ can see the fields.
|
||||
type ThingC;
|
||||
|
||||
// Functions implemented in C++.
|
||||
fn make_demo(appname: &str) -> UniquePtr<ThingC>;
|
||||
fn get_name(thing: &ThingC) -> &CxxString;
|
||||
fn do_thing(state: SharedThing);
|
||||
struct BlobMetadata {
|
||||
size: usize,
|
||||
tags: Vec<String>,
|
||||
}
|
||||
|
||||
extern "Rust" {
|
||||
// Zero or more opaque types which both languages can pass around but
|
||||
// only Rust can see the fields.
|
||||
type ThingR;
|
||||
type MultiBuf;
|
||||
|
||||
// Functions implemented in Rust.
|
||||
fn print_r(r: &ThingR);
|
||||
fn next_chunk(buf: &mut MultiBuf) -> &[u8];
|
||||
}
|
||||
|
||||
extern "C++" {
|
||||
// One or more headers with the matching C++ declarations. Our code
|
||||
// generators don't read it but it gets #include'd and used in static
|
||||
// assertions to ensure our picture of the FFI boundary is accurate.
|
||||
include!("demo/include/blobstore.h");
|
||||
|
||||
// Zero or more opaque types which both languages can pass around but
|
||||
// only C++ can see the fields.
|
||||
type BlobstoreClient;
|
||||
|
||||
// Functions implemented in C++.
|
||||
fn new_blobstore_client() -> UniquePtr<BlobstoreClient>;
|
||||
fn put(&self, parts: &mut MultiBuf) -> u64;
|
||||
fn tag(&self, blobid: u64, tag: &str);
|
||||
fn metadata(&self, blobid: u64) -> BlobMetadata;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Now we simply provide C++ definitions of all the things in the `extern "C"`
|
||||
block and Rust definitions of all the things in the `extern "Rust"` block, and
|
||||
get to call back and forth safely.
|
||||
Now we simply provide Rust definitions of all the things in the `extern "Rust"`
|
||||
block and C++ definitions of all the things in the `extern "C++"` block, and get
|
||||
to call back and forth safely.
|
||||
|
||||
Here are links to the complete set of source files involved in the demo:
|
||||
|
||||
- [demo/src/main.rs](demo/src/main.rs)
|
||||
- [demo/build.rs](demo/build.rs)
|
||||
- [demo/include/demo.h](demo/include/demo.h)
|
||||
- [demo/src/demo.cc](demo/src/demo.cc)
|
||||
- [demo/include/blobstore.h](demo/include/blobstore.h)
|
||||
- [demo/src/blobstore.cc](demo/src/blobstore.cc)
|
||||
|
||||
To look at the code generated in both languages for the example by the CXX code
|
||||
generators:
|
||||
|
14
demo/BUCK
14
demo/BUCK
@ -4,8 +4,8 @@ rust_binary(
|
||||
name = "demo",
|
||||
srcs = glob(["src/**/*.rs"]),
|
||||
deps = [
|
||||
":blobstore-sys",
|
||||
":bridge",
|
||||
":demo-sys",
|
||||
"//:cxx",
|
||||
],
|
||||
)
|
||||
@ -13,21 +13,21 @@ rust_binary(
|
||||
rust_cxx_bridge(
|
||||
name = "bridge",
|
||||
src = "src/main.rs",
|
||||
deps = [":demo-include"],
|
||||
deps = [":blobstore-include"],
|
||||
)
|
||||
|
||||
cxx_library(
|
||||
name = "demo-sys",
|
||||
srcs = ["src/demo.cc"],
|
||||
name = "blobstore-sys",
|
||||
srcs = ["src/blobstore.cc"],
|
||||
compiler_flags = ["-std=c++14"],
|
||||
deps = [
|
||||
":blobstore-include",
|
||||
":bridge/include",
|
||||
":demo-include",
|
||||
],
|
||||
)
|
||||
|
||||
cxx_library(
|
||||
name = "demo-include",
|
||||
exported_headers = ["include/demo.h"],
|
||||
name = "blobstore-include",
|
||||
exported_headers = ["include/blobstore.h"],
|
||||
deps = ["//:core"],
|
||||
)
|
||||
|
14
demo/BUILD
14
demo/BUILD
@ -6,8 +6,8 @@ rust_binary(
|
||||
name = "demo",
|
||||
srcs = glob(["src/**/*.rs"]),
|
||||
deps = [
|
||||
":blobstore-sys",
|
||||
":bridge",
|
||||
":demo-sys",
|
||||
"//:cxx",
|
||||
],
|
||||
)
|
||||
@ -15,21 +15,21 @@ rust_binary(
|
||||
rust_cxx_bridge(
|
||||
name = "bridge",
|
||||
src = "src/main.rs",
|
||||
deps = [":demo-include"],
|
||||
deps = [":blobstore-include"],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "demo-sys",
|
||||
srcs = ["src/demo.cc"],
|
||||
name = "blobstore-sys",
|
||||
srcs = ["src/blobstore.cc"],
|
||||
copts = ["-std=c++14"],
|
||||
deps = [
|
||||
":blobstore-include",
|
||||
":bridge/include",
|
||||
":demo-include",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "demo-include",
|
||||
hdrs = ["include/demo.h"],
|
||||
name = "blobstore-include",
|
||||
hdrs = ["include/blobstore.h"],
|
||||
deps = ["//:core"],
|
||||
)
|
||||
|
@ -1,10 +1,10 @@
|
||||
fn main() {
|
||||
cxx_build::bridge("src/main.rs")
|
||||
.file("src/demo.cc")
|
||||
.file("src/blobstore.cc")
|
||||
.flag_if_supported("-std=c++14")
|
||||
.compile("cxxbridge-demo");
|
||||
|
||||
println!("cargo:rerun-if-changed=src/main.rs");
|
||||
println!("cargo:rerun-if-changed=src/demo.cc");
|
||||
println!("cargo:rerun-if-changed=include/demo.h");
|
||||
println!("cargo:rerun-if-changed=src/blobstore.cc");
|
||||
println!("cargo:rerun-if-changed=include/blobstore.h");
|
||||
}
|
||||
|
26
demo/include/blobstore.h
Normal file
26
demo/include/blobstore.h
Normal file
@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
#include "rust/cxx.h"
|
||||
#include <memory>
|
||||
|
||||
namespace org {
|
||||
namespace blobstore {
|
||||
|
||||
struct MultiBuf;
|
||||
struct BlobMetadata;
|
||||
|
||||
class BlobstoreClient {
|
||||
public:
|
||||
BlobstoreClient();
|
||||
uint64_t put(MultiBuf &buf) const;
|
||||
void tag(uint64_t blobid, rust::Str tag) const;
|
||||
BlobMetadata metadata(uint64_t blobid) const;
|
||||
|
||||
private:
|
||||
class impl;
|
||||
std::shared_ptr<impl> impl;
|
||||
};
|
||||
|
||||
std::unique_ptr<BlobstoreClient> new_blobstore_client();
|
||||
|
||||
} // namespace blobstore
|
||||
} // namespace org
|
@ -1,24 +0,0 @@
|
||||
#pragma once
|
||||
#include "rust/cxx.h"
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
namespace org {
|
||||
namespace example {
|
||||
|
||||
class ThingC {
|
||||
public:
|
||||
ThingC(std::string appname);
|
||||
~ThingC();
|
||||
|
||||
std::string appname;
|
||||
};
|
||||
|
||||
struct SharedThing;
|
||||
|
||||
std::unique_ptr<ThingC> make_demo(rust::Str appname);
|
||||
const std::string &get_name(const ThingC &thing);
|
||||
void do_thing(SharedThing state);
|
||||
|
||||
} // namespace example
|
||||
} // namespace org
|
71
demo/src/blobstore.cc
Normal file
71
demo/src/blobstore.cc
Normal file
@ -0,0 +1,71 @@
|
||||
#include "demo/include/blobstore.h"
|
||||
#include "demo/src/main.rs.h"
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace org {
|
||||
namespace blobstore {
|
||||
|
||||
// Toy implementation of an in-memory blobstore.
|
||||
//
|
||||
// In reality the implementation of BlobstoreClient could be a large complex C++
|
||||
// library.
|
||||
class BlobstoreClient::impl {
|
||||
friend BlobstoreClient;
|
||||
using Blob = struct {
|
||||
std::string data;
|
||||
std::set<std::string> tags;
|
||||
};
|
||||
std::unordered_map<uint64_t, Blob> blobs;
|
||||
};
|
||||
|
||||
BlobstoreClient::BlobstoreClient() : impl(new typename BlobstoreClient::impl) {}
|
||||
|
||||
// Upload a new blob and return a blobid that serves as a handle to the blob.
|
||||
uint64_t BlobstoreClient::put(MultiBuf &buf) const {
|
||||
std::string contents;
|
||||
|
||||
// Traverse the caller's chunk iterator.
|
||||
//
|
||||
// In reality there might be sophisticated batching of chunks and/or parallel
|
||||
// upload implemented by the blobstore's C++ client.
|
||||
while (true) {
|
||||
auto chunk = next_chunk(buf);
|
||||
if (chunk.size() == 0) {
|
||||
break;
|
||||
}
|
||||
contents.append(reinterpret_cast<const char *>(chunk.data()), chunk.size());
|
||||
}
|
||||
|
||||
// Insert into map and provide caller the handle.
|
||||
auto blobid = std::hash<std::string>{}(contents);
|
||||
impl->blobs[blobid] = {std::move(contents), {}};
|
||||
return blobid;
|
||||
}
|
||||
|
||||
// Add tag to an existing blob.
|
||||
void BlobstoreClient::tag(uint64_t blobid, rust::Str tag) const {
|
||||
impl->blobs[blobid].tags.emplace(tag);
|
||||
}
|
||||
|
||||
// Retrieve metadata about a blob.
|
||||
BlobMetadata BlobstoreClient::metadata(uint64_t blobid) const {
|
||||
BlobMetadata metadata{};
|
||||
auto blob = impl->blobs.find(blobid);
|
||||
if (blob != impl->blobs.end()) {
|
||||
metadata.size = blob->second.data.size();
|
||||
std::for_each(blob->second.tags.begin(), blob->second.tags.end(),
|
||||
[&](auto &t) { metadata.tags.emplace_back(t); });
|
||||
}
|
||||
return metadata;
|
||||
}
|
||||
|
||||
std::unique_ptr<BlobstoreClient> new_blobstore_client() {
|
||||
return std::make_unique<BlobstoreClient>();
|
||||
}
|
||||
|
||||
} // namespace blobstore
|
||||
} // namespace org
|
@ -1,21 +0,0 @@
|
||||
#include "demo/include/demo.h"
|
||||
#include "demo/src/main.rs.h"
|
||||
#include <iostream>
|
||||
|
||||
namespace org {
|
||||
namespace example {
|
||||
|
||||
ThingC::ThingC(std::string appname) : appname(std::move(appname)) {}
|
||||
|
||||
ThingC::~ThingC() { std::cout << "done with ThingC" << std::endl; }
|
||||
|
||||
std::unique_ptr<ThingC> make_demo(rust::Str appname) {
|
||||
return std::make_unique<ThingC>(std::string(appname));
|
||||
}
|
||||
|
||||
const std::string &get_name(const ThingC &thing) { return thing.appname; }
|
||||
|
||||
void do_thing(SharedThing state) { print_r(*state.y); }
|
||||
|
||||
} // namespace example
|
||||
} // namespace org
|
@ -1,39 +1,59 @@
|
||||
#[cxx::bridge(namespace = "org::example")]
|
||||
#[cxx::bridge(namespace = "org::blobstore")]
|
||||
mod ffi {
|
||||
struct SharedThing {
|
||||
z: i32,
|
||||
y: Box<ThingR>,
|
||||
x: UniquePtr<ThingC>,
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
include!("demo/include/demo.h");
|
||||
|
||||
type ThingC;
|
||||
fn make_demo(appname: &str) -> UniquePtr<ThingC>;
|
||||
fn get_name(thing: &ThingC) -> &CxxString;
|
||||
fn do_thing(state: SharedThing);
|
||||
// Shared structs with fields visible to both languages.
|
||||
struct BlobMetadata {
|
||||
size: usize,
|
||||
tags: Vec<String>,
|
||||
}
|
||||
|
||||
// Rust types and signatures exposed to C++.
|
||||
extern "Rust" {
|
||||
type ThingR;
|
||||
fn print_r(r: &ThingR);
|
||||
type MultiBuf;
|
||||
|
||||
fn next_chunk(buf: &mut MultiBuf) -> &[u8];
|
||||
}
|
||||
|
||||
// C++ types and signatures exposed to Rust.
|
||||
extern "C++" {
|
||||
include!("demo/include/blobstore.h");
|
||||
|
||||
type BlobstoreClient;
|
||||
|
||||
fn new_blobstore_client() -> UniquePtr<BlobstoreClient>;
|
||||
fn put(&self, parts: &mut MultiBuf) -> u64;
|
||||
fn tag(&self, blobid: u64, tag: &str);
|
||||
fn metadata(&self, blobid: u64) -> BlobMetadata;
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ThingR(usize);
|
||||
|
||||
fn print_r(r: &ThingR) {
|
||||
println!("called back with r={}", r.0);
|
||||
// An iterator over contiguous chunks of a discontiguous file object.
|
||||
//
|
||||
// Toy implementation uses a Vec<Vec<u8>> but in reality this might be iterating
|
||||
// over some more complex Rust data structure like a rope, or maybe loading
|
||||
// chunks lazily from somewhere.
|
||||
pub struct MultiBuf {
|
||||
chunks: Vec<Vec<u8>>,
|
||||
pos: usize,
|
||||
}
|
||||
pub fn next_chunk(buf: &mut MultiBuf) -> &[u8] {
|
||||
let next = buf.chunks.get(buf.pos);
|
||||
buf.pos += 1;
|
||||
next.map(Vec::as_slice).unwrap_or(&[])
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let x = ffi::make_demo("demo of cxx::bridge");
|
||||
println!("this is a {}", ffi::get_name(x.as_ref().unwrap()));
|
||||
let client = ffi::new_blobstore_client();
|
||||
|
||||
ffi::do_thing(ffi::SharedThing {
|
||||
z: 222,
|
||||
y: Box::new(ThingR(333)),
|
||||
x,
|
||||
});
|
||||
// Upload a blob.
|
||||
let chunks = vec![b"fearless".to_vec(), b"concurrency".to_vec()];
|
||||
let mut buf = MultiBuf { chunks, pos: 0 };
|
||||
let blobid = client.put(&mut buf);
|
||||
println!("blobid = {}", blobid);
|
||||
|
||||
// Add a tag.
|
||||
client.tag(blobid, "rust");
|
||||
|
||||
// Read back the tags.
|
||||
let metadata = client.metadata(blobid);
|
||||
println!("tags = {:?}", metadata.tags);
|
||||
}
|
||||
|
67
src/lib.rs
67
src/lib.rs
@ -57,6 +57,13 @@
|
||||
//!
|
||||
//! # Example
|
||||
//!
|
||||
//! In this example we are writing a Rust application that wishes to take
|
||||
//! advantage of an existing C++ client for a large-file blobstore service. The
|
||||
//! blobstore supports a `put` operation for a discontiguous buffer upload. For
|
||||
//! example we might be uploading snapshots of a circular buffer which would
|
||||
//! tend to consist of 2 chunks, or fragments of a file spread across memory for
|
||||
//! some other reason.
|
||||
//!
|
||||
//! A runnable version of this example is provided under the *demo* directory of
|
||||
//! <https://github.com/dtolnay/cxx>. To try it out, run `cargo run` from that
|
||||
//! directory.
|
||||
@ -65,57 +72,57 @@
|
||||
//! #[cxx::bridge]
|
||||
//! mod ffi {
|
||||
//! // Any shared structs, whose fields will be visible to both languages.
|
||||
//! struct SharedThing {
|
||||
//! z: i32,
|
||||
//! y: Box<ThingR>,
|
||||
//! x: UniquePtr<ThingC>,
|
||||
//! }
|
||||
//!
|
||||
//! extern "C" {
|
||||
//! // One or more headers with the matching C++ declarations. Our code
|
||||
//! // generators don't read it but it gets #include'd and used in static
|
||||
//! // assertions to ensure our picture of the FFI boundary is accurate.
|
||||
//! include!("demo/include/demo.h");
|
||||
//!
|
||||
//! // Zero or more opaque types which both languages can pass around but
|
||||
//! // only C++ can see the fields.
|
||||
//! type ThingC;
|
||||
//!
|
||||
//! // Functions implemented in C++.
|
||||
//! fn make_demo(appname: &str) -> UniquePtr<ThingC>;
|
||||
//! fn get_name(thing: &ThingC) -> &CxxString;
|
||||
//! fn do_thing(state: SharedThing);
|
||||
//! struct BlobMetadata {
|
||||
//! size: usize,
|
||||
//! tags: Vec<String>,
|
||||
//! }
|
||||
//!
|
||||
//! extern "Rust" {
|
||||
//! // Zero or more opaque types which both languages can pass around but
|
||||
//! // only Rust can see the fields.
|
||||
//! type ThingR;
|
||||
//! type MultiBuf;
|
||||
//!
|
||||
//! // Functions implemented in Rust.
|
||||
//! fn print_r(r: &ThingR);
|
||||
//! fn next_chunk(buf: &mut MultiBuf) -> &[u8];
|
||||
//! }
|
||||
//!
|
||||
//! extern "C++" {
|
||||
//! // One or more headers with the matching C++ declarations. Our code
|
||||
//! // generators don't read it but it gets #include'd and used in static
|
||||
//! // assertions to ensure our picture of the FFI boundary is accurate.
|
||||
//! include!("demo/include/blobstore.h");
|
||||
//!
|
||||
//! // Zero or more opaque types which both languages can pass around but
|
||||
//! // only C++ can see the fields.
|
||||
//! type BlobstoreClient;
|
||||
//!
|
||||
//! // Functions implemented in C++.
|
||||
//! fn new_blobstore_client() -> UniquePtr<BlobstoreClient>;
|
||||
//! fn put(&self, parts: &mut MultiBuf) -> u64;
|
||||
//! fn tag(&self, blobid: u64, tag: &str);
|
||||
//! fn metadata(&self, blobid: u64) -> BlobMetadata;
|
||||
//! }
|
||||
//! }
|
||||
//! #
|
||||
//! # pub struct ThingR(usize);
|
||||
//! # pub struct MultiBuf;
|
||||
//! #
|
||||
//! # fn print_r(r: &ThingR) {
|
||||
//! # println!("called back with r={}", r.0);
|
||||
//! # fn next_chunk(_buf: &mut MultiBuf) -> &[u8] {
|
||||
//! # unimplemented!()
|
||||
//! # }
|
||||
//! #
|
||||
//! # fn main() {}
|
||||
//! ```
|
||||
//!
|
||||
//! Now we simply provide C++ definitions of all the things in the `extern "C"`
|
||||
//! block and Rust definitions of all the things in the `extern "Rust"` block,
|
||||
//! and get to call back and forth safely.
|
||||
//! Now we simply provide Rust definitions of all the things in the `extern
|
||||
//! "Rust"` block and C++ definitions of all the things in the `extern "C++"`
|
||||
//! block, and get to call back and forth safely.
|
||||
//!
|
||||
//! Here are links to the complete set of source files involved in the demo:
|
||||
//!
|
||||
//! - [demo/src/main.rs](https://github.com/dtolnay/cxx/blob/master/demo/src/main.rs)
|
||||
//! - [demo/build.rs](https://github.com/dtolnay/cxx/blob/master/demo/build.rs)
|
||||
//! - [demo/include/demo.h](https://github.com/dtolnay/cxx/blob/master/demo/include/demo.h)
|
||||
//! - [demo/src/demo.cc](https://github.com/dtolnay/cxx/blob/master/demo/src/demo.cc)
|
||||
//! - [demo/include/blobstore.h](https://github.com/dtolnay/cxx/blob/master/demo/include/blobstore.h)
|
||||
//! - [demo/src/blobstore.cc](https://github.com/dtolnay/cxx/blob/master/demo/src/blobstore.cc)
|
||||
//!
|
||||
//! To look at the code generated in both languages for the example by the CXX
|
||||
//! code generators:
|
||||
|
Loading…
Reference in New Issue
Block a user