文盘Rust -- FFI 浅尝 | 京东云技术团队
rust FFI 是rust与其他语言互调的桥梁,通过FFI rust 可以有效继承 C 语言的历史资产。本期通过几个例子来聊聊rust与C 语言交互的具体步骤。
场景一 调用C代码
创建工程
cargo new --bin ffi_sample
Cargo.toml 配置
[package]
name = "ffi_sample"
version = "0.1.0"
edition = "2021"
build = "build.rs"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[build-dependencies]
cc = "1.0.79"
[dependencies]
libc = "0.2.146"
libloading = "0.8.0"
编写一个简单的c程序sample.c
int add(int a,int b){
    return a+b;
}
use std::os::raw::c_int;
#[link(name = "sample")]
extern "C" {
    fn add(a: c_int, b: c_int) -> c_int;
}
fn main() {
    let r = unsafe { add(2, 18) };
    println!("{:?}", r);
}
fn main() {
    cc::Build::new().file("sample.c").compile("sample");
}
代码目录树
.
├── Cargo.lock
├── Cargo.toml
├── build.rs
├── sample.c
└── src
    └── main.rs
cargo run
场景二 使用bindgen 通过头文件绑定c语言动态链接库
修改Cargo.toml,新增bindgen依赖
[package]
name = "ffi_sample"
version = "0.1.0"
edition = "2021"
build = "build.rs"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[build-dependencies]
cc = "1.0.79"
bindgen = "0.65.1"
[dependencies]
libc = "0.2.146"
libloading = "0.8.0"
新增 sample.h 头文件
#ifndef ADD_H
#define ADD_H
int add(int a, int b);
#endif
新增 wrapper.h 头文件 wrapper.h 文件将包括所有各种头文件,这些头文件包含我们想要绑定的结构和函数的声明
#include "sample.h";
改写build.rs 编译 sample.c 生成动态链接库sample.so;通过bindgen生成rust binding c 的代码并输出到 bindings 目录
use std::path::PathBuf;
fn main() {
    // 参考cc 文档
    println!("cargo:rerun-if-changed=sample.c");
    cc::Build::new()
        .file("sample.c")
        .shared_flag(true)
        .compile("sample.so");
    // 参考 https://doc.rust-lang.org/cargo/reference/build-scripts.html
    println!("cargo:rustc-link-lib=sample.so");
    println!("cargo:rerun-if-changed=sample.h");
    let bindings = bindgen::Builder::default()
        .header("wrapper.h")
        .parse_callbacks(Box::new(bindgen::CargoCallbacks))
        .generate()
        .expect("Unable to generate bindings");
    let out_path = PathBuf::from("bindings");
    bindings
        .write_to_file(out_path.join("sample_bindings.rs"))
        .expect("Couldn't write bindings!");
}
修改main.rs include 宏引入sample 动态链接库的binding。以前我们自己手写的C函数绑定就不需要了,看看bindings/sample_bindings.rs 的内容与我们手写的函数绑定是等效的
include!("../bindings/sample_bindings.rs");
// #[link(name = "sample")]
// extern "C" {
//     fn add(a: c_int, b: c_int) -> c_int;
// }
fn main() {
    let r = unsafe { add(2, 18) };
    println!("{:?}", r);
}
代码目录树
.
├── Cargo.lock
├── Cargo.toml
├── bindings
│   └── sample_bindings.rs
├── build.rs
├── sample.c
├── sample.h
├── src
│   └── main.rs
└── wrapper.h
ffi_sample 工程的完整代码位置,读者可以clone https://github.com/jiashiwen/wenpanrust,直接运行即可
cargo run -p ffi_sample
场景三 封装一个c编写的库
secp256k1是一个椭圆曲线计算的 clib,这玩意儿在密码学和隐私计算方面的常用算法,下面我们从工程方面看看封装secp256k1如何操作
cargo new --lib wrapper_secp256k1
cargo.toml
[package]
name = "wrapper_secp256k1"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[build-dependencies]
cc = "1.0.79"
bindgen = "0.65.1"
[dependencies]
git 引入 submodule
cd wrapper_secp256k1
git submodule add https://github.com/bitcoin-core/secp256k1  wrapper_secp256k1/secp256k1_sys 
工程下新建bindings目录用来存放绑定文件,该目录与src平级
wrapper.h
#include "secp256k1_sys/secp256k1/include/secp256k1.h"
use std::path::PathBuf;
fn main() {
    println!("cargo:rustc-link-lib=secp256k1");
    println!("cargo:rerun-if-changed=wrapper.h");
    let bindings = bindgen::Builder::default()
        .header("wrapper.h")
        .parse_callbacks(Box::new(bindgen::CargoCallbacks))
        .generate()
        .expect("Unable to generate bindings");
    let out_path = PathBuf::from("bindings");
    bindings
        .write_to_file(out_path.join("bindings.rs"))
        .expect("Couldn't write bindings!");
}
cargo build 通过
编写测试 lib.rs
include!("../bindings/secp256k1.rs");
#[cfg(test)]
mod tests {
    use super::*;
    #[test]
    fn test_create_pubkey() {
        // secp256k1返回公钥
        let mut pubkey: secp256k1_pubkey = secp256k1_pubkey { data: [0; 64] };
        let prikey: u8 = 1;
        unsafe {
            let context = secp256k1_context_create(SECP256K1_CONTEXT_SIGN);
            assert!(!context.is_null());
            let ret = secp256k1_ec_pubkey_create(&*context, &mut pubkey, &prikey);
            assert_eq!(ret, 1);
        }
    }
}
运行测试 cargo test 报错
warning: `wrapper_secp256k1` (lib) generated 5 warnings
error: linking with `cc` failed: exit status: 1
  |
  = note: LC_ALL="C" PATH="/usr/local/Cellar/rust/1.70.0/lib/rustlib/x86_64-apple-darwin/bin:/Users/jiashiwen/local/Cellar/rust/1.70.0/lib/rustlib/x86_64-apple-darwin/lib/libobject-6d1da0e5d7930106.rlib" "/usr/local/Cellar/rust/1.70.0/lib/rustlib/x86_64-apple-darwin/lib/libmemchr-d6d74858e37ed726.rlib" "/usr/local/Cellar/rust/1.70.0/lib/rustlib/x86_64-apple-darwin/lib/libaddr2line-d75e66c6c1b76fdd.rlib" "/usr/local/Cellar/rust/1.70.0/lib/rustlib/x86_64-apple-darwin/lib/libgimli-546ea342344e3761.rlib" "/usr/local/Cellar/rust/1.70.0/lib/rustlib/x86_64-apple-darwin/lib/librustc_demangle-8ad10e36ca13f067.rlib" "/usr/local/Cellar/rust/1.70.0/lib/rustlib/x86_64-apple-darwin/lib/libstd_detect-0543b8486ac00cf6.rlib" "/usr/local/Cellar/rust/1.70.0/lib/rustlib/x86_64-apple-darwin/lib/libhashbrown-7f0d42017ce08763.rlib" "/usr/local/Cellar/rust/1.70.0/lib/rustlib/x86_64-apple-darwin/lib/libminiz_oxide-65e6b9c4725e3b7f.rlib" "/usr/local/Cellar/rust/1.70.0/lib/rustlib/x86_64-apple-darwin/lib/libadler-131157f72607aea7.rlib" "/usr/local/Cellar/rust/1.70.0/lib/rustlib/x86_64-apple-darwin/lib/librustc_std_workspace_alloc-f7d15060b16c135d.rlib" "/usr/local/Cellar/rust/1.70.0/lib/rustlib/x86_64-apple-darwin/lib/libunwind-a52bfac5ae872be2.rlib" "/usr/local/Cellar/rust/1.70.0/lib/rustlib/x86_64-apple-darwin/lib/libcfg_if-1762d9ac100ea3e7.rlib" "/usr/local/Cellar/rust/1.70.0/lib/rustlib/x86_64-apple-darwin/lib/liblibc-f8e0e4708f61f3f4.rlib" "/usr/local/Cellar/rust/1.70.0/lib/rustlib/x86_64-apple-darwin/lib/liballoc-af9a608dd9cb26b2.rlib" "/usr/local/Cellar/rust/1.70.0/lib/rustlib/x86_64-apple-darwin/lib/librustc_std_workspace_core-9777023438fd3d6a.rlib" "/usr/local/Cellar/rust/1.70.0/lib/rustlib/x86_64-apple-darwin/lib/libcore-83ca6d61eb70e9b8.rlib" "/usr/local/Cellar/rust/1.70.0/lib/rustlib/x86_64-apple-darwin/lib/libcompiler_builtins-ea2ca6e1df0449b8.rlib" "-lSystem" "-lc" "-lm" "-L" "/usr/local/Cellar/rust/1.70.0/lib/rustlib/x86_64-apple-darwin/lib" "-o" "/Users/jiashiwen/rustproject/wrapper_secp256k1/target/debug/deps/wrapper_secp256k1-4bf30c62ecfdf2a7" "-Wl,-dead_strip" "-nodefaultlibs"
  = note: ld: library not found for -lsecp256k1
          clang: error: linker command failed with exit code 1 (use -v to see invocation)
warning: `wrapper_secp256k1` (lib test) generated 5 warnings (5 duplicates)
error: could not compile `wrapper_secp256k1` (lib test) due to previous error; 5 warnings emitted
报错显示找不到编译 secp256k1 相对应的库。
手动编译secp256k1
cd secp256k1_sys
./autogen.sh
./configure
make
make install
编译完成后,测试通过
其实 secp256k1 有对应的 rust wrapper,我们这里只是展示一下封装的过程。
wrapper_secp256k1 工程的完整代码位置,有兴趣的朋友可以clone https://github.com/jiashiwen/wenpanrust。通过以下操作查看运行结果:
- 
  
clone 项目
git clone https://github.com/jiashiwen/wenpanrust cd wenpanrust - 
  
update submodule
git submodule init git submodule update - 
  
编译 secp256k1
cd wrapper_secp256k1/secp256k1_sys ./autogen.sh ./configure make make install - 
  
run test
cargo test -p wrapper_secp256k1 
参考资料
Rust FFI (C vs Rust)学习杂记.pdf
bindgen官方文档
Rust FFI 编程 - bindgen 使用示例
作者:京东科技 贾世闻
来源:京东云开发者社区
关注公众号
					低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 
							
								
								    上一篇
								    
								
								【OpenAI】ChatGPT函数调用(Function Calling)实践 | 京东云技术团队
6月13日OpenAI在Chat Completions API中添加了新的函数调用(Function Calling)能力,帮助开发者通过API方式实现类似于ChatGPT插件的数据交互能力。 本文在作者上一篇文章《私有框架代码生成实践》的基础上,依旧使用自然语言低代码搭建场景作为案例,将嵌入向量搜索(Embedding)获取私有知识库的方式,替换为函数调用方式,以我们更熟悉的结构化数据结构、关系型数据库的方式进行知识库管理。同时函数调用能力的灵活性和可扩展性,也可以帮助用户使用自然语言搭建更加复杂的页面内容、进行更丰富的交互操作。 一、 什么是函数调用 函数调用(Function Calling)是OpenAI在6月13日发布的新能力。根据官方博客描述,函数调用能力可以让模型输出一个请求调用函数的消息,其中包含所需调用的函数信息、以及调用函数时所携带的参数信息。这是一种将GPT能力与外部工具/API连接起来的新方式。 支持函数调用的新模型,可以根据用户的输入自行判断何时需要调用哪些函数,并且可以根据目标函数的描述生成符合要求的请求参数。 开发人员可以使用函数调用能力,通过GPT实现...
 - 
							
								
								    下一篇
								    
								
								即刻洞悉市场,一文教你实时把握股票涨幅
在股票交易市场,涨幅是一个基础的量价指标,可以衡量资金对个股的拉升力度,是监控主力异动的重要指标。通过关注涨幅,可以了解到资金青睐哪些个股,市场关注哪些板块。 本教程主要提供一种基于 DolphinDB 流数据处理框架,实时计算 1 分钟、5 分钟和10 分钟涨幅榜的低延时解决方案。 1. 应用场景描述 1.1 数据源 本教程基于上交所 2021 年某日的快照数据进行代码调试,在 DolphinDB 中存储的表结构为: 字段名称 数据类型 数据说明 securityID SYMBOL 证券代码 dateTime TIMESTAMP 日期时间 preClosePx DOUBLE 昨收价 openPx DOUBLE 开始价 highPx DOUBLE 最高价 lowPx DOUBLE 最低价 lastPx DOUBLE 最新价 …… …… …… 1.2 计算指标 本教程示例代码计算了 1 分钟,5 分钟,10 分钟涨幅榜。 涨幅计算公式:(lastPx - prevLastPx) / prevLastPx * 100 % 指标名称 含义 factor_1min 1 分钟涨幅,prevLas...
 
相关文章
文章评论
共有0条评论来说两句吧...

			
				
				
				
				
				
				
				
微信收款码
支付宝收款码