X-Git-Url: http://plrg.eecs.uci.edu/git/?a=blobdiff_plain;f=docs%2FHowToSetUpLLVMStyleRTTI.rst;h=38929948590916ecc1198980e012abe21631295e;hb=f7e51b209e3c7c4013b0346396ab344130bb9c6a;hp=a2e46b3d7f213767911f3f451461f7987ca561b7;hpb=2ba4bd97d1aedf50c424c45ee327abddbf34dd12;p=oota-llvm.git diff --git a/docs/HowToSetUpLLVMStyleRTTI.rst b/docs/HowToSetUpLLVMStyleRTTI.rst index a2e46b3d7f2..38929948590 100644 --- a/docs/HowToSetUpLLVMStyleRTTI.rst +++ b/docs/HowToSetUpLLVMStyleRTTI.rst @@ -1,5 +1,3 @@ -.. _how-to-set-up-llvm-style-rtti: - ====================================================== How to set up LLVM-style RTTI for your class hierarchy ====================================================== @@ -42,14 +40,14 @@ RTTI for this class hierarchy: 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 @@ -137,7 +135,7 @@ steps: public: - Square(double S) : SideLength(S) {} + Square(double S) : Shape(SK_Square), SideLength(S) {} - double computeArea() /* override */; + double computeArea() override; }; class Circle : public Shape { @@ -145,7 +143,7 @@ steps: 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 @@ -177,7 +175,7 @@ steps: 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; @@ -188,7 +186,7 @@ steps: 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; @@ -225,8 +223,8 @@ steps: [...] template static bool classof(const T *, - ::llvm::enable_if_c< - ::llvm::is_base_of::value + ::std::enable_if< + ::std::is_base_of::value >::type* = 0) { return true; } [...] }; @@ -297,6 +295,78 @@ ordering right:: | 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`` @@ -307,6 +377,20 @@ 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 `_ vs. +`DeclContext `_ +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