Crate hicc

Crate hicc 

Source
Expand description

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

三个相关的库:

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

**已知约束:

  1. 依赖c++11或更高版本的特性.**
  2. 不直接支持操作符重载函数,需要利用hicc::cpp封装

§类型映射通用规则

使用者需要了解映射规则, 特别是c++类作为函数返回值时映射后的类型.

hicc::import_classhicc::import_lib宏会基于映射规则更改函数的参数和返回值类型,新类型支持自动解引用.

在自动解引用无法支持的场景需要显式调用相关接口实现类型转换.

§函数返回类型映射规则.

c++返回类类型rust对应类型
TT
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>

注意:

  1. 字节对齐要求: 要求所有c++对象都至少按照size_t字节数进行对齐, 未对齐地址被视为非法指针.
  2. hicc::ClassPtr<'_, T, N = 1>支持多重指针的,所有的指针映射规则都相同, 作为返回值时,其生命周期参数为'static.
  3. 所有c++类映射类型T都支持hicc::AbiClass trait.
  4. 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参数类型
TT
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_classhicc::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::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>>;

§使用样例

§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函数类型:

  1. rust函数参数可以不包括c++函数的缺省参数
  2. rust函数可以忽略c++函数的返回值.
  3. 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中的关键字classc++的类映射为ruststruct. 无函数体的方法利用#[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]声明这是一个纯抽象类,最终会映射为rusttrait.

利用内置函数@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§

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>中使用.
RustAny
c++容器需要保存rust的数据时,rust的数据类型需要转换为RustAny保存.
RustHashKey
RustKey
shared_ptr
对应std::shared_ptr<T>
unique_ptr
对应std::unique_ptr<T, D>
weak_ptr
对应std::weak_ptr<T>.

Enums§

RustPtr

Traits§

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