Прежде всего, система LLVM чрезвычайно специфична и не является заменой системы RTTI.
Помещения
- Для большинства классов нет необходимости генерировать информацию RTTI
- Когда это требуется, информация имеет смысл только в пределах данной иерархии
- Мы исключаем мульти-наследование из этой системы
Идентификация класса объекта
Возьмем простую иерархию, например:
struct Base {}; /* abstract */
struct DerivedLeft: Base {}; /* abstract */
struct DerivedRight:Base {};
struct MostDerivedL1: DerivedLeft {};
struct MostDerivedL2: DerivedLeft {};
struct MostDerivedR: DerivedRight {};
Мы создадим перечисление , специфичное для этой иерархии , с элементом перечисления для каждого члена иерархии, который может быть создан (остальные будут бесполезны).
enum BaseId {
DerivedRightId,
MostDerivedL1Id,
MostDerivedL2Id,
MostDerivedRId
};
Затем класс Base
будет дополнен методом, который будет возвращать это перечисление.
struct Base {
static inline bool classof(Base const*) { return true; }
Base(BaseId id): Id(id) {}
BaseId getValueID() const { return Id; }
BaseId Id;
};
И каждый конкретный класс также расширяется таким образом:
struct DerivedRight: Base {
static inline bool classof(DerivedRight const*) { return true; }
static inline bool classof(Base const* B) {
switch(B->getValueID()) {
case DerivedRightId: case MostDerivedRId: return true;
default: return false;
}
}
DerivedRight(BaseId id = DerivedRightId): Base(id) {}
};
Теперь можно просто запросить точный тип для приведения.
Сокрытие деталей реализации
Иметь пользователей, убивающих с помощью getValueID
, было бы проблематично, поэтому в LLVM это скрыто с использованием classof
методов.
Данный класс должен реализовывать два метода classof
: один для его самой глубокой базы (с проверкой подходящих значений BaseId
) и один для себя (чистая оптимизация). Например:
struct MostDerivedL1: DerivedLeft {
static inline bool classof(MostDerivedL1 const*) { return true; }
static inline bool classof(Base const* B) {
return B->getValueID() == MostDerivedL1Id;
}
MostDerivedL1(): DerivedLeft(MostDerivedL1Id) {}
};
Таким образом, мы можем проверить, возможно ли приведение с помощью шаблонов:
template <typename To, typename From>
bool isa(From const& f) {
return To::classof(&f);
}
Представьте на мгновение, что To
равно MostDerivedL1
:
- если
From
равно MostDerivedL1
, то мы вызываем первую перегрузку classof
, и она работает
- если
From
является чем-то другим, тогда мы вызываем вторую перегрузку classof
, и проверка использует перечисление для определения соответствия конкретного типа.
Надеюсь, это будет понятнее.