1# Interactive Tool User Guide 2 3## Introduction 4 5You can use Bindgen and CXX to implement the interaction between Rust and C/C++. Bindgen enables Rust to call C by converting C interfaces into Rust interfaces. CXX implement interaction between C++ and Rust by generating bindings between C interfaces and Rust interfaces. 6 7 8 9## Using Bindgen 10 11### Procedure 12The following example shows how to use Bindgen to implement invocation of C by Rust. 13 141. In the header file **lib.h**, define two interfaces **FuncAAddB** and **SayHello** in C. The **FuncAAddB** interface calculates the sum of two numbers, and the **SayHello** interface prints strings. 15 16 ```c 17 #ifndef BUILD_RUST_TESTS_BINDGEN_TEST_LIB_H_ 18 #define BUILD_RUST_TESTS_BINDGEN_TEST_LIB_H_ 19 #include <stdint.h> 20 #include "build/rust/tests/test_bindgen_test/test_for_hello_world/lib2.h" 21 22 uint32_t FuncAAddB(uint32_t a, uint32_t b); 23 void SayHello(const char *message); 24 25 #endif // BUILD_RUST_TESTS_BINDGEN_TEST_LIB_H_ 26 ``` 27 28 292. Add the implementation of the two interfaces to **lib.c**. 30 31 ```c 32 #include "build/rust/tests/test_bindgen_test/test_for_hello_world/lib.h" 33 #include <stdint.h> 34 #include <stdio.h> 35 36 void SayHello(const char *message) 37 { 38 printf("This is a test for bindgen hello world:\n"); 39 printf("%s\n", message); 40 } 41 42 uint32_t FuncAAddB(uint32_t a, uint32_t b) 43 { 44 printf("This is a test for bindgen of a + b:\n"); 45 return a + b; 46 } 47 ``` 48 493. Create the **main.rs** file to call C interfaces through c_ffi using Rust. Note that insecure interfaces called by Rust must be encapsulated by using **unsafe**. 50 51 ```rust 52 //! bindgen test for hello world 53 #![allow(clippy::approx_constant)] 54 mod c_ffi { 55 #![allow(dead_code)] 56 #![allow(non_upper_case_globals)] 57 #![allow(non_camel_case_types)] 58 include!(env!("BINDGEN_RS_FILE")); 59 } 60 /// pub fn add_two_numbers_in_c 61 pub fn add_two_numbers_in_c(a: u32, b: u32) -> u32 { 62 unsafe { c_ffi::FuncAAddB(a, b) } 63 } 64 65 use std::ffi::c_char; 66 use std::ffi::CString; 67 68 /// fn main() 69 fn main() { 70 println!("{} + {} = {}", 3, 7, add_two_numbers_in_c(3, 7)); 71 let c_str = CString::new("This is a message from C").unwrap(); 72 let c_world: *const c_char = c_str.as_ptr() as *const c_char; 73 unsafe { 74 c_ffi::SayHello(c_world); 75 } 76 } 77 78 ``` 79 804. Create the **BUILD.gn** file to define the dependency of the Rust module on the C module. 81 82 ```GN 83 import("//build/ohos.gni") 84 85 ohos_shared_library("c_lib") { 86 sources = [ "lib.c" ] 87 defines = [ "COMPONENT_IMPLEMENTATION" ] 88 } 89 90 rust_bindgen("c_lib_bindgen") { 91 header = "lib.h" 92 } 93 94 ohos_rust_executable("bindgen_test") { 95 deps = [ ":c_lib" ] 96 deps += [ ":c_lib_bindgen" ] 97 sources = [ "main.rs" ] 98 bindgen_output = get_target_outputs(":c_lib_bindgen") 99 inputs = bindgen_output 100 rustenv = [ "BINDGEN_RS_FILE=" + rebase_path(bindgen_output[0]) ] 101 crate_root = "main.rs" 102 } 103 ``` 104 105**Verification** 106 107 108 109 110## Using CXX 111 112### Calling Rust Interfaces by C++ 113 1141. In the Rust **lib.rs** file, add the C++ interfaces to be called in **mod ffi**, and add the interfaces in **extern "Rust"** to expose them to C++. 115 116 ```rust 117 //! #[cxx::bridge] 118 #[cxx::bridge] 119 mod ffi{ 120 #![allow(dead_code)] 121 #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] 122 struct Shared { 123 z: usize, 124 } 125 extern "Rust"{ 126 fn print_message_in_rust(); 127 fn r_return_primitive() -> usize; 128 fn r_return_shared() -> Shared; 129 fn r_return_rust_string() -> String; 130 fn r_return_sum(_: usize, _: usize) -> usize; 131 } 132 } 133 134 fn print_message_in_rust(){ 135 println!("Here is a test for cpp call Rust."); 136 } 137 fn r_return_shared() -> ffi::Shared { 138 println!("Here is a message from Rust,test for ffi::Shared:"); 139 ffi::Shared { z: 1996 } 140 } 141 fn r_return_primitive() -> usize { 142 println!("Here is a message from Rust,test for usize:"); 143 1997 144 } 145 fn r_return_rust_string() -> String { 146 println!("Here is a message from Rust,test for String"); 147 "Hello World!".to_owned() 148 } 149 fn r_return_sum(n1: usize, n2: usize) -> usize { 150 println!("Here is a message from Rust,test for {} + {} is:",n1 ,n2); 151 n1 + n2 152 } 153 154 ``` 155 1562. Include **lib.rs.h** (converted from **lib.rs** by the CXX tool) in C++ code. 157 158 ```c++ 159 #include <iostream> 160 #include "build/rust/tests/test_cxx/src/lib.rs.h" 161 162 int main(int argc, const char* argv[]) 163 { 164 int a = 2021; 165 int b = 4; 166 print_message_in_rust(); 167 std::cout << r_return_primitive() << std::endl; 168 std::cout << r_return_shared().z << std::endl; 169 std::cout << std::string(r_return_rust_string()) << std::endl; 170 std::cout << r_return_sum(a, b) << std::endl; 171 return 0; 172 } 173 ``` 174 1753. Create the **BUILD.gn** file. The underlying rust_cxx calls the CXX tool to convert the **lib.rs** file into **lib.rs.h** and **lib.rs.cc**. **ohos_rust_static_ffi** implements compilation of the Rust source code, and **ohos_executable** implements compilation of the C++ code. 176 177 ``` 178 import("//build/ohos.gni") 179 import("//build/templates/rust/rust_cxx.gni") 180 181 rust_cxx("test_cxx_exe_gen") { 182 sources = [ "src/lib.rs" ] 183 } 184 185 ohos_rust_static_ffi("test_cxx_examp_rust") { 186 sources = [ "src/lib.rs" ] 187 deps = [ "//build/rust:cxx_rustdeps" ] 188 } 189 190 ohos_executable("test_cxx_exe") { 191 sources = [ "main.cpp" ] 192 sources += get_target_outputs(":test_cxx_exe_gen") 193 194 include_dirs = [ "${target_gen_dir}" ] 195 deps = [ 196 ":test_cxx_examp_rust", 197 ":test_cxx_exe_gen", 198 "//build/rust:cxx_cppdeps", 199 ] 200 } 201 ``` 202 203**Verification** 204 205 206 207### Calling C++ by Rust 208 2091. Create the header file **client_blobstore.h**. 210 211 ```c++ 212 #ifndef BUILD_RUST_TESTS_CLIENT_BLOBSTORE_H 213 #define BUILD_RUST_TESTS_CLIENT_BLOBSTORE_H 214 #include <memory> 215 #include "third_party/rust/cxx/include/cxx.h" 216 217 namespace nsp_org { 218 namespace nsp_blobstore { 219 struct MultiBufs; 220 struct Metadata_Blob; 221 222 class client_blobstore { 223 public: 224 client_blobstore(); 225 uint64_t put_buf(MultiBufs &buf) const; 226 void add_tag(uint64_t blobid, rust::Str add_tag) const; 227 Metadata_Blob get_metadata(uint64_t blobid) const; 228 229 private: 230 class impl; 231 std::shared_ptr<impl> impl; 232 }; 233 234 std::unique_ptr<client_blobstore> blobstore_client_new(); 235 } // namespace nsp_blobstore 236 } // namespace nsp_org 237 #endif 238 ``` 239 2402. Create the **client_blobstore.cpp** file. 241 242 ```c++ 243 #include <algorithm> 244 #include <functional> 245 #include <set> 246 #include <string> 247 #include <unordered_map> 248 #include "src/main.rs.h" 249 #include "build/rust/tests/test_cxx_rust/include/client_blobstore.h" 250 251 namespace nsp_org { 252 namespace nsp_blobstore { 253 // Toy implementation of an in-memory nsp_blobstore. 254 // 255 // The real implementation of client_blobstore could be a large complex C++ 256 // library. 257 class client_blobstore::impl { 258 friend client_blobstore; 259 using Blob = struct { 260 std::string data; 261 std::set<std::string> tags; 262 }; 263 std::unordered_map<uint64_t, Blob> blobs; 264 }; 265 266 client_blobstore::client_blobstore() : impl(new class client_blobstore::impl) {} 267 268 // Upload a new blob and return a blobid that serves as a handle to the blob. 269 uint64_t client_blobstore::put_buf(MultiBufs &buf) const 270 { 271 std::string contents; 272 273 // Traverse the caller's res_chunk iterator. 274 // 275 // In reality there might be sophisticated batching of chunks and/or parallel 276 // upload implemented by the nsp_blobstore's C++ client. 277 while (true) { 278 auto res_chunk = next_chunk(buf); 279 if (res_chunk.size() == 0) { 280 break; 281 } 282 contents.append(reinterpret_cast<const char *>(res_chunk.data()), res_chunk.size()); 283 } 284 285 // Insert into map and provide caller the handle. 286 auto res = std::hash<std::string> {} (contents); 287 impl->blobs[res] = {std::move(contents), {}}; 288 return res; 289 } 290 291 // Add add_tag to an existing blob. 292 void client_blobstore::add_tag(uint64_t blobid, rust::Str add_tag) const 293 { 294 impl->blobs[blobid].tags.emplace(add_tag); 295 } 296 297 // Retrieve get_metadata about a blob. 298 Metadata_Blob client_blobstore::get_metadata(uint64_t blobid) const 299 { 300 Metadata_Blob get_metadata {}; 301 auto blob = impl->blobs.find(blobid); 302 if (blob != impl->blobs.end()) { 303 get_metadata.size = blob->second.data.size(); 304 std::for_each(blob->second.tags.cbegin(), blob->second.tags.cend(), 305 [&](auto &t) { get_metadata.tags.emplace_back(t); }); 306 } 307 return get_metadata; 308 } 309 310 std::unique_ptr<client_blobstore> blobstore_client_new() 311 { 312 return std::make_unique<client_blobstore>(); 313 } 314 } // namespace nsp_blobstore 315 } // namespace nsp_org 316 317 ``` 318 3193. In **ffi** of the **main.rs** file, use the macro **includes!** to import the header file **client_blobstore.h**. Then, the **main()** function of Rust can call the C++ interfaces in ffi mode. 320 321 ```rust 322 //! test_cxx_rust 323 #[cxx::bridge(namespace = "nsp_org::nsp_blobstore")] 324 mod ffi { 325 // Shared structs with fields visible to both languages. 326 struct Metadata_Blob { 327 size: usize, 328 tags: Vec<String>, 329 } 330 331 // Rust types and signatures exposed to C++. 332 extern "Rust" { 333 type MultiBufs; 334 335 fn next_chunk(buf: &mut MultiBufs) -> &[u8]; 336 } 337 338 // C++ types and signatures exposed to Rust. 339 unsafe extern "C++" { 340 include!("build/rust/tests/test_cxx_rust/include/client_blobstore.h"); 341 342 type client_blobstore; 343 344 fn blobstore_client_new() -> UniquePtr<client_blobstore>; 345 fn put_buf(&self, parts: &mut MultiBufs) -> u64; 346 fn add_tag(&self, blobid: u64, add_tag: &str); 347 fn get_metadata(&self, blobid: u64) -> Metadata_Blob; 348 } 349 } 350 351 // An iterator over contiguous chunks of a discontiguous file object. 352 // 353 // Toy implementation uses a Vec<Vec<u8>> but in reality this might be iterating 354 // over some more complex Rust data structure like a rope, or maybe loading 355 // chunks lazily from somewhere. 356 /// pub struct MultiBufs 357 pub struct MultiBufs { 358 chunks: Vec<Vec<u8>>, 359 pos: usize, 360 } 361 /// pub fn next_chunk 362 pub fn next_chunk(buf: &mut MultiBufs) -> &[u8] { 363 let next = buf.chunks.get(buf.pos); 364 buf.pos += 1; 365 next.map_or(&[], Vec::as_slice) 366 } 367 368 /// fn main() 369 fn main() { 370 let client = ffi::blobstore_client_new(); 371 372 // Upload a blob. 373 let chunks = vec![b"fearless".to_vec(), b"concurrency".to_vec()]; 374 let mut buf = MultiBufs { chunks, pos: 0 }; 375 let blobid = client.put_buf(&mut buf); 376 println!("This is a test for Rust call cpp:"); 377 println!("blobid = {}", blobid); 378 379 // Add a add_tag. 380 client.add_tag(blobid, "rust"); 381 382 // Read back the tags. 383 let get_metadata = client.get_metadata(blobid); 384 println!("tags = {:?}", get_metadata.tags); 385 } 386 ``` 387 3884. Create the **BUILD.gn** file. Use CXX to convert **main.rs** into **lib.rs.h** and **lib.rs.cc**, which are used as the source code of **test_cxx_rust_staticlib**. Compile Rust **main.rs**, and add the dependency **test_cxx_rust_staticlib**. 389 390 ``` 391 import("//build/ohos.gni") 392 393 rust_cxx("test_cxx_rust_gen") { 394 sources = [ "src/main.rs" ] 395 } 396 397 ohos_static_library("test_cxx_rust_staticlib") { 398 sources = [ "src/client_blobstore.cpp" ] 399 sources += get_target_outputs(":test_cxx_rust_gen") 400 include_dirs = [ 401 "${target_gen_dir}", 402 "//third_party/rust/cxx/v1/crate/include", 403 "include", 404 ] 405 deps = [ 406 ":test_cxx_rust_gen", 407 "//build/rust:cxx_cppdeps", 408 ] 409 } 410 411 ohos_rust_executable("test_cxx_rust") { 412 sources = [ "src/main.rs" ] 413 deps = [ 414 ":test_cxx_rust_staticlib", 415 "//build/rust:cxx_rustdeps", 416 ] 417 } 418 ``` 419 420**Verification** 421 422