在 Rust 中模拟 C++ 类的功能 类型信息及向下转换第二
在上一节中我们手工实现了虚函数表,完成了继承、重写以及向上转换的操作,接下来我们要实现向下转换,它的实现大概是下面的样子:
pub fn dynamic_cast<'a, B, D>(base: &'a B) -> Option<&'a D>
{
if can_dynamic_cast_to::<B, D>(base)
{
Some(unsafe { reinterpret_cast(base) })
}
else
{
None
}
}
这里我们使用了上一节实现的 reinterpret_cast 函数来进行类型的转换,实际上 reinterpret_cast 可以转换任何类型,而不管转换是否安全,因此才会被标记为 unsafe,上一节我们用它做向上转换,是因为我们可以确保转换是安全的,但在这里就要先行检测安全再进行转换。
为了实现 can_dynamic_cast_to 函数,我们需要一些额外的信息,也就是运行时类型识别,我们先简单实现一个:
pub struct TypeInfo
{
base_class: Option<&'static TypeInfo>
}
impl TypeInfo
{
fn is_same(&self, other: &TypeInfo) -> bool
{
self as *const TypeInfo == other as *const TypeInfo
}
fn is_base_of(&self, other: &TypeInfo) -> bool
{
let mut ret = self.is_same(other);
if !ret
{
if let Some(other) = other.base_class
{
ret = self.is_base_of(other);
}
}
ret
}
}
这个类型信息简单了点,可以从当前类一直向上查找到基类为止,别看它小,但用于我们实现 can_dynamic_cast_to 足够了。
接下来要为每个类添加类型信息,如下:
pub struct BaseVTable
{
_type_info_: &'static TypeInfo,
...
}
impl Base
{
pub const TYPEINFO: TypeInfo = TypeInfo
{
base_class: None,
};
pub const VTABLE: BaseVTable = BaseVTable
{
_type_info_: &Self::TYPEINFO,
...
};
}
pub struct Derive1VTable
{
_type_info_: &'static TypeInfo,
...
}
impl Derive1
{
pub const TYPEINFO: TypeInfo = TypeInfo
{
base_class: Some(&Base::TYPEINFO),
};
pub const VTABLE: Derive1VTable = Derive1VTable
{
_type_info_: &Self::TYPEINFO,
...
};
}
pub struct Derive2VTable
{
_type_info_: &'static TypeInfo,
...
}
impl Derive2
{
pub const TYPEINFO: TypeInfo = TypeInfo
{
base_class: Some(&Derive1::TYPEINFO),
};
pub const VTABLE: Derive2VTable = Derive2VTable
{
_type_info_: &Self::TYPEINFO,
...
};
}
现在我们可以实现 can_dynamic_cast_to 了,如下:
fn can_dynamic_cast_to<B, D>(base: &B) -> bool
{
let typeinfo: &TypeInfo =
{
let p = base as *const B;
let p = p as *const *const *const TypeInfo;
unsafe { &***p }
};
&D::TYPEINFO.is_base_of(typeinfo)
}
根据我们实现的类的内存布局,无论是任何类,第一级指针,指向对象本身,这是毋庸置疑的;因为 vptr 是类的第一个成员,所以第二级指针都指向虚表;而虚表中第一个元素是指向类型信息的指针,所以第三级指针指向类型信息。因此我们将对象的引用转换为三级的类型信息指针,从而获取到对象的实际类型信息。
接下来我们通过 D::TYPEINFO 来获取要转换到的类型的类型信息,从而判断是否可以进行转换。但是这里存在两个问题:
如果用户传递给我们的 base 参数不是一个我们实现的类,甚至是一个 i32 或者 &str,怎么办?我们知道这样做是不合法的,却无法阻止这样的事情发生,甚至我们连安全性都不能保证。如下:
if let Some(_) = dynamic_cast<i32, i64>(22) ... if let Some(_) = dynamic_cast<&str, Derive2>("abcd") ...
- D::TYPEINFO 无法通过编译,Rust 的模板要求在展开前进行语法检查,此刻 Rust 还不知道 D 的定义,这和 C++ 的模板不一样。
幸运的是,两个问题可以用一个方法解决,我们可以定义一个 trait TypeInfoTrait,并且我们的类都要求实现 TypeInfoTrait,如下:
pub unsafe trait TypeInfoTrait
{
fn get_typeinfo() -> &'static TypeInfo;
}
unsafe impl TypeInfoTrait for Base
{
fn get_typeinfo() -> &'static TypeInfo { &Self::TYPEINFO }
}
unsafe impl TypeInfoTrait for Derive1
{
fn get_typeinfo() -> &'static TypeInfo { &Self::TYPEINFO }
}
unsafe impl TypeInfoTrait for Derive2
{
fn get_typeinfo() -> &'static TypeInfo { &Self::TYPEINFO }
}
如此一来,我们可以要求模板参数 B、D 都实现 TypeInfoTrait,缩小了 dynamic_cast 方法的适用范围,在一定程度上保障了安全,但我们无法阻止用户自行实现 TypeInfoTrait,所以我们将它标记为 unsafe。
fn can_dynamic_cast_to<B, D>(base: &B) -> bool
where
B: TypeInfoTrait,
D: TypeInfoTrait,
{
let typeinfo: &TypeInfo =
{
let p = base as *const B;
let p = p as *const *const *const TypeInfo;
unsafe { &***p }
};
&D::get_typeinfo().is_base_of(typeinfo)
}
pub fn dynamic_cast<'a, B, D>(base: &'a B) -> Option<&'a D>
where
B: TypeInfoTrait,
D: TypeInfoTrait,
...
pub fn dynamic_cast_mut<'a, B, D>(base: &'a mut B) -> Option<&'a mut D>
where
B: TypeInfoTrait,
D: TypeInfoTrait,
...
dynamic_cast 实现完成,我们来验证一下:
use crate::dynamic_cast;
fn func3(base: &super::Base) -> (i32, i32, i32)
{
let z = if let Some(d1) = dynamic_cast::<super::Base, super::Derive1>(base)
{ d1.func3() } else { -1 };
(base.func1(), base.func2(100), z)
}
#[test]
fn test_fn2()
{
let b = super::Base::new(1, 2);
assert_eq!((1, 102, -1), func3(&b));
let d1 = super::Derive1::new(1, 2, 3);
assert_eq!((3, 102, 3), func3(&d1));
let d2 = super::Derive2::new(1, 2, 3);
assert_eq!((3, 302, 203), func3(&d2));
}
自此,我们手工实现 C++ 类的功能已经验证完成,接下来我们要开始用宏来生成这些代码。
《单亲宝贝》剧情片高清在线免费观看:https://www.jgz518.com/xingkong/122489.html
《总裁小叔的心尖宠》短片剧高清在线免费观看:https://www.jgz518.com/xingkong/13481.html
《旁观者粤语》韩国剧高清在线免费观看:https://www.jgz518.com/xingkong/29957.html
《少年骇客:全面进化第二季》欧美动漫高清在线免费观看:https://www.jgz518.com/xingkong/51321.html
哈哈哈,写的太好了https://www.lawjida.com/