Как реализована LLVM <a>? - PullRequest
23 голосов
/ 18 мая 2011

С http://llvm.org/docs/CodingStandards.html#ci_rtti_exceptions

LLVM широко использует ручную форму RTTI, в которой используются такие шаблоны, как isa <>, cast <> и dyn_cast <>.Эта форма RTTI является опциональной и может быть добавлена ​​к любому классу.Это также существенно более эффективно, чем dynamic_cast <>.

Как реализованы isa и другие?

Ответы [ 3 ]

32 голосов
/ 20 мая 2011

Прежде всего, система 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, и проверка использует перечисление для определения соответствия конкретного типа.

Надеюсь, это будет понятнее.

6 голосов
/ 18 мая 2011

Просто добавьте материал в ответ osgx: в основном, каждый класс должен реализовывать метод classof (), который делает все необходимые вещи.Например, процедура класса classof () выглядит следующим образом:

  // Methods for support type inquiry through isa, cast, and dyn_cast:
  static inline bool classof(const Value *) {
    return true; // Values are always values.
  }

Чтобы проверить, есть ли у нас класс соответствующего типа, у каждого класса есть свой уникальный ValueID.Вы можете проверить полный список значений ValueID внутри файла include / llvm / Value.h.Этот ValueID используется следующим образом (выдержка из Function.h):

  /// Methods for support type inquiry through isa, cast, and dyn_cast:
  static inline bool classof(const Function *) { return true; }
  static inline bool classof(const Value *V) {
    return V->getValueID() == Value::FunctionVal;
  }

Итак, вкратце: каждый класс должен реализовывать метод classof (), который выполняет необходимое решение.Рассматриваемая реализация состоит из набора уникальных ValueID.Таким образом, чтобы реализовать classof (), нужно просто сравнить ValueID аргумента с собственным ValueID.

Если я правильно помню, первая реализация isa <> и друзей были приняты из boost ~ 10 лет назад.Прямо сейчас реализации значительно расходятся:)

5 голосов
/ 18 мая 2011

Следует отметить, что http://llvm.org/docs/ProgrammersManual.html#isa - этот документ имеет дополнительное описание.

Исходный код isa, cast и dyn_cast находится в одном файле и много комментируется.

http://llvm.org/doxygen/Casting_8h_source.html

00047 // isa<X> - Return true if the parameter to the template is an instance of the
00048 // template type argument.  Used like this:
00049 //
00050 //  if (isa<Type*>(myVal)) { ... }
00051 //
00052 template <typename To, typename From>
00053 struct isa_impl {
00054   static inline bool doit(const From &Val) {
00055     return To::classof(&Val);
00056   }
00057 };

00193 // cast<X> - Return the argument parameter cast to the specified type.  This
00194 // casting operator asserts that the type is correct, so it does not return null
00195 // on failure.  It does not allow a null argument (use cast_or_null for that).
00196 // It is typically used like this:
00197 //
00198 //  cast<Instruction>(myVal)->getParent()
00199 //
00200 template <class X, class Y>
00201 inline typename cast_retty<X, Y>::ret_type cast(const Y &Val) {
00202   assert(isa<X>(Val) && "cast<Ty>() argument of incompatible type!");
00203   return cast_convert_val<X, Y,
00204                           typename simplify_type<Y>::SimpleType>::doit(Val);
00205 }

00218 // dyn_cast<X> - Return the argument parameter cast to the specified type.  This
00219 // casting operator returns null if the argument is of the wrong type, so it can
00220 // be used to test for a type as well as cast if successful.  This should be
00221 // used in the context of an if statement like this:
00222 //
00223 //  if (const Instruction *I = dyn_cast<Instruction>(myVal)) { ... }
00224 //
00225 
00226 template <class X, class Y>
00227 inline typename cast_retty<X, Y>::ret_type dyn_cast(const Y &Val) {
00228   return isa<X>(Val) ? cast<X, Y>(Val) : 0;
00229 }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...