Expand description
hicc提供调用c++接口的能力.
三个相关的库:
hicc核心功能库.hicc-std为c++标准库中的容器提供rust api.hicc-build方便编译c++适配代码.
**已知约束:
- 依赖
c++11或更高版本的特性.** - 不直接支持操作符重载函数,需要利用
hicc::cpp封装
§类型映射通用规则
使用者需要了解映射规则, 特别是c++类作为函数返回值时映射后的类型.
hicc::import_class和hicc::import_lib宏会基于映射规则更改函数的参数和返回值类型,新类型支持自动解引用.
在自动解引用无法支持的场景需要显式调用相关接口实现类型转换.
§函数返回类型映射规则.
c++返回类类型 | rust对应类型 |
|---|---|
| T | T |
T&& | T |
std::unique_ptr<T> | T |
std::unique_ptr<T, D> | hicc::unique_ptr<T> |
std::shared_ptr<T, D> | hicc::shared_ptr<T> |
const T& | hicc::ClassRef<'_, T> |
T& | hicc::ClassRefMut<'_, T> |
T* | hicc::ClassMutPtr<'_, T, 1> |
const T* | hicc::ClassPtr<'_, T, 1> |
注意:
- 字节对齐要求: 要求所有
c++对象都至少按照size_t字节数进行对齐, 未对齐地址被视为非法指针. hicc::ClassPtr<'_, T, N = 1>支持多重指针的,所有的指针映射规则都相同, 作为返回值时,其生命周期参数为'static.- 所有
c++类映射类型T都支持hicc::AbiClasstrait. T,ClassRef<'_, T>,ClassRefMut<'_, T>,ClassPtr<'_, T>,ClassMutPtr<'_, T>这些类型内存布局相同,可相互转换.
合理使用ClassRef<'_, T>作为返回值类型,因为携带了生命周期参数,可消除c++接口存在的内存安全问题. 典型的是stl容器的迭代器接口,如下所示:
hicc::import_class! {
#[cpp(class = "std::vector<int>")]
struct IntVec {
#[cpp(method = "typename Self::const_iterator begin() const")]
fn begin(&self) -> &IntVecIter;
}
#[cpp(class = "std::vector<int>::const_iterator")]
struct IntVecIter {
}
}注: hicc::import_class宏会将返回类型&IntVecIter修改为hicc::ClassRef<'_, IntVecIter>.
上面例子中,将rust接口中返回值的类型修改为引用类型,如此可以避免c++中因为修改容器导致修改前获取的迭代器失效带来的内存安全问题.
注意: 这也是为什么不从c++头文件自动生成rust api的原因,
直接映射会导致很多内存安全问题,rust api需要重新设计.
§函数参数类型映射规则.
c++参数类型 | rust参数类型 |
|---|---|
| T | T |
T&& | T |
std::unique_ptr<T> | T |
std::unique_ptr<T, D> | hicc::unique_ptr<T> |
std::shared_ptr<T, D> | hicc::shared_ptr<T> |
const T& | &T |
T& | &mut T |
const T* | &hicc::ClassPtr<'_, T, 1> |
T* | &hicc::ClassMutPtr<'_, T, 1> |
注意hicc::ClassPtr<'_, T, N = 1>支持多重指针的,所有的指针映射规则都相同.
§模板(泛型)参数类型映射规则
hicc支持映射c++模板类,模版类型参数可能是c++类型或者是POD类型.
同样hicc::import_class和hicc::import_lib可以自动转换和c++交互函数的参数和返回类型.
如果需要为模版类添加rust方法时,应该显式使用映射后的类型.
对于模板参数,类型映射规则如下:
| c++模板参数类型 | rust参数 | rust返回值 |
|---|---|---|
| T | <T as AbiType>::InputType | <T as AbiType>::OutputType |
| T&& | <T as AbiType>::InputType | <T as AbiType>::OutputType |
| const T& | &<T as AbiType>::InputType | <T as AbiType>::OutputRef<'_> |
| T& | &mut <T as AbiType>::InputType | <T as AbiType>::OutputRefMut<'_> |
| const T* | <T as AbiType>::InputPtr<'_> | <T as AbiType>::OutputPtr<'_> |
| T* | <T as AbiType>::InputMutPtr<'_> | <T as AbiType>::OutputMutPtr<'_> |
参考hicc-std::vector的get接口定义:
impl<T: AbiType + 'static> vector<T> {
pub fn get(&self) -> Option<T::OutputRef<'_>> {
//...
}
}非class类型用于rust泛型,必须结合hicc::Pod<T>使用.
参考hicc-std::basic_string<T>的相关定义:
type string = basic_string<hicc::Pod<i8>>;
type u16string = basic_string<hicc::Pod<i16>>;
type u32string = basic_string<hicc::Pod<i32>>;§使用样例
§hello_world
hicc::cpp可嵌入c++代码, hicc::import_lib定义需要调用的c++ api.
hicc::cpp! {
#include <iostream>
static void hello_world() {
std::cout << "hello world!" << std::endl;
}
}
hicc::import_lib! {
#![link_name = "example"]
#[cpp(func = "void hello_world()")]
fn hello_world();
}
fn main() {
hello_world();
}build.rs利用hicc_buid::Build编译此文件.
hicc_build::Build::new().rust_file("src/main.rs").compile("example");
println!("cargo::rustc-link-lib=example");
println!("cargo::rustc-link-lib=stdc++");
println!("cargo::return-if-changed=src/main.rs"); §缺省参数和忽略返回值
如下场景,对于非变长参数的c++函数,允许映射不同的rust函数类型:
rust函数参数可以不包括c++函数的缺省参数rust函数可以忽略c++函数的返回值.rust函数可以将返回值包装在hicc::Exception中捕获c++异常.
hicc::cpp! {
#include <iostream>
static int foo(int v1, int v2 = 0) {
std::cout << "foo(v1 = " << v1 << ", v2 = " << v2 << ")" << std::endl;
throw 3;
return v1 + v2;
}
}
hicc::import_lib! {
#![link_name = "example"]
#[cpp(func = "int foo(int, int)")]
fn foo(v: i32) -> hicc::Exception<()>;
}
fn main() {
println!("{:?}", foo(1).ok());
}§stl example
hicc::import_class中的关键字class将c++的类映射为rust的struct. 无函数体的方法利用#[cpp(method = ...)]映射为对应的c++类的成员函数.
use std::ffi::CStr;
hicc::cpp! {
#include <string>
static std::string hello_world() {
return "hello_world";
}
}
hicc::import_class! {
#[cpp(class = "std::string")]
class string {
#[cpp(method = "const char* c_str() const")]
fn c_str(&self) -> *const i8;
fn as_cstr(&self) -> &CStr {
unsafe { CStr::from_ptr(self.c_str()) }
}
}
}
hicc::import_lib! {
#![link_name = "example"]
class string;
#[cpp(func = "std::string hello_world()")]
fn hello_world() -> string;
}
fn main() {
let hello = hello_world();
println!("{:?}", hello.as_cstr());
}
§hicc-std使用样例.
注: hicc-std提供了对stl容器的完整支持.
业务依赖std::map<int, std::string>, 需要提供实例化类的构建函数即可.
use hicc::AbiClass;
hicc::cpp! {
// c++侧需要引用hicc提供的头文件.
#include <hicc/std/map.hpp>
#include <hicc/std/string.hpp>
// 按需定义容器类型. 可以包含非缺省的Allocator等模版参数类型.
typedef std::map<int, std::string> CppMap;
}
hicc::import_lib! {
#![link_name = "example"]
// 对应`c++`的`CppMap`
class RustMap = hicc_std::map<hicc::Pod<i32>, hicc_std::string>;
// 创建容器接口.
#[cpp(func = "std::unique_ptr<CppMap> hicc::make_unique<CppMap>()")]
fn rustmap_new() -> RustMap;
}
fn main() {
let mut map = rustmap_new();
let name = hicc_std::string::from(c"hello");
map.insert(&0, &name);
assert_eq!(map.get(&1), None);
assert_eq!(map.get(&0), Some(name.as_ref()));
}§继承c++抽象类
同样利用hicc::import_class实现c++抽象类的映射(当前不支持多继承).
#[interface]声明这是一个纯抽象类,最终会映射为rust的trait.
利用内置函数@make_proxy和宏#[interface(name = ...)], 基于组合模式,提供Rust继承c++抽象类的相似效果.
对于需要利用@make_proxy创建的类,必须在定义时提供c++构造函数的定义,下面例子中的:
#[cpp(class = "Baz", ctor = "Baz()")]
class Baz: Bar {
#[cpp(method = "void baz() const")]
fn baz(&self);
}完整样例:
hicc::cpp! {
#include <hicc/std/memory.hpp>
#include <iostream>
struct Foo {
virtual ~Foo() {};
virtual void foo() const = 0;
};
struct Bar: public Foo {
virtual void bar() const = 0;
};
struct Baz: public Bar {
virtual void foo() const override {
std::cout << "C++ Baz::foo" << std::endl;
}
virtual void bar() const override {
std::cout << "C++ Baz::bar" << std::endl;
}
void baz() const {
std::cout << "C++ Baz::baz" << std::endl;
}
~Baz() {
std::cout << "C++ Baz::~Baz" << std::endl;
}
};
}
hicc::import_class! {
#[interface]
class Foo {
#[cpp(method = "void foo() const")]
fn foo(&self);
}
#[interface]
class Bar: Foo {
#[cpp(method = "void bar() const")]
fn bar(&self);
}
#[cpp(class = "Baz", ctor = "Baz()")]
class Baz: Bar {
#[cpp(method = "void baz() const")]
fn baz(&self);
}
}
hicc::import_lib! {
#![link_name = "example"]
class Baz;
#[cpp(func = "Baz @make_proxy<Baz>()")]
#[interface(name = "Bar")]
fn new_rust_baz(intf: hicc::Interface<Baz>) -> Baz;
#[cpp(func = "std::unique_ptr<Baz> std::make_unique<Baz>()")]
fn new_cpp_baz() -> Baz;
}
struct RustBaz;
impl Bar for RustBaz {
fn bar(&self) {
println!("Rust Baz::bar");
}
}
impl Foo for RustBaz {
fn foo(&self) {
println!("Rust Baz::foo");
}
}
impl Drop for RustBaz {
fn drop(&mut self) {
println!("Rust Baz::~Baz");
}
}
fn main() {
let cpp_baz = new_cpp_baz();
cpp_baz.foo();
cpp_baz.bar();
cpp_baz.baz();
let rust_baz = new_rust_baz(RustBaz);
rust_baz.foo();
rust_baz.bar();
rust_baz.baz();
}Macros§
- cpp
- 支持嵌入任意
c++代码,一般用于包含必要的c++头文件. - import_
class - 提供映射
c++类的功能. - import_
lib - 提供映射
c++全局函数的功能.
Structs§
- Class
Array - 对应
c++类对象只读数组 - Class
MutArray - 对应
c++类对象可写数组 - Class
MutPtr - 对应
T*/T**/... - Class
Ptr - 对应
const T*/const T**/... - Class
Ref - 对应
&T. - Class
RefMut - 对应
&mut T. - Exception
- 对应
c++接口抛出的异常 - Exception
Info - 保存
c++异常信息 - Function
- 对应
c++的std::function<R(...)>类型. - Interface
- 类型标识,用于需要从
rust的trait创建c++类对象的场景. - Pod
c++模板参数, 非c++类必须包装在pod<T>中使用.- RustAny
c++容器需要保存rust的数据时,rust的数据类型需要转换为RustAny保存.- Rust
Hash Key - RustKey
- shared_
ptr - 对应
std::shared_ptr<T> - unique_
ptr - 对应
std::unique_ptr<T, D> - weak_
ptr - 对应
std::weak_ptr<T>.
Enums§
Traits§
- AbiClass
- 遵循
RAII原则管理c++资源的生命周期. - AbiType
- 用于支持
c++模板类. 对应rust侧泛型参数必须支持AbiType. - Function
Type - 辅助
Function的实现. - Import
Lib - 辅助
import_lib宏的实现.