我有一个类层次结构:
class A { } //
class AA : A { } // A
class AAA : AA { } // / \
class AAB : AA { } // AA AB
class AB : A { } // / \ / \
class ABA : AB { } // AAA AAB ABA ABB
class ABB : AB { } //
我想为这个ierarchy模拟RTTI(当然不使用它),在给定指向A
的指针的情况下,我可以找出它的实际类型(类似于什么typeid
确实),作为标识类的整数。
此外,我希望识别我的类型的整数集合是连续的,从0到N-1(在我的例子中从0到6):
class A { virtual int t(){return 0;} } //
class AA : A { virtual int t(){return 1;} } // A(0)
class AAA : AA { virtual int t(){return 2;} } // / \
class AAB : AA { virtual int t(){return 3;} } // AA(1) AB(4)
class AB : A { virtual int t(){return 4;} } // / \ / \
class ABA : AB { virtual int t(){return 5;} } // AAA(2) AAB(3) ABA(5) ABB(6)
class ABB : AB { virtual int t(){return 6;} } //
(顺序并不重要:例如A::t
可以返回3和AAB::t
0。
是否可以让编译器将索引分配给我的类?
我认为CRTP可以帮助我;类似的东西:
class X : A, AssignFirstAvailableIndex< X > { }
但我对模板不够好。我该如何实现AssignFirstAvailableIndex
模板类?
(当然编译器可以在编译时看到所有类)
答案 0 :(得分:3)
有一种标准方法可以实现您的需求。标准区域设置方面使用它来标识自己。考虑检查标准标题“locale”。
class Base {
public:
// Being constructed contains a new unique identifier
class Id {
// An id factory returns a sequence of nonnegative ints
static int allocate() {
static int total = 0;
return total++;
}
int _local;
public:
Id(): _local(allocate()) {}
int get() const {return _local;}
};
//Child classes should make this function return an id generated by Base::Id constructed as static member.
virtual int id() const = 0;
};
class Child1{
public:
static const Base::Id _id;
virtual int id() { return _id.get(); }
};
class Child2 {
public:
static const Base::Id _id;
virtual int id() { return _id.get(); }
};
class Child3 {
public:
static const Base::Id _id;
virtual int id() { return _id.get(); }
};
静态成员可能在实现文件中初始化,或者被模板化以允许直接从头部进行实例化或被重构以进行延迟初始化。
答案 1 :(得分:0)
也许是这样的? (语法可能在整个地方都是错误的,但你明白了)
class A {
virtual int t() { return 0; }
}
template <bool AB>
class AX<AB>: A {
virtual int t() { return AB ? 1 : 2; }
};
template <bool AB2>
template <bool AB>
class AXX<AB2>: AX<AB> {
virtual int t() { return AX<AB>::t() * 2 + (AB2 ? 1 : 2); }
}
......但严重的是,请只使用RTTI。
答案 2 :(得分:0)
我不认为有任何方法可以在编译时生成索引,除非使用enum
(我认为这是一种非常合理的方法)。我不确定模板是否有用,因为模板纯粹是功能性的,并且没有任何地方存储任何全局状态(即当前索引)除了模板化类型本身的名称(这正是你正在尝试的)避免)。
如果你真的想要整数ID,那么在运行时设置它们可能最容易,而不是太努力。
首先,有一个表示类型的对象,并使用典型的手工制作的RTTI方法:让每个类都有该对象的静态实例,并让其虚拟的get-type-info函数返回指向该对象的指针宾语。所以每个类都会有一些代码,如下所示:
static TypeInfo ms_type_info;
virtual const TypeInfo *GetTypeInfo() const {
return &ms_type_info;
}
你定义了类型信息,将<<whatever you info you want>>
存储的任何信息放入TypeInfo
部分,使其比编译器的RTTI更好;
TypeInfo WhateverClass::ms_type_info(<<whatever info you want>>);
(我见过的每个实现都使用一个宏来自动创建这两个文本;有点难看,但你的选择有限,而且比输入它更好。)
TypeInfo
结构本身看起来有点像这样:
struct TypeInfo {
int type_index;
TypeInfo *next;
TypeInfo(<<whatever>>) {<<see below>>}
};
如果读者更喜欢获取和设置功能,那么可能会有这些功能。
TypeInfo
对象对于类而不是函数应该是静态的,因为目的是创建所有TypeInfos
的列表。你有两个选择。自动的是让每个TypeInfo
在上面留空的构造函数中,将自己添加到某种全局链表中;另一个是拥有一个大功能,可以手动将它想要的功能添加到全局列表中。
然后,在启动时,运行TypeInfo
个对象并为每个对象分配一个索引。假设有一个TypeInfo *g_first_type_info
指向列表中的第一个类型信息,那么这样的事情会发生:
int next_type_index=0;
for(TypeInfo *ti=g_first_type_info;ti;ti=ti->next)
ti->type_index=next_type_index++;
现在,当您需要整数ID时,可以轻松检索它:
object->GetTypeInfo()->type_index;
您现在可以轻松实现t
:
virtual int t() const {
return ms_type_info.type_index;
}
充分披露我能想到的问题:
类型索引不是在编译时设置的,因此如果构建数组,则需要在运行时构建它们。如果您的数组本身就是编译时常量,这可能会浪费代码 - 但对于不支持C99样式数组初始化语法的编译器,这有时会使代码更具可读性。
如果你想在main
开始之前在全局构造函数中做所有事情,你可能会失败,因为TypeInfo
对象只是普通的全局对象而且(在这种情况下无论如何)都不是' t无论如何都要准备好使用,直到分配了类型索引。
链接器倾向于去除似乎没有被使用的全局对象,因此静态库中的类型信息对象可能永远不会将自己添加到列表中。因此,您应该记住,虽然自动注册方法运行良好,但当它不起作用时,它不会,所以您可能最终必须手动注册事项。
答案 3 :(得分:0)
我已成功使用此处描述的方法: http://www.flipcode.com/archives/Run_Time_Type_Information.shtml。基本上每个需要RTTI的类都有一个返回其rtti类型的静态函数。使用类型结构的优点是可以向rtti结构添加函数。
我修改了这种方法以允许component->IsOfType(CollisionComponent::GetClass())
等内容。
我还扩展了RTTI结构以提供类名,现在我可以调用component->IsOfType("CollisionComponent")
而不包括CollisionComponent.h。
与this动态类创建结合使用时,此方法非常方便。我能够在脚本中创建和识别C ++类,并实例化大量不同的组件,这些组件仅在OS /硬件支持所需类型时加载。我还可以根据服务器版本重新配置加载的组件类型。例如,服务器可以允许使用"CollisionComponent_V2_0"
,而另一个服务器可以强制使用"CollisionComponent_Proxy_V2"