You've already forked linux-packaging-mono
Imported Upstream version 5.18.0.205
Former-commit-id: 7f59f7e792705db773f1caecdaa823092f4e2927
This commit is contained in:
parent
5cd5df71cc
commit
8e12397d70
414
external/llvm/docs/HowToSetUpLLVMStyleRTTI.rst
vendored
Normal file
414
external/llvm/docs/HowToSetUpLLVMStyleRTTI.rst
vendored
Normal file
@ -0,0 +1,414 @@
|
||||
======================================================
|
||||
How to set up LLVM-style RTTI for your class hierarchy
|
||||
======================================================
|
||||
|
||||
.. contents::
|
||||
|
||||
Background
|
||||
==========
|
||||
|
||||
LLVM avoids using C++'s built in RTTI. Instead, it pervasively uses its
|
||||
own hand-rolled form of RTTI which is much more efficient and flexible,
|
||||
although it requires a bit more work from you as a class author.
|
||||
|
||||
A description of how to use LLVM-style RTTI from a client's perspective is
|
||||
given in the `Programmer's Manual <ProgrammersManual.html#isa>`_. This
|
||||
document, in contrast, discusses the steps you need to take as a class
|
||||
hierarchy author to make LLVM-style RTTI available to your clients.
|
||||
|
||||
Before diving in, make sure that you are familiar with the Object Oriented
|
||||
Programming concept of "`is-a`_".
|
||||
|
||||
.. _is-a: http://en.wikipedia.org/wiki/Is-a
|
||||
|
||||
Basic Setup
|
||||
===========
|
||||
|
||||
This section describes how to set up the most basic form of LLVM-style RTTI
|
||||
(which is sufficient for 99.9% of the cases). We will set up LLVM-style
|
||||
RTTI for this class hierarchy:
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
class Shape {
|
||||
public:
|
||||
Shape() {}
|
||||
virtual double computeArea() = 0;
|
||||
};
|
||||
|
||||
class Square : public Shape {
|
||||
double SideLength;
|
||||
public:
|
||||
Square(double S) : SideLength(S) {}
|
||||
double computeArea() override;
|
||||
};
|
||||
|
||||
class Circle : public Shape {
|
||||
double Radius;
|
||||
public:
|
||||
Circle(double R) : Radius(R) {}
|
||||
double computeArea() override;
|
||||
};
|
||||
|
||||
The most basic working setup for LLVM-style RTTI requires the following
|
||||
steps:
|
||||
|
||||
#. In the header where you declare ``Shape``, you will want to ``#include
|
||||
"llvm/Support/Casting.h"``, which declares LLVM's RTTI templates. That
|
||||
way your clients don't even have to think about it.
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
#include "llvm/Support/Casting.h"
|
||||
|
||||
#. In the base class, introduce an enum which discriminates all of the
|
||||
different concrete classes in the hierarchy, and stash the enum value
|
||||
somewhere in the base class.
|
||||
|
||||
Here is the code after introducing this change:
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
class Shape {
|
||||
public:
|
||||
+ /// Discriminator for LLVM-style RTTI (dyn_cast<> et al.)
|
||||
+ enum ShapeKind {
|
||||
+ SK_Square,
|
||||
+ SK_Circle
|
||||
+ };
|
||||
+private:
|
||||
+ const ShapeKind Kind;
|
||||
+public:
|
||||
+ ShapeKind getKind() const { return Kind; }
|
||||
+
|
||||
Shape() {}
|
||||
virtual double computeArea() = 0;
|
||||
};
|
||||
|
||||
You will usually want to keep the ``Kind`` member encapsulated and
|
||||
private, but let the enum ``ShapeKind`` be public along with providing a
|
||||
``getKind()`` method. This is convenient for clients so that they can do
|
||||
a ``switch`` over the enum.
|
||||
|
||||
A common naming convention is that these enums are "kind"s, to avoid
|
||||
ambiguity with the words "type" or "class" which have overloaded meanings
|
||||
in many contexts within LLVM. Sometimes there will be a natural name for
|
||||
it, like "opcode". Don't bikeshed over this; when in doubt use ``Kind``.
|
||||
|
||||
You might wonder why the ``Kind`` enum doesn't have an entry for
|
||||
``Shape``. The reason for this is that since ``Shape`` is abstract
|
||||
(``computeArea() = 0;``), you will never actually have non-derived
|
||||
instances of exactly that class (only subclasses). See `Concrete Bases
|
||||
and Deeper Hierarchies`_ for information on how to deal with
|
||||
non-abstract bases. It's worth mentioning here that unlike
|
||||
``dynamic_cast<>``, LLVM-style RTTI can be used (and is often used) for
|
||||
classes that don't have v-tables.
|
||||
|
||||
#. Next, you need to make sure that the ``Kind`` gets initialized to the
|
||||
value corresponding to the dynamic type of the class. Typically, you will
|
||||
want to have it be an argument to the constructor of the base class, and
|
||||
then pass in the respective ``XXXKind`` from subclass constructors.
|
||||
|
||||
Here is the code after that change:
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
class Shape {
|
||||
public:
|
||||
/// Discriminator for LLVM-style RTTI (dyn_cast<> et al.)
|
||||
enum ShapeKind {
|
||||
SK_Square,
|
||||
SK_Circle
|
||||
};
|
||||
private:
|
||||
const ShapeKind Kind;
|
||||
public:
|
||||
ShapeKind getKind() const { return Kind; }
|
||||
|
||||
- Shape() {}
|
||||
+ Shape(ShapeKind K) : Kind(K) {}
|
||||
virtual double computeArea() = 0;
|
||||
};
|
||||
|
||||
class Square : public Shape {
|
||||
double SideLength;
|
||||
public:
|
||||
- Square(double S) : SideLength(S) {}
|
||||
+ Square(double S) : Shape(SK_Square), SideLength(S) {}
|
||||
double computeArea() override;
|
||||
};
|
||||
|
||||
class Circle : public Shape {
|
||||
double Radius;
|
||||
public:
|
||||
- Circle(double R) : Radius(R) {}
|
||||
+ Circle(double R) : Shape(SK_Circle), Radius(R) {}
|
||||
double computeArea() override;
|
||||
};
|
||||
|
||||
#. Finally, you need to inform LLVM's RTTI templates how to dynamically
|
||||
determine the type of a class (i.e. whether the ``isa<>``/``dyn_cast<>``
|
||||
should succeed). The default "99.9% of use cases" way to accomplish this
|
||||
is through a small static member function ``classof``. In order to have
|
||||
proper context for an explanation, we will display this code first, and
|
||||
then below describe each part:
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
class Shape {
|
||||
public:
|
||||
/// Discriminator for LLVM-style RTTI (dyn_cast<> et al.)
|
||||
enum ShapeKind {
|
||||
SK_Square,
|
||||
SK_Circle
|
||||
};
|
||||
private:
|
||||
const ShapeKind Kind;
|
||||
public:
|
||||
ShapeKind getKind() const { return Kind; }
|
||||
|
||||
Shape(ShapeKind K) : Kind(K) {}
|
||||
virtual double computeArea() = 0;
|
||||
};
|
||||
|
||||
class Square : public Shape {
|
||||
double SideLength;
|
||||
public:
|
||||
Square(double S) : Shape(SK_Square), SideLength(S) {}
|
||||
double computeArea() override;
|
||||
+
|
||||
+ static bool classof(const Shape *S) {
|
||||
+ return S->getKind() == SK_Square;
|
||||
+ }
|
||||
};
|
||||
|
||||
class Circle : public Shape {
|
||||
double Radius;
|
||||
public:
|
||||
Circle(double R) : Shape(SK_Circle), Radius(R) {}
|
||||
double computeArea() override;
|
||||
+
|
||||
+ static bool classof(const Shape *S) {
|
||||
+ return S->getKind() == SK_Circle;
|
||||
+ }
|
||||
};
|
||||
|
||||
The job of ``classof`` is to dynamically determine whether an object of
|
||||
a base class is in fact of a particular derived class. In order to
|
||||
downcast a type ``Base`` to a type ``Derived``, there needs to be a
|
||||
``classof`` in ``Derived`` which will accept an object of type ``Base``.
|
||||
|
||||
To be concrete, consider the following code:
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
Shape *S = ...;
|
||||
if (isa<Circle>(S)) {
|
||||
/* do something ... */
|
||||
}
|
||||
|
||||
The code of the ``isa<>`` test in this code will eventually boil
|
||||
down---after template instantiation and some other machinery---to a
|
||||
check roughly like ``Circle::classof(S)``. For more information, see
|
||||
:ref:`classof-contract`.
|
||||
|
||||
The argument to ``classof`` should always be an *ancestor* class because
|
||||
the implementation has logic to allow and optimize away
|
||||
upcasts/up-``isa<>``'s automatically. It is as though every class
|
||||
``Foo`` automatically has a ``classof`` like:
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
class Foo {
|
||||
[...]
|
||||
template <class T>
|
||||
static bool classof(const T *,
|
||||
::std::enable_if<
|
||||
::std::is_base_of<Foo, T>::value
|
||||
>::type* = 0) { return true; }
|
||||
[...]
|
||||
};
|
||||
|
||||
Note that this is the reason that we did not need to introduce a
|
||||
``classof`` into ``Shape``: all relevant classes derive from ``Shape``,
|
||||
and ``Shape`` itself is abstract (has no entry in the ``Kind`` enum),
|
||||
so this notional inferred ``classof`` is all we need. See `Concrete
|
||||
Bases and Deeper Hierarchies`_ for more information about how to extend
|
||||
this example to more general hierarchies.
|
||||
|
||||
Although for this small example setting up LLVM-style RTTI seems like a lot
|
||||
of "boilerplate", if your classes are doing anything interesting then this
|
||||
will end up being a tiny fraction of the code.
|
||||
|
||||
Concrete Bases and Deeper Hierarchies
|
||||
=====================================
|
||||
|
||||
For concrete bases (i.e. non-abstract interior nodes of the inheritance
|
||||
tree), the ``Kind`` check inside ``classof`` needs to be a bit more
|
||||
complicated. The situation differs from the example above in that
|
||||
|
||||
* Since the class is concrete, it must itself have an entry in the ``Kind``
|
||||
enum because it is possible to have objects with this class as a dynamic
|
||||
type.
|
||||
|
||||
* Since the class has children, the check inside ``classof`` must take them
|
||||
into account.
|
||||
|
||||
Say that ``SpecialSquare`` and ``OtherSpecialSquare`` derive
|
||||
from ``Square``, and so ``ShapeKind`` becomes:
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
enum ShapeKind {
|
||||
SK_Square,
|
||||
+ SK_SpecialSquare,
|
||||
+ SK_OtherSpecialSquare,
|
||||
SK_Circle
|
||||
}
|
||||
|
||||
Then in ``Square``, we would need to modify the ``classof`` like so:
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
- 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_OtherSpecialSquare;
|
||||
+ }
|
||||
|
||||
The reason that we need to test a range like this instead of just equality
|
||||
is that both ``SpecialSquare`` and ``OtherSpecialSquare`` "is-a"
|
||||
``Square``, and so ``classof`` needs to return ``true`` for them.
|
||||
|
||||
This approach can be made to scale to arbitrarily deep hierarchies. The
|
||||
trick is that you arrange the enum values so that they correspond to a
|
||||
preorder traversal of the class hierarchy tree. With that arrangement, all
|
||||
subclass tests can be done with two comparisons as shown above. If you just
|
||||
list the class hierarchy like a list of bullet points, you'll get the
|
||||
ordering right::
|
||||
|
||||
| Shape
|
||||
| Square
|
||||
| SpecialSquare
|
||||
| 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``
|
||||
---------------------------
|
||||
|
||||
To be more precise, let ``classof`` be inside a class ``C``. Then the
|
||||
contract for ``classof`` is "return ``true`` if the dynamic type of the
|
||||
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
|
||||
``simplify_type``. However, those two need reference documentation in
|
||||
the form of doxygen comments as well. We need the doxygen so that we can
|
||||
say "for full details, see http://llvm.org/doxygen/..."
|
||||
|
||||
Rules of Thumb
|
||||
==============
|
||||
|
||||
#. The ``Kind`` enum should have one entry per concrete class, ordered
|
||||
according to a preorder traversal of the inheritance tree.
|
||||
#. The argument to ``classof`` should be a ``const Base *``, where ``Base``
|
||||
is some ancestor in the inheritance hierarchy. The argument should
|
||||
*never* be a derived class or the class itself: the template machinery
|
||||
for ``isa<>`` already handles this case and optimizes it.
|
||||
#. For each class in the hierarchy that has no children, implement a
|
||||
``classof`` that checks only against its ``Kind``.
|
||||
#. For each class in the hierarchy that has children, implement a
|
||||
``classof`` that checks a range of the first child's ``Kind`` and the
|
||||
last child's ``Kind``.
|
Reference in New Issue
Block a user