学习 LLVM(5) Twine 类

类 Twine 定义为文件 llvm/include/ADT/Twine.h 中。

参见:http://llvm.org/docs/ProgrammersManual.html#Twine

== 说明 ==
Twine 是一个轻量的数据结构,用于高效率地表达字符串的连接。

Twine 是一种 rope(串接),它使用二叉树表示一个连接的字符串,结果字符串是二叉树节点由前序遍历拼接而成。只有当需要字符串拼接结果的时候,才进行这种拼接到一个最终的 buffer 中,因此可以避免在多个字符串拼接中产生临时的 string 对象,及避免不必要的高消耗的内存分配操作。

Twine 对象一般在堆栈中分配,这样在语句结束的时候会自动释放。因为这个原因,一般不保存 Twine 对象,做为参数传递使用较多。

== 实现 ==

class Twine {
  union Child LHS, RHS;  // 二叉树的左、右节点。
  char/*enum NodeKind*/ LHSKind, RHSKind;  // 左右节点的类型。
  Twine(...)  // 非常多种形态的构造 *注1
  concat()    // 拼接(Twine = Twine + Twine)
  str() // 进行实际拼接,并返回为 std::string
  toVector() // 拼接并转换为 vector<char>
  toStringRef(),toNullTerminatedStringRef() // 灵巧转换为 StringRef
  print(),dump() // 输出、调试 Twine。
}
// 一些可以构造为 Twine 的操作符 '+' 的重载,如 operator+(StringRef&, char*) 等。

 

* 注1:构造 Twine(const long long &Val) 因为实际要保存 Val 的指针,因此 Val 这里使用了引用。调用者需要自己保证 Val 的内存合法。

* enum NodeKine 声明了 Twine 左右节点可能的所有类型,主要有:
** NullKind -- 空字符串,任何串与之拼接结果都是 Null(应该是相当于 null)
** EmptyKind -- 空字符串(相当于 "")
** TwineKind -- 节点是另一个 Twine 的指针。
** CStringKind -- 节点是 const char *
** StdStringKind -- 节点是 std::string *
** StringRefKind -- 节点是 StringRef *
** CharKind -- 节点是一个字符。
** DecUIKind 等多种数字类型,有符号的、无符号的,32,64位的。
** UHexKind -- 输出 16进制格式的数字。

* union Child 对应 enum NodeKind 为每一种节点类型定义了保存何种数据。sizeof(Child) == 4,当数据能容纳在 4 字节内的直接保存,否则保存的是指针(如 std::string*)。

== 例子 ==

StringRef sr1 = StringRef("world", 5);
std::string s1 = "LLVM!";
Twine t("Hello "); // 构造新的一个 Twine 从 const char *
Twine t2 = t.concat(sr1);   // t2 = "Hello world"
Twine t3 = t2 + " from ";   // t3 = "Hello world from "
Twine t4 = t3.concat(s1);   // t4 = "Hello world from LLVM!"
std::string s2 = t4.str();  // 得到 "Hello world from LLVM!"
Twine t5 = t4 + " And 你还能拼接一个数字: " + Twine(123);

 

== 实现机理和问题 ==
* Twine 要解决的问题是字符串拼接。在程序中,有很多需要临时拼接一个字符串,提供给别的函数做参数使用。
**  首先,拼接出来的字符串参数可能并不使用,则延迟拼接会减少空间分配的消耗。
**  次之,如果是多次拼接,如例子中所示,Twine 使用二叉树保存拼接表达式,也即模板表达式(expression),在最后真正需要结果(.str())的时候才进行实际的字符串拼接,这样可以大量节省分配、释放内存所需的昂贵操作。

* 我看现在 Twine.str() 方法中使用 SmallString<256> 作为拼接字符串使用的临时空间,并使用递归调用分别遍历左右节点产生拼接结果。也许还可以以后用更好的方法来进行实际合并?
* 节点可能保存的是 std::string, StringRef, long long 的指针,因此指针指向的数据要在 Twine 使用周期内确保生存。如果不小心使用,则会导致不可预知的结果。

你可能感兴趣的:(llvm)