当前位置: 首页 > news >正文

Nebula3 RTTI 小技巧

注意: 以下代码省略了命名空间前缀和”using namespace xxx”声明来改善可读性. 另外, 因为我没有通过编译器运行这些代码, 不保证有些手误.

不要把RttiRTTI搞混:

Rtti 是类名, MyClass::RTTI 是类的Rtti对象的名字. 每个RefCounted派生的类都有一个Core::Rtti的静态实例, 它在main()之前初始化.

检查一个对象是否为特定类或者其派生类:

这是Nebula3 RTTI系统的标准特性, 可以检查一个对象是否可以安全地转换为特定类接口:

// check whether obj is instance of a specific class: if (obj->IsInstanceOf(MyClass::RTTI)) … // check whether obj is instance of class, or a derived class: if (obj->IsA(MyClass::RTTI))…
Nebula2相比, N3RTTI检查是非常快的(N2, 这需要先把一个类名字符串转换成一个指针). N3, RTTI检查只是简单的指针比较. IsA()在类不匹配时可能会慢些, 因为它需要在继承树中遍历到根部. 这样一来, 最好使用IsInstanceOf()方法, 因为它只是一个指针比较.

尽管这两个方法都有类名和类四字符码(fourcc)的版本, 显然它们比直接使用RTTI对象要慢:

if (obj->IsInstanceOf(“MyNamespace::MyClass”)) … if (obj->IsInstanceOf(FourCC(‘MYCL’))… if (obj->IsA(“MyNamespace::MyClass”))… if (obj->IsA(FourCC(‘MYCL’))…

使用Ptr<> cast 方法来进行安全的转换:

Ptr<> 类有3个转换方法, 其中2个是安全的向上和向下转换, 还有一个不安全但快速的C风格的强制转换. 要进行向下转换(从一父类转换为特定子类)可以这么做:

// assume that res is a Ptr<Resource>, and safely down-cast // it to a Ptr<D3D9Texture> (D3D9Texture is a subclass of Resource): const Ptr<D3D9Texture>& d3d9Tex = res.downcast<D3D9Texture>();

如果tex不是D3D9Texture对象的话, 这会产生一个运行时错误.

安全的向上转换也差不多:

const Ptr<Resource>& res = d3d9Tex.upcast<Resource>();
一个不安全的C风格转换是这样的:

const Ptr<Resource>& res = d3d9Tex.cast<Resource>();

不安全的转换是最快的(release模式中, 编译器会把这个调用优化掉), 但是很显然它会让你自作自受. 那两个安全的转换方法会调用Rtti::IsDerivedFrom()方法, 而且不会有临时的Ptr<>对象产生, 因为返回的是一个 const引用.

直接查询RTTI 对象:

你可以在没有实际对象的情况下直接查询许多类属性:

// get the name of a class: const String& className = MyClass::RTTI.GetName(); // get the FourCC identifier of aclass: FourCC classFourCC = MyClass::RTTI.GetFourCC(); // get a pointer to the Rtti object of the parent class // (returns 0 when called on RefCounted::RTTI) Rtti* parentRtti = MyClass::RTTI.GetParent(); // check if a class is derived from this class: // by Rtti object: if (MyClass::RTTI.IsDerivedFrom(OtherClass::RTTI)) … // by class name: if (MyClass::RTTI.IsDerivedFrom(“MyNamespace::OtherClass”)) … // by class fourcc: if (MyClass::RTTI.IsDerivedFrom(FourCC(‘OTHR’))…
你可以检查两个Rtti对象是否相等:

const Rtti& otherRtti = …; if (MyClass::RTTI == otherRtti)… if (MyClass::RTTI != otherRtti)…

由于保证了每个类只有一个Rtti对象, 所以这个比较跟比较2Rtti对象的指针是等价的(事实上相等和不等运算符就是这么干的).

直接从RTTI对象创建实例:

Ptr<MyClass> myObj = (MyClass*) MyClass::RTTI.Create();

这个过时的C风格转换看起来格格不入, 但在这里是必须的, 因为Rtti::Create()方法只是一个普通指针, 而不是一个智能指针.

通过RTTI对象创建实例而不是MyClass::Create()静态方法对于把对象类型做为方法调用参数时非常有用:

Ptr<RefCounted> CreateObjectOfAnyClass(const Rtti& rtti) { return rtti.Create(); }

这比其它两种创建方法要快: 通过类名创建和类的四字符码(fourcc)创建.

通过类名或FourCC标识符创建对象

你可以用Core::Factory 单件以类名和FourCC标识符创建从RefCounted派生的对象:

Ptr<MyClass> obj = (MyClass*) Factory::Instance()->Create(“MyNamespace::MyClass”); Ptr<MyClass> obj = (MyClass*) Factory::Instance()->Create(FourCC(‘MYCL’));
这主要用于序列化代码, 或者对象类型需要经过网络连接进行转达.

Core::Factory单件查找类的RTTI对象

你可以通过类名或类的FourCC标识符来获得RTTI静态对象的指针:

const Rtti* rtti = Factory::Instance()->GetClassRtti(“MyNamespace::MyClass”); const Rtti* rtti = Factory::Instance()->GetClassRtti(FourCC(‘MYCL’));
如果类不存在, 调用会失败. 你可以用ClassExsits()方法检查类是否已经注册给工厂(factory):

bool classExists = Factory::Instance()->ClassExists(“MyNamespace::MyClass”); bool classExists = Factory::Instance()->ClassExists(FourCC(‘MYCL’));

疑难解答

通常在使用Nebula3RTTI系统时有两个问题.

当编写一个新类时, 可能会发生FourCC已经被占用的情况. 这时, 启动程序时会弹出一个这样的错误对话框:

修正冲突的办法是改变其中的一个FourCC代码并重新编译.

另一个问题就是有些类没有在程序启动时注册, 因为它的静态RTTI对象的构造方法被链接器(linker)优化没了”. 这通常是因为没有实际的C++代码直接用到这个类. 例如一个对象只通过类名(FourCC)进行创建并且只通过虚函数进行访问.

这种条件下, 链接器会完全地抛弃这个类的.obj单元, 因为外部没有调用到这个对象单元. 这对于减于可执行文件的体积很有效, 并且跟C++的静态对象模型工作得很好. 但是对于Nebula3的动态对象模型来说, 我们需要欺骗链接器来链接没有使用的类到可执行文件中去. 幸亏我们不需要为每个RefCounted派生的类做这件事, 只需要针对某些继承树中的类(如渲染层中的ModeNode的子类和ModeNodeInstance的子类, 还有应用程序层中的Property的子类)

为了防止链接器抛弃某个类, 建议进行下面的处理:

  1. 在一个.h文件中集中加入__RegisterClass(MyClass)
  2. .cc文件中包含这个头文件. 这样就可以保证不被链接器所抛弃了

头文件 /nebula3/code/render/render_classregistry.h 是一个进行集中类注册的典型例子.

相关文章:

  • azure 最佳实践 -- 保持冗余
  • Swift 3 打印功能
  • 还需要编写DAO代码吗?SQL+接口就足够了!(Part 1)
  • 百度关闭竞价排名部分功能,以提升用户体验
  • linux 更新nodejs到最新
  • 移动MM初探之一:苹果APP Store模式将不再赢利?
  • 七大热门商业智能产品(非开源)
  • azure 最佳实践 3--最小协同操作
  • linux-2.6.26内核中ARM中断实现详解(2)
  • azure 最佳实践4 --可水平扩展的设计
  • 开发即过程!立此纪念一个IT新名词的诞生
  • azure 最佳实践5--使用分区
  • asp.net Webapi登录azureAD并调用azure graph api
  • (翻译)terry crowley: 写给程序员
  • 推荐x61使用nhc软件控制风扇
  • 【跃迁之路】【735天】程序员高效学习方法论探索系列(实验阶段492-2019.2.25)...
  • Asm.js的简单介绍
  • bootstrap创建登录注册页面
  • Docker 1.12实践:Docker Service、Stack与分布式应用捆绑包
  • Druid 在有赞的实践
  • Fastjson的基本使用方法大全
  • Lucene解析 - 基本概念
  • Median of Two Sorted Arrays
  • MQ框架的比较
  • Netty源码解析1-Buffer
  • overflow: hidden IE7无效
  • Spring Security中异常上抛机制及对于转型处理的一些感悟
  • sublime配置文件
  • 阿里云爬虫风险管理产品商业化,为云端流量保驾护航
  • 读懂package.json -- 依赖管理
  • 入门级的git使用指北
  • 数据科学 第 3 章 11 字符串处理
  • 思否第一天
  • 我的zsh配置, 2019最新方案
  • 小程序开发中的那些坑
  • 学习JavaScript数据结构与算法 — 树
  • 智能合约开发环境搭建及Hello World合约
  • ​创新驱动,边缘计算领袖:亚马逊云科技海外服务器服务再进化
  • # Java NIO(一)FileChannel
  • # 日期待t_最值得等的SUV奥迪Q9:空间比MPV还大,或搭4.0T,香
  • #NOIP 2014#Day.2 T3 解方程
  • #ubuntu# #git# repository git config --global --add safe.directory
  • (1)(1.11) SiK Radio v2(一)
  • (3)(3.2) MAVLink2数据包签名(安全)
  • (Forward) Music Player: From UI Proposal to Code
  • (附源码)spring boot建达集团公司平台 毕业设计 141538
  • (七)MySQL是如何将LRU链表的使用性能优化到极致的?
  • (三)Hyperledger Fabric 1.1安装部署-chaincode测试
  • (提供数据集下载)基于大语言模型LangChain与ChatGLM3-6B本地知识库调优:数据集优化、参数调整、Prompt提示词优化实战
  • (五)网络优化与超参数选择--九五小庞
  • (原創) 人會胖會瘦,都是自我要求的結果 (日記)
  • (转)使用VMware vSphere标准交换机设置网络连接
  • (转载)在C#用WM_COPYDATA消息来实现两个进程之间传递数据
  • .NET/C# 避免调试器不小心提前计算本应延迟计算的值
  • .Net8 Blazor 尝鲜