1# 交互工具使用指导 2 3## 概述 4 5Bindgen和CXX工具的主要功能是实现Rust和C/C++之间的交互。其中,Bindgen通过将C接口转换为Rust接口来实现Rust对C的调用,CXX可以通过建立C接口和Rust接口的映射关系来实现C++和Rust的相互调用。 6 7 8 9## Bindgen工具使用指导 10 11### 操作步骤 12下面是一个使用bindgen实现Rust调用C的示例。 13 141. 在C代码侧,使用头文件lib.h定义两个接口,接口FuncAAddB用来实现两数求和,接口SayHello用来打印字符串。 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. 在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. 添加文件main.rs,就可以在Rust侧通过c_ffi实现对C侧的接口调用。注意Rust侧调用的不安全接口需要使用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. 添加构建文件BUILD.gn,建立Rust模块对C模块的依赖。 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**调测验证** 106 107 108 109 110## CXX工具使用指导 111 112### C++调用Rust接口 113 1141. 在Rust侧文件lib.rs里mod ffi写清楚需要调用的C++接口,并将接口包含在extern "Rust"里面,暴露给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. C++侧将cxx工具转换出来的lib.rs.h包含进来,就可以使用C++侧的接口。 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. 添加构建文件BUILD.gn。rust_cxx底层调用CXX工具将lib.rs文件转换成lib.rs.h和lib.rs.cc文件,ohos_rust_static_ffi实现Rust侧源码的编译,ohos_executable实现C++侧代码的编译。 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**调测验证** 204 205 206 207 208### Rust调用C++ 209 2101. 添加头文件client_blobstore.h。 211 212 ```c++ 213 #ifndef BUILD_RUST_TESTS_CLIENT_BLOBSTORE_H 214 #define BUILD_RUST_TESTS_CLIENT_BLOBSTORE_H 215 #include <memory> 216 #include "third_party/rust/cxx/include/cxx.h" 217 218 namespace nsp_org { 219 namespace nsp_blobstore { 220 struct MultiBufs; 221 struct Metadata_Blob; 222 223 class client_blobstore { 224 public: 225 client_blobstore(); 226 uint64_t put_buf(MultiBufs &buf) const; 227 void add_tag(uint64_t blobid, rust::Str add_tag) const; 228 Metadata_Blob get_metadata(uint64_t blobid) const; 229 230 private: 231 class impl; 232 std::shared_ptr<impl> impl; 233 }; 234 235 std::unique_ptr<client_blobstore> blobstore_client_new(); 236 } // namespace nsp_blobstore 237 } // namespace nsp_org 238 #endif 239 ``` 240 2412. 添加cpp文件client_blobstore.cpp。 242 243 ```c++ 244 #include <algorithm> 245 #include <functional> 246 #include <set> 247 #include <string> 248 #include <unordered_map> 249 #include "src/main.rs.h" 250 #include "build/rust/tests/test_cxx_rust/include/client_blobstore.h" 251 252 namespace nsp_org { 253 namespace nsp_blobstore { 254 // Toy implementation of an in-memory nsp_blobstore. 255 // 256 // In reality the implementation of client_blobstore could be a large complex C++ 257 // library. 258 class client_blobstore::impl { 259 friend client_blobstore; 260 using Blob = struct { 261 std::string data; 262 std::set<std::string> tags; 263 }; 264 std::unordered_map<uint64_t, Blob> blobs; 265 }; 266 267 client_blobstore::client_blobstore() : impl(new class client_blobstore::impl) {} 268 269 // Upload a new blob and return a blobid that serves as a handle to the blob. 270 uint64_t client_blobstore::put_buf(MultiBufs &buf) const 271 { 272 std::string contents; 273 274 // Traverse the caller's res_chunk iterator. 275 // 276 // In reality there might be sophisticated batching of chunks and/or parallel 277 // upload implemented by the nsp_blobstore's C++ client. 278 while (true) { 279 auto res_chunk = next_chunk(buf); 280 if (res_chunk.size() == 0) { 281 break; 282 } 283 contents.append(reinterpret_cast<const char *>(res_chunk.data()), res_chunk.size()); 284 } 285 286 // Insert into map and provide caller the handle. 287 auto res = std::hash<std::string> {} (contents); 288 impl->blobs[res] = {std::move(contents), {}}; 289 return res; 290 } 291 292 // Add add_tag to an existing blob. 293 void client_blobstore::add_tag(uint64_t blobid, rust::Str add_tag) const 294 { 295 impl->blobs[blobid].tags.emplace(add_tag); 296 } 297 298 // Retrieve get_metadata about a blob. 299 Metadata_Blob client_blobstore::get_metadata(uint64_t blobid) const 300 { 301 Metadata_Blob get_metadata {}; 302 auto blob = impl->blobs.find(blobid); 303 if (blob != impl->blobs.end()) { 304 get_metadata.size = blob->second.data.size(); 305 std::for_each(blob->second.tags.cbegin(), blob->second.tags.cend(), 306 [&](auto &t) { get_metadata.tags.emplace_back(t); }); 307 } 308 return get_metadata; 309 } 310 311 std::unique_ptr<client_blobstore> blobstore_client_new() 312 { 313 return std::make_unique<client_blobstore>(); 314 } 315 } // namespace nsp_blobstore 316 } // namespace nsp_org 317 318 ``` 319 3203. main.rs文件,在main.rs文件的ffi里面,通过宏include!将头文件client_blobstore.h引入进来,从而在Rust的main函数里面就可以通过ffi的方式调用C++的接口。 321 322 ```rust 323 //! test_cxx_rust 324 #[cxx::bridge(namespace = "nsp_org::nsp_blobstore")] 325 mod ffi { 326 // Shared structs with fields visible to both languages. 327 struct Metadata_Blob { 328 size: usize, 329 tags: Vec<String>, 330 } 331 332 // Rust types and signatures exposed to C++. 333 extern "Rust" { 334 type MultiBufs; 335 336 fn next_chunk(buf: &mut MultiBufs) -> &[u8]; 337 } 338 339 // C++ types and signatures exposed to Rust. 340 unsafe extern "C++" { 341 include!("build/rust/tests/test_cxx_rust/include/client_blobstore.h"); 342 343 type client_blobstore; 344 345 fn blobstore_client_new() -> UniquePtr<client_blobstore>; 346 fn put_buf(&self, parts: &mut MultiBufs) -> u64; 347 fn add_tag(&self, blobid: u64, add_tag: &str); 348 fn get_metadata(&self, blobid: u64) -> Metadata_Blob; 349 } 350 } 351 352 // An iterator over contiguous chunks of a discontiguous file object. 353 // 354 // Toy implementation uses a Vec<Vec<u8>> but in reality this might be iterating 355 // over some more complex Rust data structure like a rope, or maybe loading 356 // chunks lazily from somewhere. 357 /// pub struct MultiBufs 358 pub struct MultiBufs { 359 chunks: Vec<Vec<u8>>, 360 pos: usize, 361 } 362 /// pub fn next_chunk 363 pub fn next_chunk(buf: &mut MultiBufs) -> &[u8] { 364 let next = buf.chunks.get(buf.pos); 365 buf.pos += 1; 366 next.map_or(&[], Vec::as_slice) 367 } 368 369 /// fn main() 370 fn main() { 371 let client = ffi::blobstore_client_new(); 372 373 // Upload a blob. 374 let chunks = vec![b"fearless".to_vec(), b"concurrency".to_vec()]; 375 let mut buf = MultiBufs { chunks, pos: 0 }; 376 let blobid = client.put_buf(&mut buf); 377 println!("This is a test for Rust call cpp:"); 378 println!("blobid = {}", blobid); 379 380 // Add a add_tag. 381 client.add_tag(blobid, "rust"); 382 383 // Read back the tags. 384 let get_metadata = client.get_metadata(blobid); 385 println!("tags = {:?}", get_metadata.tags); 386 } 387 ``` 388 3894. 添加构建文件BUILD.gn。使用CXX将main.rs转换成lib.rs.h和lib.rs.cc,同时将产物作为test_cxx_rust_staticlib的源码,编译Rust源码main.rs并将test_cxx_rust_staticlib依赖进来。 390 391 ``` 392 import("//build/ohos.gni") 393 394 rust_cxx("test_cxx_rust_gen") { 395 sources = [ "src/main.rs" ] 396 } 397 398 ohos_static_library("test_cxx_rust_staticlib") { 399 sources = [ "src/client_blobstore.cpp" ] 400 sources += get_target_outputs(":test_cxx_rust_gen") 401 include_dirs = [ 402 "${target_gen_dir}", 403 "//third_party/rust/cxx/v1/crate/include", 404 "include", 405 ] 406 deps = [ 407 ":test_cxx_rust_gen", 408 "//build/rust:cxx_cppdeps", 409 ] 410 } 411 412 ohos_rust_executable("test_cxx_rust") { 413 sources = [ "src/main.rs" ] 414 deps = [ 415 ":test_cxx_rust_staticlib", 416 "//build/rust:cxx_rustdeps", 417 ] 418 } 419 ``` 420 421**调测验证** 422 423