• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# 交互工具使用指导
2
3## 概述
4
5Bindgen和CXX工具的主要功能是实现Rust和C/C++之间的交互。其中,Bindgen通过将C接口转换为Rust接口来实现Rust对C的调用,CXX可以通过建立C接口和Rust接口的映射关系来实现C++和Rust的相互调用。
6
7![bindgen_and_cxx_tools](./figures/bindgen_and_cxx_tools.png)
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![bindgen_test](./figures/bindgen_test.png)
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.hlib.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![cpp_call_rust](./figures/cpp_call_rust.png)
206
207
208### Rust调用C++
209
2101. 添加头文件client_blobstore.h211
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.cpp242
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.hlib.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![rust_call_cpp](./figures/rust_call_cpp.png)