-.. _how-to-set-up-llvm-style-rtti:
-
======================================================
How to set up LLVM-style RTTI for your class hierarchy
======================================================
-.. sectionauthor:: Sean Silva <silvas@purdue.edu>
-
.. contents::
Background
double SideLength;
public:
Square(double S) : SideLength(S) {}
- double computeArea() /* override */;
+ double computeArea() override;
};
class Circle : public Shape {
double Radius;
public:
Circle(double R) : Radius(R) {}
- double computeArea() /* override */;
+ double computeArea() override;
};
The most basic working setup for LLVM-style RTTI requires the following
public:
- Square(double S) : SideLength(S) {}
+ Square(double S) : Shape(SK_Square), SideLength(S) {}
- double computeArea() /* override */;
+ double computeArea() override;
};
class Circle : public Shape {
public:
- Circle(double R) : Radius(R) {}
+ Circle(double R) : Shape(SK_Circle), Radius(R) {}
- double computeArea() /* override */;
+ double computeArea() override;
};
#. Finally, you need to inform LLVM's RTTI templates how to dynamically
double SideLength;
public:
Square(double S) : Shape(SK_Square), SideLength(S) {}
- double computeArea() /* override */;
+ double computeArea() override;
+
+ static bool classof(const Shape *S) {
+ return S->getKind() == SK_Square;
double Radius;
public:
Circle(double R) : Shape(SK_Circle), Radius(R) {}
- double computeArea() /* override */;
+ double computeArea() override;
+
+ static bool classof(const Shape *S) {
+ return S->getKind() == SK_Circle;
[...]
template <class T>
static bool classof(const T *,
- ::llvm::enable_if_c<
- ::llvm::is_base_of<Foo, T>::value
+ ::std::enable_if<
+ ::std::is_base_of<Foo, T>::value
>::type* = 0) { return true; }
[...]
};
| OtherSpecialSquare
| Circle
+A Bug to be Aware Of
+--------------------
+
+The example just given opens the door to bugs where the ``classof``\s are
+not updated to match the ``Kind`` enum when adding (or removing) classes to
+(from) the hierarchy.
+
+Continuing the example above, suppose we add a ``SomewhatSpecialSquare`` as
+a subclass of ``Square``, and update the ``ShapeKind`` enum like so:
+
+.. code-block:: c++
+
+ enum ShapeKind {
+ SK_Square,
+ SK_SpecialSquare,
+ SK_OtherSpecialSquare,
+ + SK_SomewhatSpecialSquare,
+ SK_Circle
+ }
+
+Now, suppose that we forget to update ``Square::classof()``, so it still
+looks like:
+
+.. code-block:: c++
+
+ static bool classof(const Shape *S) {
+ // BUG: Returns false when S->getKind() == SK_SomewhatSpecialSquare,
+ // even though SomewhatSpecialSquare "is a" Square.
+ return S->getKind() >= SK_Square &&
+ S->getKind() <= SK_OtherSpecialSquare;
+ }
+
+As the comment indicates, this code contains a bug. A straightforward and
+non-clever way to avoid this is to introduce an explicit ``SK_LastSquare``
+entry in the enum when adding the first subclass(es). For example, we could
+rewrite the example at the beginning of `Concrete Bases and Deeper
+Hierarchies`_ as:
+
+.. code-block:: c++
+
+ enum ShapeKind {
+ SK_Square,
+ + SK_SpecialSquare,
+ + SK_OtherSpecialSquare,
+ + SK_LastSquare,
+ SK_Circle
+ }
+ ...
+ // Square::classof()
+ - static bool classof(const Shape *S) {
+ - return S->getKind() == SK_Square;
+ - }
+ + static bool classof(const Shape *S) {
+ + return S->getKind() >= SK_Square &&
+ + S->getKind() <= SK_LastSquare;
+ + }
+
+Then, adding new subclasses is easy:
+
+.. code-block:: c++
+
+ enum ShapeKind {
+ SK_Square,
+ SK_SpecialSquare,
+ SK_OtherSpecialSquare,
+ + SK_SomewhatSpecialSquare,
+ SK_LastSquare,
+ SK_Circle
+ }
+
+Notice that ``Square::classof`` does not need to be changed.
+
.. _classof-contract:
The Contract of ``classof``
argument is-a ``C``". As long as your implementation fulfills this
contract, you can tweak and optimize it as much as you want.
+For example, LLVM-style RTTI can work fine in the presence of
+multiple-inheritance by defining an appropriate ``classof``.
+An example of this in practice is
+`Decl <http://clang.llvm.org/doxygen/classclang_1_1Decl.html>`_ vs.
+`DeclContext <http://clang.llvm.org/doxygen/classclang_1_1DeclContext.html>`_
+inside Clang.
+The ``Decl`` hierarchy is done very similarly to the example setup
+demonstrated in this tutorial.
+The key part is how to then incorporate ``DeclContext``: all that is needed
+is in ``bool DeclContext::classof(const Decl *)``, which asks the question
+"Given a ``Decl``, how can I determine if it is-a ``DeclContext``?".
+It answers this with a simple switch over the set of ``Decl`` "kinds", and
+returning true for ones that are known to be ``DeclContext``'s.
+
.. TODO::
Touch on some of the more advanced features, like ``isa_impl`` and