Crate hicc

Source
Expand description

hicc提供调用c++接口的能力.

三个相关的库:

  1. hicc核心功能库.
  2. hicc-stdc++标准库中的容器提供rust api.
  3. hicc-build方便编译c++适配代码.

约束: 依赖c++11或更高版本的特性.

§类型映射通用规则

所有数据类型可以分为两大类: PODclass.

c++类型rust参数(POD)rust参数(class)rust返回值(POD)rus返回值(class)说明
TTTTT
ClassRef<'_, T>生命周期和输入关联
ClassRefMut<'_, T>生命周期和输入关联
ClassPtr<'_, T>生命周期和输入关联
ClassMutPtr<'_, T>生命周期和输入关联
T&&TTTT
ClassRef<'_, T>生命周期和输入关联
ClassRefMut<'_, T>生命周期和输入关联
ClassPtr<'_, T>生命周期和输入关联
ClassMutPtr<'_, T>生命周期和输入关联
std::unique_ptr<T>unique_ptr<T>Tunique_ptr<T>T
ClassRef<'_, T>生命周期和输入关联
ClassRefMut<'_, T>生命周期和输入关联
ClassPtr<'_, T>生命周期和输入关联
ClassMutPtr<'_, T>生命周期和输入关联
std::unique_ptr<T, D>unique_ptr<T>unique_ptr<T>unique_ptr<T>unique_ptr<T>D非缺省模板参数
ClassRef<'_, unique_ptr<T>>生命周期和输入关联
ClassRefMut<'_, unique_ptr<T>>生命周期和输入关联
ClassPtr<'_, unique_ptr<T>>生命周期和输入关联
ClassMutPtr<'_, unique_ptr<T>>生命周期和输入关联
std::shared_ptr<T>shared_ptr<T>shared_ptr<T>shared_ptr<T>shared_ptr<T>
ClassRef<'_, shared_ptr<T>>生命周期和输入关联
ClassRefMut<'_, shared_ptr<T>>生命周期和输入关联
ClassPtr<'_, shared_ptr<T>>生命周期和输入关联
ClassMutPtr<'_, shared_ptr<T>>生命周期和输入关联
const T&&T&T&TClassRef<'_, T>
T
ClassPtr<'_, T>
T&&mut T&mut T&mut TClassRefMut<'_, T>
T
ClassMutPtr<'_, T>
const T**const T&ClassPtr<'_, T>*const TClassPtr<'_, T>多重指针对应ClassPtr<'_, T, N>
&TT非多重指针
&TClassRef<’_, T>非多重指针
T**mut T&ClassMutPtr<'_, T>*mut TClassMutPtr<'_, T>多重指针对应ClassMutPtr<'_, T, N>
&mut TT非多重指针
&mut TClassRefMut<’_, T>非多重指针
std::function<R(ArgTypes...)>Function<fn(ArgTypes…)->R>Function<fn(ArgTypes…)->R>其中R/ArgTypes遵循上述映射规则
exception--Exception<T>Exception<T>其中T遵循上述映射规则
va_list...---

对于class类型有几个特点:

  1. 无论c++返回值类型是什么, rust可按需要选择值、引用或指针类型,作为返回值时他们的内存布局完全相同.
  2. 输入参数类型需要准确匹配,代表不同的生命周期. 值传递等同于所有权转移,其他为资源借用.
  3. T/ClassRef/ClassRefMut/ClassPtr/ClassMutPtr总是可以通过AbiClass::is_null判断返回是否为空指针.
  4. 接口定义时可按POD定义为T/&T/&mut T/*const T/*mut T, hicc完成类型自动转换.参见import_lib/import_class.

§模板(泛型)参数类型映射规则

hicc支持映射c++模板类,要利用AbiType来屏蔽上述PODclass两类模板参数类型上映射规则的差异.

对于模板参数,类型映射规则如下:

c++模板参数类型rust参数rust返回值
T<T as AbiType>::InputType<T as AbiType>::OutputType
const T&<T as AbiType>::InputRef<'_><T as AbiType>::OutputRef<'_>
T&<T as AbiType>::InputRefMut<'_><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::vectorget接口定义:

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>>;

§二次开发

二次开发分为三个步骤

  1. 转换为cabi: c++代码
  2. 映射为rust api: rust代码
  3. hicc-build编译c++代码

§hello world函数和类.

为如下helloHelloWorld实现提供rust api.

#include <string>
std::string hello(const std::string& name) {
    return std::string("hello ") + name;
}

class HelloWorld {
    std::string hello;
public:
    HelloWorld(const std::string& hello): hello(hello) {}
    std::string hi(const std::string& name) { return hello + " " + name; }
};

§转换为cabi

// src/cabi.cpp
#include <hicc/std/memory.hpp>
#include <hicc/std/string.hpp>

EXPORT_CLASS_DECLARE(HelloWorld, HelloWorldMethods);

// 将成员方法映射为`cabi`接口.
struct HelloWorldMethods {
    EXPORT_MEMBER_METHOD(&HelloWorld::hi);
};
 
// 将全局函数和构造函数映射为`cabi`接口.
EXPORT_METHODS_BEG(hello_1_0) {
    EXPORT_METHOD(hello);
    EXPORT_METHOD(hicc::make_unique<HelloWorld, const std::string&>);
}EXPORT_METHODS_END();

EXPORT_METHOD宏完成c++接口到cabi的映射. 具体参见import_lib/import_class.

§映射为rust api

// 映射HelloWorld
hicc::import_class! {
    class HelloWorld {
        fn hello(name: &hicc_std::string) -> hicc_std::string;
    }
}

// 映射构造函数
hicc::import_lib! {
    #![link_name = "hello_1_0"]

    fn hello(name: &hicc_std::string) -> hicc_std::string;
    // 推荐将全局构造函数设置为`crate::HelloWorld`的关联函数.
    #[member(class = HelloWorld, method = new)]
    fn helloworld_new(hello: &hicc_std::string) -> HelloWorld;
}

// 使用
fn main() {
    let hello = hello(&hicc_std::string::from(c"world"));
    println!("{:?}", hello.as_cstr());

    let hello = HelloWorld::new(&hicc_std::string::from(c"Hello"));
    let hi = hello.hi(&hicc_std::string::from(c"World"));
    println!("{:?}", hi.as_cstr());
}

import_lib/import_class中的函数声明将cabi接口导入到rust. 具体参见import_lib/import_class.

§编译c++代码

// build.rs
hicc_build::Build::new().file("src/cabi.cpp").compile("hello_1.0");
println!("cargo::rustc-link-lib=hello_1.0");
println!("cargo::rustc-link-lib=stdc++");

hicc_buildc++代码编译为静态库,将生成的库和stdc++链接到rust最终生成的二进制件.

§返回引用类型的处理

如果HelloWorld增加如下接口:

class HelloWorld {
public:
    // ...
    const std::string& get_hello() const { return hello; }
};

c++侧适配函数无特殊处理:

EXPORT_METHODS_BEG(hello_1_0) {
    //...
    EXPORT_MEMBER_METHOD(&HelloWorld::get_hello);
}EXPORT_METHODS_END();

根据类型映射规则,rust api返回值类型应该是ClassRef<'_, T>:

hicc::import_class! {
    class HelloWorld {
        //...
        fn get_hello(&self) -> hicc::ClassRef<'_, hicc_std::string>;
    }
}

推荐利用import_class完成类型映射:

hicc::import_class! {
    // 声明为`c++` class
    class String = hicc_std::string;
    class HelloWorld {
        //...
        // 根据映射规则,`&String`自动转换为`ClassRef<'_, String>`.
        fn get_hello(&self) -> &String;
    }
}

注: import_lib有同样的功能.

hicc::import_class! {
    // 重定义类型`hicc_std::string`为`String`
    class String = hicc_std::string;
    //...
}

hicc::import_lib! {
    // `String`已经被定义,这里只需要声明即可.
    class String;
    //...
}

说明: 最终生成的代码中, class关键字最终都对应到ruststruct关键字.

Macros§

import_class
实现c++ classrust struct的映射.
import_lib
提供rust调用c++函数,包括c++类构造函数的能力.

Structs§

ClassArray
对应c++类对象只读数组
ClassMutArray
对应c++类对象可写数组
ClassMutPtr
对应T*/T**/...
ClassPtr
对应const T*/const T**/...
ClassRef
对应&T.
ClassRefMut
对应&mut T.
Exception
对应c++接口抛出的异常
ExceptionInfo
保存c++异常信息
Function
对应c++std::function<R(...)>类型.
Interface
类型标识,用于需要从rust的trait创建c++类对象的场景.
Pod
c++模板参数, 非c++类必须包装在pod<T>中使用.
shared_ptr
对应std::shared_ptr<T>
unique_ptr
对应std::unique_ptr<T, D>
weak_ptr
对应std::weak_ptr<T>.

Traits§

AbiClass
遵循RAII原则管理c++资源的生命周期.
AbiType
用于支持c++模板类. 对应rust侧泛型参数必须支持AbiType.
FunctionType
辅助Function的实现.
ImportLib
辅助import_lib宏的实现.