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### Rust调用C++ 208 2091. 添加头文件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. 添加cpp文件client_blobstore.cpp。 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 // In reality the 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. main.rs文件,在main.rs文件的ffi里面,通过宏include!将头文件client_blobstore.h引入进来,从而在Rust的main函数里面就可以通过ffi的方式调用C++的接口。 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. 添加构建文件BUILD.gn。使用CXX将main.rs转换成lib.rs.h和lib.rs.cc,同时将产物作为test_cxx_rust_staticlib的源码,编译Rust源码main.rs并将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**调测验证** 421