LLVM的RTTI特性


本文思路來源於http://llvm.org/docs/HowToSetUpLLVMStyleRTTI.html,敘述有不同,望諒解,希望能從其他方面幫助大家了解C++語言的底層實現。

背景

在LLVM中默認禁止了C++的RTTI特性(RTTI特性的開關-fno-rtti),主要是為了性能考慮(C++默認的RTTI特別冗余,會使得編譯生成的文件大小增大,如果不使用RTTI反射機制的話,建議關閉。如果你對性能有極致要求的話,還可以考慮-fno-exceptions 禁用異常機制,但是關閉這兩個特性的話,需要重新編譯每一個依賴的軟件,比如最常用的libstdc++,這個工作量就比較大了)。但是為了方便考慮,LLVM中又使用了自己手擼(hand-rolled,手卷,感覺翻譯成手擼可能比較貼合語義)的RTTI,這種特有的RTTI特性更有效而且更加靈活。當然,方便性的同時,也帶來了更多的工作量。

在這里所有的工作都在LLVM 的UserManual中有體現,在深入研究之前,要求類的書寫者(類的使用者,根本不會遇到如何實現LLVM的RTTI特性問題)了解“is-a”與“is-like-a”的關系(B繼承至A,覆蓋A的方法,B is-a A,新增方法,B is-like-a A)。

基礎

本節介紹如何設置最基本的LLVM風格的RTTI(這足以滿足99.9%的情況)。比如,我們的類繼承關系如下:

 

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;
};

 

按照以下4步進行修改,你就可以得到一個llvm形式的RTTI:

1.添加頭文件

#include "llvm/Support/Casting.h"

2.在基類中,添加一個枚舉類型,這個枚舉類型存儲所有繼承至該類的每個類的值(其實就是枚舉編碼)

實現代碼如下:

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;
 };

這里值得提的一點是,llvm風格的RTTI支持沒有v-tables(虛函數表)的類,而C++默認的dynamic_cast<>運算符並不支持這種轉換。

3.接下來,需要確保該類被初始化為與類的動態類型相對應的值。通常,您希望它是基類構造函數的參數,然后從子類構造函數傳入各自的XXXkind。

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;
 };

4.有了上邊的這些代碼,還不夠,還需要一步:告訴類,自己的類型是什么,這里是通過classof方法實現的

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;
+  }
 };

這里已經完成了LLVM風格的RTTI(其實C++的RTTI實現也是同樣的方法,這里用法有點不准確,LLVM和MSVC的RTTI實現略有不同,大概流程是typeid,調用___RTtypeid(),判斷是否有vfptr,然后根據type_info來進行實現的,具體可以看下struct RTTIXXX那幾個結構體,感興趣的反匯編一下看看

如何實現層次繼承的RTTI特性,大家可以關注下原文,主要是修改對應的classof,將原來的==判斷改為多個判斷,這里不進行贅述。

經驗法則

懶得翻譯了,自己看吧,很簡單,重要的是繼承樹的先序遍歷

 

  1. The Kind enum should have one entry per concrete class, ordered according to a preorder traversal of the inheritance tree.
  2. 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.
  3. For each class in the hierarchy that has no children, implement a classof that checks only against its Kind.
  4. 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.

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM