Chromium 基礎庫使用說明(結合c++ 17入門經典 好好理解 非常重要)


轉自:Chromium 基礎庫使用說明

原文:

Important Abstractions and Data Structures

基礎howto介紹:chrome是如何調用啟動的:https://www.chromium.org/developers/how-tos/getting-around-the-chrome-source-code

 Threading and Tasks in Chrome - Bigben - 博客園 (cnblogs.com)

Chromium和WebKit的智能指針實現原理分析_老羅的Android之旅-CSDN博客

Chromium多線程模型設計和實現分析_老羅的Android之旅-CSDN博客

Chromium多線程通信的Closure機制分析_老羅的Android之旅-CSDN博客

C++ in Chromium 101 - Codelab 編程開始 bind 多線程 回調 - Bigben - 博客園 (cnblogs.com) 


Chromium 提供了一個類似 WTF 的基礎庫,甚至包含了更多的內容。這個基礎庫在 Blink 之外被廣泛使用(Blink 里面仍然使用的是 WTF),了解它的使用對我們實際的代碼編寫是十分重要的。本文主要介紹 Chromium 基礎庫包括的主要內容,並詳細說明一些重要類型的使用方式。如果需要了解某個特定目錄或者文件的內容概要,學輝的這篇文檔可以提供一個不錯的全面索引,另外 Chromium 為所有的基礎庫類型都提供了完整的單元測試,通過閱讀單元測試代碼了解這些類型的使用也是很好的方式。

 

Chromium 基礎庫概覽

Chromium 基礎庫包括的內容十分繁雜,我把其中的主要部分大致分為以下幾類:

  • 容器類型

Chromium 的代碼主要使用 STL 容器類型,比如 std::vector,std::list,另外 GCC 和 MSVC 提供的 STL 擴展容器類型 hash_map 和 hash_set 也在 Chromium 中使用,不過統一放在 base 名字空間里面,通過 base::hash_map,base_hash_set 使用。

在 STL 外,Chromium 基礎庫還提供了一些額外的容器類型比如 base::LinkedList,base::MRUCache 等。

容器類型代碼位於 containers 子目錄下。

  • 智能指針

Chromium 提供了一篇官方的文檔 Smart Pointer Guidelines 講解了在 Chromium 里面常見的幾種智能指針,最常見的包括 base::scoped_ptr,base::ScopedVector,base::WeakPtr 和 base::scoped_refptr。

智能指針代碼位於 memory 子目錄下。

  • 回調函數

Chromium 基礎庫提供了 base::Bind 機制,可以將全局函數和成員函數,跟它的調用上下文綁定在一起,構成一個回調函數對象。這個回調函數對象可以被傳遞,被保存,被當做消息發送到線程的消息循環里面,最后我們可以通過這個回調函數對象的 Run 方法調用跟它關聯的函數。

回調函數代碼位於基礎庫的根目錄下。

  • 線程相關

Chromium 基礎庫提供了大量跟線程相關的設施,包括平台線程的封裝類型 base::Thread,線程本地存儲 base::ThreadLocalPointer,消息循環 base::MessageLoop,線程同步設施 base::Lock,base::WaitableEvent 等等,還有原子操作和內存屏障的支持。

線程相關的代碼位於 threading,message_loop,synchronization 子目錄下,原子操作和內存屏障位於根目錄的 atomicops.h。

  • 字串處理

Chromium 使用 std::string 作為字串容器,官方文檔 Chromium String usage 提供了在 Chromium 里面字串使用的一些說明。另外 strings 子目錄下提供了一些針對字串的輔助操作設施。

  • 文件操作

Chromium 基礎庫的 base::File 提供了文件相關的操作,相關的代碼位於根目錄和 files 子目錄下;

  • 計時器

Chromium 基礎庫的 base::Timer 提供了計時器相關的操作,相關的代碼位於 timer 子目錄下;

  • 日志和調試

Chromium 基礎庫提供了通用的日志輸出和各種調試輔助等機制,相關的代碼位於根目錄, debug 和 profile 子目錄下;

  • 系統監控

包括系統狀態監控,電池狀態監控和內存監控,分別位於 system_monitor,power_monitor,和 memory 子目錄下;

  • Android 相關

基礎庫的 android 子目錄下是 Android 平台相關的代碼,除了包括其它基礎類型的 Android 適配代碼外,還有一些 Android 平台特有的類型,像一些用於 JNI 支持的輔助類型。

除了上面列舉的部分外,基礎庫還包括的一些設施有進程,內存分配器,國際化支持,隨機數生成,Base64編碼,Sha1編碼等等,還有一些難以歸類的工具類型。

 

容器類型

 

LinkedList

base::LinkedList 是 std::list 的一個替代品,優點是當你擁有一個節點對象時,要刪除這個節點只需要 O(1) 的復雜度,並且插入節點不需要新增分配內存。能夠做到這一點是因為 LinkedList 要求節點類型必須以 LinkNode 作為基類,而 LinkNode 本身已經包含了指向前/后節點的指針。下面的代碼演示了 LinkedList 的常見用法:

 
  1. class MyNodeType : public LinkNode<MyNodeType> {
  2. ...
  3. };
  4. LinkedList<MyNodeType> list;
  5. LinkNode<MyNodeType>* n1 = ...;
  6. LinkNode<MyNodeType>* n2 = ...;
  7. LinkNode<MyNodeType>* n3 = ...;
  8. list.Append(n1);
  9. list.Append(n3);
  10. n2->InsertBefore(n3);
  11. for (LinkNode<MyNodeType>* node = list.head();
  12. node != list.end();
  13. node = node->next()) {
  14. MyNodeType* value = node->value();
  15. ...
  16. }
 

MRUCache

MRU 是 most recently used 的縮寫,MRUCache 提供了一個類似 Map 的容器類型,主要的區別是可以設定容器的最大容納個數,如果超過則自動移除最久不被使用的那個對象。

MRUCache 實際上還存在幾種不同的變種:

  1. MRUCache 是最常用的,它假設自身不擁有對象,當移除對象時不執行刪除操作;
  2. OwningMRUCache 假設自己擁有對象,並要求存儲對象是使用指針類型,在移除對象時會執行刪除操作;
  3. HashingMRUCache 跟 MRUCache 的區別是,它內部使用 base::hash_map 而不是 std::map 存儲對象,所以也要求鍵值對象支持 hash 操作;
 

智能指針

按照官方文檔的說明,什么時候我們應該使用什么類型的智能指針:

  • 擁有對象的時候

使用 scoped_ptr 或者 ScopedVector,它們可以使用來管理所擁有的非引用計數的堆分配對象。

  • 不擁有對象的時候

使用 raw pointer 或者 WeakPtr。如果其它代碼擁有對象,但是你需要知道這個對象是否已經被銷毀,就使用 WeakPtr,當所關聯的對象被銷毀的時候 WeakPtr 會自動被置空。你可以通過 WeakPtr.get 方法獲得關聯對象的指針,如果返回值為空則說明對象已經被銷毀。

  • 使用引用計數對象的時候

使用 scoped_refptr,不過 Chromium 不鼓勵使用引用計數對象,特別是在多線程場景下,引用計數對象會使對象的擁有權難以確定和對象銷毀的順序和時機難以確定。

 

scoped_ptr

base::scoped_ptr 是 Chromium 里面最常用的智能指針,一些常見的用法:

 
  1. // We put a pointer into a smart pointer at construction time.
  2. scoped_ptr<base::Value> value(base::JSONReader::Read(data));
  3. scoped_ptr<Foo> foo_ptr(new Foo(...));
  4. // ...Or by using reset().
  5. scoped_ptr<Bar> bar_ptr; // Like "Bar* bar_ptr = NULL;".
  6. bar_ptr.reset(new Bar(...)); // Now |bar_ptr| is non-NULL and owns the object.
  7. // We can test the smart pointer directly or use get() to see the raw pointer underneath.
  8. if (!value)
  9. return false;
  10. Foo* raw_ptr = foo_ptr.get();
  11. // We can call through the smart pointer as if it were a pointer.
  12. DictionaryValue* dict = NULL;
  13. if (!value->GetAsDictionary(&dict))
  14. return false;

當 scoped_ptr 作為函數參數使用時,這意味着函數的代碼會獲得參數對象的所有權,函數的調用者如果不是使用一個臨時的 scoped_ptr 的話,它需要使用 Pass() 方法來放棄自己的 scoped_ptr 對對象的所有權,例程如下:

 
  1. // Foo() takes ownership of |bar|.
  2. void Foo(scoped_ptr<Bar> bar);
  3. ...
  4. scoped_ptr<Bar> bar_ptr(new Bar());
  5. Foo(bar_ptr.Pass()); // Pass() makes |bar_ptr| NULL.
  6. Foo(scoped_ptr<Bar>(new Bar())); // No need to use Pass() on temporaries.

如果函數返回一個 scoped_ptr,這意味着函數的調用者獲得返回對象的所有權,例程如下:

 
  1. // Foo takes ownership of |bar|, and the caller takes ownership of the returned
  2. // object.
  3. scoped_ptr<Bar> Foo(scoped_ptr<Bar> bar) {
  4. if (cond) {
  5. return bar.Pass(); // Transfers ownership of |bar| back to
  6. // the caller.
  7. }
  8. return scoped_ptr<Bar>(new Bar())); // No Pass() necessary on temporaries.
  9. // Note that on this codepath, |bar| gets deleted here.
  10. }

最后需要注意的是不應該在函數的參數和返回值中使用 scoped_ptr 的指針或者引用形式(scoped_ptr<>* scoped_ptr<>&),它會模糊所有權的轉移,使最終誰擁有對象的所有權難以理解。

 

ScopedVector

在 STL 容器里面存儲 scoped_ptr, 類似 std::vector<scoped_ptr<T> > 這樣的用法可能會有問題,比如下面的代碼:

 
  1. std::vector<scoped_ptr<T> > vec;
  2. ...
  3. // 對象的所有權會從 vec 轉移到 scoped_ptr p,並隨着 p 被銷毀而銷毀!!!
  4. scoped_ptr<T> p = vec[0];

因為上述代碼的危險性,所以 Chromium 不支持通過 STL 容器存儲 scoped_ptr,它提供了 base::ScopedVector 來滿足大部分這種需求,ScopedVector 擁有存儲在它內部的對象,並在移除對象的時候負責銷毀對象,如果 ScopedVector 本身被銷毀,它會銷毀它所存儲的所有對象。因為 ScopedVector 內部存儲的是 raw pointer,就不存在像 std::vector<scoped_ptr<T> > 這樣容易誤用的危險性。

 
  1. base::ScopedVector<T> vec;
  2. ...
  3. // 通過 raw pointer p 使用對象,不會有所有權的轉移
  4. T* p = vec[0];

如果需要在其它 STL 容器里面使用智能指針,希望在容器被銷毀或者移除元素時自動銷毀容器存儲的對象,可以考慮使用 linked_ptr。

 

WeakPtr

base::WeakPtr 是所謂的弱指針,Chromium 鼓勵更多使用 WeakPtr 而不是濫用需要引用計數的 scoped_refptr,因為 WeakPtr 明確不會擁有對象的所有權,也不會影響對象的銷毀順序。

base::WeakPtr 需要通過 base::WeakPtrFactory 創建,一般情況下它們使用的方式是這樣的:

 
  1. class Controller {
  2. public:
  3. void SpawnWorker() { Worker::StartNew(weak_factory_.GetWeakPtr()); }
  4. void WorkComplete(const Result& result) { ... }
  5. private:
  6. // Member variables should appear before the WeakPtrFactory, to ensure
  7. // that any WeakPtrs to Controller are invalidated before its members
  8. // variable's destructors are executed, rendering them invalid.
  9. WeakPtrFactory<Controller> weak_factory_;
  10. };
  11. class Worker {
  12. public:
  13. static void StartNew(const WeakPtr<Controller>& controller) {
  14. Worker* worker = new Worker(controller);
  15. // Kick off asynchronous processing...
  16. }
  17. private:
  18. Worker(const WeakPtr<Controller>& controller)
  19. : controller_(controller) {}
  20. void DidCompleteAsynchronousProcessing(const Result& result) {
  21. if (controller_)
  22. controller_->WorkComplete(result);
  23. }
  24. WeakPtr<Controller> controller_;
  25. };
  1. 需要支持 WeakPtr 的類型 Controller 擁有一個 WeakPtrFactory 的成員變量,外部獲取的 WeakPtr 都是通過這個 WeakPtrFactory 創建的;
  2. 當 Controller 對象被銷毀時,它的 WeakPtrFactory 成員變量也會同時被銷毀,WeakPtrFactory 被銷毀的同時會將所有通過它創建的 WeakPtr 置空;
  3. Controller 的 WeakPtrFactory 的成員變量一般放在最后面,這樣它就是第一個被銷毀的成員變量,似乎沒有太大意義,不過 Chromium 習慣使用這樣的方式;

在多線程環境下使用 WeakPtr 和 WeakPtrFactory 需要注意,它們只支持這樣的方式:

  1. WeakPtrFactory 和 WeakPtr 屬於創建它們的線程,也只能在創建它們的線程將 WeakPtr 置空,檢查一個 WeakPtr 是否為空,和訪問 WeakPtr 指向的對象;
  2. 屬於線程 A 的 WeakPtr 可以傳遞給 線程 B,線程 B 不能直接使用這個 WeakPtr,這不是線程安全的,但是它可以使用這個 WeakPtr 往線程 A 發送任務(PostTask),因為任務是在線程 A 執行的,所以任務執行代碼本身可以使用這個 WeakPtr;
 

scoped_refptr

用於支持引用計數對象的智能指針,要求對象類型繼承至 RefCounted 或者 RefCountedThreadSafe,后者是線程安全的。Chromium 因為歷史遺留的緣故,當前的代碼中使用 scoped_refptr 的地方還比較多,但是目前官方已經不鼓勵 scoped_refptr 的使用,認為它會導致對象的所有權,和銷毀的順序和時機難以確定,並認為絕大部分情況下 scoped_refptr 都可以使用 scoped_ptr 和 WeakPtr 來取代,設計本身也不應該過多依賴多個線程共享對象這種方式。

下面是一些簡單的使用例程:

 
  1. class MyFoo : public RefCounted<MyFoo> {
  2. ...
  3. };
  4. void some_function() {
  5. scoped_refptr<MyFoo> foo = new MyFoo();
  6. foo->Method(param);
  7. // |foo| is released when this function returns
  8. }
  9. void some_other_function() {
  10. scoped_refptr<MyFoo> foo = new MyFoo();
  11. ...
  12. foo = NULL; // explicitly releases |foo|
  13. ...
  14. if (foo)
  15. foo->Method(param);
  16. }
  17. {
  18. scoped_refptr<MyFoo> a = new MyFoo();
  19. scoped_refptr<MyFoo> b;
  20. b.swap(a);
  21. // now, |b| references the MyFoo object, and |a| references NULL.
  22. }
  23. {
  24. scoped_refptr<MyFoo> a = new MyFoo();
  25. scoped_refptr<MyFoo> b;
  26. b = a;
  27. // now, |a| and |b| each own a reference to the same MyFoo object.
  28. }
 

linked_ptr

linked_ptr 行為上有些類似 scoped_refptr,但是不需要對象本身支持引用計數,它是通過將所有指向同一個對象的 linked_ptr 鏈接成一條鏈來實現引用計數的,當一個 linked_ptr 從另外一個 linked_ptr 拷貝時,它會把自身加入這條鏈,而這個 linked_ptr 被銷毀時,它會把自身從這條鏈移除,如果它是最后一個,則同時銷毀指向的對象。

linked_ptr 實際上有可能比 scoped_refptr 更危險,它使得對象的持有者和銷毀時機變得更不明確,同時也不是線程安全的。所以 linked_ptr 一般只是用在 STL 容器上面,容器持有這些對象,並且在容器本身被銷毀時銷毀對象,這樣就不會產生太多混亂。

base::SupportsUserData 的實現里面使用了 linked_ptr,用來在一個 std::map 里面存儲 User Data。

 
  1. typedef std::map<const void*, linked_ptr<Data> > DataMap;
  2. // Externally-defined data accessible by key.
  3. DataMap user_data_;
  4. SupportsUserData::Data* SupportsUserData::GetUserData(const void* key) const {
  5. DataMap::const_iterator found = user_data_.find(key);
  6. if (found != user_data_.end())
  7. return found->second.get();
  8. return NULL;
  9. }
  10. void SupportsUserData::SetUserData(const void* key, Data* data) {
  11. user_data_[key] = linked_ptr<Data>(data);
  12. }
 

回調函數

Chromium 提供了 base::Bind 和模版類型 base::Callback 對函數回調提供了支持,下面是一個簡單的使用例程,將一個全局函數綁定到一個 Callback 對象,並通過 Callback.Run 調用這個函數:

 
  1. int Return5() { return 5; }
  2. base::Callback<int(void)> func_cb = base::Bind(&Return5);
  3. LOG(INFO) << func_cb.Run(); // Prints 5.

如果要綁定一個類的成員函數,我們需要為 Bind 方法提供這個類的一個實例對象,把它跟 Callback 對象綁定,為了保證這個對象在 Callback 對象被執行時仍然存活,或者 Callback 對象能夠知道這個對象已經被銷毀,我們需要提供一個 scoped_refptr 或者 WeakPtr,通過 base::Unretained(ptr) 用 raw pointer 也可以,不過后果自負... 早期 Chromium 的代碼使用 scoped_refptr 比較多,現在 Chromium 更傾向於使用 WeakPtr,當然使用 WeakPtr 時我們要注意這個 Callback 只能在 WeakPtr 所屬的線程中被調用,因為它是非線程安全的,下面是一個使用 scoped_refptr 的例子:

 
  1. class Ref : public base::RefCountedThreadSafe<Ref> {
  2. public:
  3. int Foo() { return 3; }
  4. void PrintBye() { LOG(INFO) << "bye."; }
  5. };
  6. scoped_refptr<Ref> ref = new Ref();
  7. base::Callback<void(void)> ref_cb = base::Bind(&Ref::Foo, ref);
  8. LOG(INFO) << ref_cb.Run(); // Prints out 3.

如果綁定的函數需要參數,我們可以事先綁定所有參數對象到 Callback 里面,也可以事先不綁定參數,甚至可以事先只綁定一部分參數,事先綁定所有參數的 Callback 在 Chromium 里面稱為閉包 Closure:

 
  1. void MyFunc(int i, const std::string& str) {}
  2. base::Callback<void(int, const std::string&)> cb = base::Bind(&MyFunc);
  3. cb.Run(23, "hello, world");
  4. void MyFunc(int i, const std::string& str) {}
  5. base::Callback<void(void)> cb = base::Bind(&MyFunc, 23, "hello world");
  6. cb.Run();
  7. base::Closure cb = base::Bind(&MyClass::MyFunc, this, 23, "hello world");

如果想讓 Callback 對象擁有跟它綁定的類對象或者參數對象,也可以使用 base::Owned 或者 base::Passed 方法,分別針對 raw pointer 和 scoped_ptr,如果是 scoped_ptr 類型參數的話,在調用時 Callback 就會將這個參數對象的所有權轉移給被回調的函數,最后 Callback 對象被銷毀時會自動銷毀綁定的類對象和參數對象(如果還擁有這個參數對象的話):

 
  1. MyClass* myclass = new MyClass;
  2. base::Bind(&MyClass::Foo, base::Owned(myclass));
  3. void TakesOwnership(scoped_ptr<Foo> arg) {}
  4. scoped_ptr<Foo> f(new Foo);
  5. // f becomes null during the following call.
  6. base::Closure cb = base::Bind(&TakesOwnership, base::Passed(&f));

總而言之,在使用 Chromium 的回調函數機制時,一定要非常清楚跟 Callback 對象綁定的類對象和參數對象的所有權和生命周期,避免在 Callback 被調用時,訪問到已經被銷毀的對象。

 

線程相關

 

線程和消息循環

base::Thread 是 Chromium 提供的對平台線程的封裝,並自帶了消息循環 base::MessageLoop,如果需要一個不用消息循環的線程,可以考慮使用 base::SimpleThread。

一個繼承 base::Thread 的自己的線程類,可能需要復寫 Init 和 Cleanup 方法,它們在這個線程中被調用,分別位於消息循環啟動和停止的時候。

 
  1. class InProcessRendererThread : public base::Thread {
  2. public:
  3. ...
  4. protected:
  5. virtual void Init() override;
  6. virtual void CleanUp() override;
  7. ...
  8. };

我們可以通過 Thread.message_loop 或者 Thread.message_loop_proxy 方法獲取這個線程的消息循環,后者返回的是 MessageLoopProxy,在 Chromium 里面使用 MessageLoopProxy 比直接使用 MessageLoop 要更普遍,並且通過 scoped_refptr 的方式使用 MessageLoopProxy 比通過 raw pointer 的方式使用 MessageLoop 也更安全,通過下面的兩種方式可以獲得當前運行線程的 MessageLoopProxy。

 
  1. MessageLoop::current()->message_loop_proxy()
  2. MessageLoopProxy::current()

MessageLoopProxy 繼承了接口 SequencedTaskRunner,后者又繼承了接口 TaskRunner,所以 MessageLoopProxy 實現了一系列的 PostXXXTask 的方法。一個 Task 實際上就是一個 Closure,如前所述 Closure 就是一個預先綁定了所有參數對象的 Callback 對象。通過 MessageLoopProxy PostTask 就相當於發送一個消息給這個 MessageLoopProxy 所屬的線程,這個被發送的 Callback 對象將會在 MessageLoopProxy 所屬的線程執行,跟 Callback 對象綁定的函數將會被調用。

PostXXXTask 有若干變種,包括延遲的時間,是否是 Non-Nestable。延遲時間比較容易理解,不需要延遲則為 0,而 Non-Nestable 的意思是 - 如果 Task T1 在執行過程中 Post Task T2 到當前線程的 MessageLoop,並且 T1 接着直接調用 MessageLoop 的 Run,或者 RunLoop 的 Run 方法,相當於要求 MessageLoop 在當前消息循環中進入一個子循環,馬上執行其它等待中的任務,在這種狀況下 MessageLoop 進入了 Nested 狀態,如果 T2 是 Non-Nestable,Chromium 將會保證 T2 在這種情況下絕對不會被執行,如果 T2 不是 Non-Nestable,就有可能在會被執行。

下面是一個簡單使用例程:

 
  1. scoped_refptr<base::MessageLoopProxy> ui_loop_;
  2. base::WeakPtr<SharedRendererState> ui_thread_weak_ptr_;
  3. void SharedRendererState::PostExternalDrawConstraintsToChildCompositor(
  4. const ParentCompositorDrawConstraints& parent_draw_constraints) {
  5. if (UpdateDrawConstraints(parent_draw_constraints)) {
  6. // No need to hold the lock_ during the post task.
  7. ui_loop_->PostTask(
  8. FROM_HERE,
  9. base::Bind(&SharedRendererState::UpdateParentDrawConstraintsOnUIThread,
  10. ui_thread_weak_ptr_));
  11. }
  12. }

如果需要任務執行后原線程獲得通知,可以使用 PostTaskAndReply 方法,參考下面的例程,task 執行后,reply 會在調用 PostTaskAndReplay 的原線程被調用,並且 task 和 reply 對象都保證在原線程被銷毀,這樣我們可以在 task 和 reply 上綁定必須要在原線程銷毀的對象。另外一些需要注意的地方:

  • task 綁定的類對象會作為參數傳遞給 reply 的回調函數;
  • reply 綁定的類對象 DataLoder 不是線程安全的,它通過 WeakPtr 跟 reply 綁定,可以提前被銷毀,reply 會被自動取消;
 
  1. bool PostTaskAndReply(const tracked_objects::Location& from_here,
  2. const Closure& task,
  3. const Closure& reply);
  4. class DataBuffer : public RefCountedThreadSafe<DataBuffer> {
  5. public:
  6. // Called to add data into a buffer.
  7. void AddData(void* buf, size_t length);
  8. ...
  9. };
  10. class DataLoader : public SupportsWeakPtr<DataLoader> {
  11. public:
  12. void GetData() {
  13. scoped_refptr<DataBuffer> buffer = new DataBuffer();
  14. target_thread_.message_loop_proxy()->PostTaskAndReply(
  15. FROM_HERE,
  16. base::Bind(&DataBuffer::AddData, buffer),
  17. base::Bind(&DataLoader::OnDataReceived, AsWeakPtr(), buffer));
  18. }
  19. private:
  20. void OnDataReceived(scoped_refptr<DataBuffer> buffer) {
  21. // Do something with buffer.
  22. }
  23. };

如果 PostTask 之后,我們又希望取消它,可以使用 base::CancelableTaskTracker 來 PostTask,CancelableTaskTracker 本身不是線程安全的,它的創建,銷毀,PostTask,Cancel 都必須在同一個線程。下面是一個簡單的使用例子:

 
  1. Thread worker_thread("worker thread");
  2. worker_thread.Start();
  3. CancelableTaskTracker::TaskId task_id =
  4. task_tracker_.PostTaskAndReply(worker_thread.message_loop_proxy().get(),
  5. FROM_HERE,
  6. Bind(&DoNothing),
  7. Bind(&DoNothing));
  8. task_tracker_.TryCancel(task_id);
 

線程本地存儲

Chromium 提供了 ThreadLocalPointer,它是平台相關的線程本地存儲機制的封裝,可以存放一個 raw pointer,如果需要的是 bool 類型的變量,ThreadLocalBoolean 提供了更簡單的使用方式。ThreadLocalPointer 的一個簡單例程:

 
  1. // My class is logically attached to a single thread. We cache a pointer
  2. // on the thread it was created on, so we can implement current().
  3. MyClass::MyClass() {
  4. DCHECK(Singleton<ThreadLocalPointer<MyClass> >::get()->Get() == NULL);
  5. Singleton<ThreadLocalPointer<MyClass> >::get()->Set(this);
  6. }
  7. MyClass::~MyClass() {
  8. DCHECK(Singleton<ThreadLocalPointer<MyClass> >::get()->Get() != NULL);
  9. Singleton<ThreadLocalPointer<MyClass> >::get()->Set(NULL);
  10. }
  11. // Return the current MyClass associated with the calling thread, can be
  12. // NULL if there isn't a MyClass associated.
  13. MyClass* MyClass::current() {
  14. return Singleton<ThreadLocalPointer<MyClass> >::get()->Get();
  15. }
 

線程同步

base::Lock 是平台相關鎖的封裝,base::AutoLock 提供了一個自動加鎖/解鎖的輔助類,這部分都比較容易理解。base::ConditionVariable 是平台相關的條件量的封裝,跟其它庫的 Condition 類型有些不同的是,它需要在構造時就指定對應的鎖,而不是在 Wait 的時候才指定。

為了方便實現線程同步消息,Chromium 還提供了 base::WaitableEvent (如果是位於 cc 模塊的代碼,也可以使用 cc::CompletionEvent,它是 base::WaitableEvent 的封裝),WaitableEvent 構造函數的第一個參數 manual_reset 的含義是,如果它為 false,一個已經 signaled 的 WaitableEvent 在被查詢 IsSignaled 后會自動恢復到 unsignaled 的狀態,所以一般沒有特殊需要第一個參數都應該為 true。下面是一個簡單的線程同步消息處理的例子:

 
  1. template <typename T>
  2. static void RunTaskWithResult(base::Callback<T(void)> task,
  3. T* result,
  4. base::WaitableEvent* completion) {
  5. *result = task.Run();
  6. completion->Signal();
  7. }
  8. base::WaitableEvent completion(true, false);
  9. bool result = false;
  10. QueueTask(
  11. base::Bind(&RunTaskWithResult<bool>, init_task, &result, &completion));
  12. completion.Wait();

base::WaitableEventWatcher 提供了 WaitableEvent 異步響應的使用方式,請看下面的例程,我們可以通過 WaitableEventWatcher 監控某個 WaitableEvent,並在它被 Signal 的時候觸發事先設定的回調函數,實際內部實現是當 WaitableEvent 被 Signal 時,WaitableEventWatcher 事先設定的 Callback 對象會被發送到 StartWatching 的調用線程的消息循環里面:

 
  1. class MyClass {
  2. public:
  3. void DoStuffWhenSignaled(WaitableEvent *waitable_event) {
  4. watcher_.StartWatching(waitable_event,
  5. base::Bind(&MyClass::OnWaitableEventSignaled, this);
  6. }
  7. private:
  8. void OnWaitableEventSignaled(WaitableEvent* waitable_event) {
  9. // OK, time to do stuff!
  10. }
  11. base::WaitableEventWatcher watcher_;
  12. };
 

字串處理

Chromium 主要使用 std::string 作為字串類型,std::string 的一個主要問題是它本身不包含編碼信息,所以 Chromium 約定 std::string 使用 UTF-8 編碼,基礎庫里面還提供了 base::string16,string16 使用 UTF-16 編碼。

Chromium 另外還有一個 base::StringPiece 類型,它類似 WTF 里面的 CString,基本上就是 C 風格字串的一個簡單封裝,StringPiece 通常只是用來傳遞一塊 string data 或者 raw data,它本身並不擁有這些數據,銷毀時也不會釋放數據。

下面是一些使用時的注意事項:

  • 使用 string.empty() 做空串檢查;
  • 字串常量使用 char[] 而不是 std::string,比如 const char kFoo[] = “foo”;
  • 在函數輸入參數中使用 std::string,最好使用引用常量的方式避免拷貝;
  • 在循環的 inner loop 里面,一般應該避免臨時 std::string 對象創建;

Chromium 提供的一些字串處理的輔助方法,比如字串格式化,分割,數值字串類型轉換,比較,替換等等,位於 strings 子目錄下,都比較簡單,這里就不再詳細說明了。

 

文件操作

 

PathService

base::PathService 提供了一種設定和獲取一些預定義用途目錄的機制,在 Android 上,我們需要的目錄定義在 base_path_android.h 和 ui_base_path.h 里面,另外 PathService.java 提供了在 Java 端設定路徑的功能。

 
  1. enum {
  2. PATH_ANDROID_START = 300,
  3. DIR_ANDROID_APP_DATA, // Directory where to put Android app's data.
  4. DIR_ANDROID_EXTERNAL_STORAGE, // Android external storage directory.
  5. PATH_ANDROID_END
  6. };
 
  1. PathService.override(PathService.DIR_MODULE, "/system/lib/");
  2. final int DIR_RESOURCE_PAKS_ANDROID = 3003;
  3. PathService.override(DIR_RESOURCE_PAKS_ANDROID,
  4. "/system/framework/webview/paks");
 

File

base::File 提供了平台相關的文件對象的封裝,可以通過它對文件和目錄進行操作,包括創建,讀寫文件等等。base::FilePath 提供了一個文件或者目錄路徑的封裝。

base::FileProxy 提供了一種異步文件操作的方法,你可以為 FileProxy 設置一個 TaskRunner,比如某個線程的 MessageLoopProxy,然后在 FileProxy 上執行的操作實際上都是由這個 TaskRunner 所屬的線程異步執行,FileProxy 提供的方法跟 File 基本一致,一般后面會增加一個用於響應操作結果的 Callback 對象,這個 Callback 對象會在原調用線程執行。FileProxy 有一個限制是不能同時 Proxy 多個操作,只有完成一個操作后才能執行下一個操作。

下面是一個簡單的例程,我們在另外一個 file_thread_ 線程創建或者打開一個文件,當文件創建或者打開后,原線程會執行 DidCreateOrOpen 函數處理操作結果:

 
  1. TaskRunner* file_task_runner() const {
  2. return file_thread_.message_loop_proxy().get();
  3. }
  4. void DidCreateOrOpen(File::Error error) {
  5. error_ = error;
  6. MessageLoop::current()->QuitWhenIdle();
  7. }
  8. FileProxy proxy(file_task_runner());
  9. proxy.CreateOrOpen(
  10. test_path(),
  11. File::FLAG_CREATE | File::FLAG_READ,
  12. Bind(&FileProxyTest::DidCreateOrOpen, weak_factory_.GetWeakPtr()));
  13. MessageLoop::current()->Run();
  14. EXPECT_EQ(File::FILE_OK, error_);
  15. EXPECT_TRUE(proxy.IsValid());
  16. EXPECT_TRUE(proxy.created());
  17. EXPECT_TRUE(PathExists(test_path()));

Chromium 還提供很多文件相關的輔助類:

  • base::FileEnumerator 提供了枚舉某個 FilePath 下面的子文件的功能;
  • base::FilePathWatcher 提供了監控某個文件或者目錄變化的功能;
  • base::ImportantFileWriter 提供了另外一種文件寫入方式,避免應用崩潰導致文件寫入一半,數據不完整的狀況,原理是先寫入一個臨時文件,寫完后再重命名;
  • base::MemoryMappedFile 提供了一種將只讀文件全部或者部分映射到內存,讀取文件相當於內存訪問,加快讀取的速度的機制;
  • file_util.h 里面提供大量文件操作的輔助方法,比如 CreateTemporaryFile,GetFileSize 等等;
 

計時器

base::Timer 實際上相當於 MessageLoop::PostDelayedTask 的封裝,對外提供了一次性或者不斷重復的計時器功能。Timer 的構造函數里面 retain_user_task 的含義是,當 Timer 被 Stop 的時候,關聯的任務是否被保留,默認值為 true,也就是保留而不置空。跟 WTF 里面的 Timer 一樣,base::Timer 是有線程歸屬性的,它屬於調用 Start 或者 Reset 方法的線程,設置的任務也在這個線程里面執行。

 
  1. base::Timer timer(false, false);
  2. EXPECT_FALSE(timer.IsRunning());
  3. timer.Start(FROM_HERE, TimeDelta::FromDays(1),
  4. base::Bind(&TimerTestCallback));
  5. EXPECT_TRUE(timer.IsRunning());
  6. timer.Stop();
  7. EXPECT_FALSE(timer.IsRunning());
  8. EXPECT_TRUE(timer.user_task().is_null());

base::ElapsedTimer 提供一個簡單的方法給程序計算某些操作的耗時。

 

日志和調試

 

日志輸出

Chromeium 提供了 LOG,DLOG,VLOG 幾種輸出日志的方式,類似下面這樣的代碼:

 
  1. LOG(INFO) << "Found " << num_cookies << " cookies";
  2. LOG_IF(INFO, num_cookies > 10) << "Got lots of cookies";

INFO 是輸出日志的級別,一共包括 INFO,WARNING,ERROR 和 FATAL 這四種,其中 FATAL 會在日志輸出后自動引發一個崩潰。LOG_IF 提供了額外的條件判斷,條件成立時才輸出日志。DLOG 跟 LOG 的區別是 DLOG 只在 DEBUG 版本才生效,而 VLOG 跟 LOG 的區別是可以用 verbose 級別來控制是否生效,比如:

 
  1. VLOG(1) << "I'm printed when you run the program with --v=1 or more";
  2. VLOG(2) << "I'm printed when you run the program with --v=2 or more";

當啟動開關 --v=1 時 VLOG 1 以上的級別生效。

 

調用跟蹤

Chromium 提供了強大的 Tracing 機制,在 Android 上也對接了 Android Systrace 機制,所以對我們來說,最簡單的方式就打開 Chromium Tracing,然后通過 Android Systrace 捕捉跟蹤的輸出。

在 TestShell 里面,我們可以通過設置 BrowserActivity.ENABLE_ATRACE 開啟 Chromium Tracing,或者通過菜單開啟,然后調用 Android systrace 命令捕捉即可。

如果要增加跟蹤的方法,最簡單的方式是使用如下代碼:

 
  1. TRACE_EVENT0("android_webview", "BrowserViewRenderer::OnDrawHardware");

更復雜的跟蹤方式可以參考 trace_event.h 里面的說明文檔。

 

調用堆棧

base::debug::StackTrace 提供了調用堆棧打印的功能,StackTrace 會在被構造的時候存儲當前的調用堆棧數據,然后可以通過它直接打印到控制台或者獲取相應的文本,在 Android 上是通過 logcat 輸出 error 日志。一般來說 StackTrace 可以作為函數的臨時變量輸出當前函數的調用堆棧,也可以作為對象的成員變量記錄對象創建時的調用堆棧。StackTrace 輸出的是地址信息,還需要使用符號表和對應的工具翻譯成可讀的函數名字。

下面是一個簡單的使用例程和輸出的結果:

 
  1. void AwContents::Destroy(JNIEnv* env, jobject obj) {
  2. base::debug::StackTrace().Print();
  3. ...
  4. }
  5. #00 0x751c38a1 /data/app-lib/com.uc.webkit.test-1/libwebviewuc.so+0x001f08a1
  6. #01 0x4153f30f /system/lib/libdvm.so+0x0001d30f
 

系統監控

 

內存監控

Chromium 提供了 MemoryPressureListener 接口,在 Android 上實際對接了 ComponentCallbacks2 的 onTrimMemory 和 onLowMemory(MemoryPressureListener.java)。

使用方式如下:

 
  1. void OnMemoryPressure(MemoryPressureLevel memory_pressure_level) {
  2. ...
  3. }
  4. // Start listening.
  5. MemoryPressureListener* my_listener =
  6. new MemoryPressureListener(base::Bind(&OnMemoryPressure));
  7. ...
  8. // Stop listening.
  9. delete my_listener;
  • 創建 MemoryPressureListener 對象,並傳入一個 Callback 對象,啟動監聽;
  • 銷毀 MemoryPressureListener 停止監聽;
  • Callback 對象會在創建 MemoryPressureListener 的線程被調用,調用是異步的,通過線程消息,即使這個線程就是 Android 的 UI 線程;
  • MemoryPressureLevel 包括 MEMORY_PRESSURE_MODERATE 和 MEMORY_PRESSURE_CRITICAL,跟 Android ComponentCallbacks2.onTrimMemory 和 onLowMemory 的對應關系如下面代碼所示;
 
  1. // Modules are advised to free buffers that are cheap to re-allocate and not
  2. // immediately needed.
  3. DEFINE_MEMORY_PRESSURE_LEVEL(MEMORY_PRESSURE_MODERATE, 0)
  4. // At this level, modules are advised to free all possible memory.
  5. // The alternative is to be killed by the system, which means all memory will
  6. // have to be re-created, plus the cost of a cold start.
  7. DEFINE_MEMORY_PRESSURE_LEVEL(MEMORY_PRESSURE_CRITICAL, 2)
 
  1. public void onTrimMemory(int level) {
  2. maybeNotifyMemoryPresure(level);
  3. }
  4. public void onLowMemory() {
  5. nativeOnMemoryPressure(MemoryPressureLevelList.MEMORY_PRESSURE_CRITICAL);
  6. }
  7. public static void maybeNotifyMemoryPresure(int level) {
  8. if (level >= ComponentCallbacks2.TRIM_MEMORY_COMPLETE) {
  9. nativeOnMemoryPressure(MemoryPressureLevelList.MEMORY_PRESSURE_CRITICAL);
  10. } else if (level >= ComponentCallbacks2.TRIM_MEMORY_BACKGROUND ||
  11. level == ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL) {
  12. // Don't notifiy on TRIM_MEMORY_UI_HIDDEN, since this class only
  13. // dispatches actionable memory pressure signals to native.
  14. nativeOnMemoryPressure(MemoryPressureLevelList.MEMORY_PRESSURE_MODERATE);
  15. }
  16. }
 

Android 相關

base::BuildInfo 基本上等同於 Android 的 android.os.Build 的 Native 版本,可以通過它獲得一些 Build 相關的信息,比如系統版本號等。

path_utils.h 提供一些輔助函數,用於獲取 Android 系統或者應用相關的特定目錄,比如 GetDataDirectory 返回當前應用的 Data 目錄。

jni_string.h 提供了一些跟字串相關的輔助函數,用於 Java String 和 Native String 之間的轉換,比如 ConvertJavaStringToUTF8,ConvertUTF8ToJavaString 等。

jni_array.h 提供了一些跟數組相關的輔助函數,比如 ToJavaXXXArray 將一個 Native 數組轉換成一個 Java 數組對象,轉換過程中原始的數據會被拷貝。

 

ScopedJavaLocalRef, ScopedJavaGlobalRef,JavaObjectWeakGlobalRef

base::android::ScopedJavaLocalRef 和 base::android::ScopedJavaGlobalRef 提供了在 Native 端持有一個 Java 對象,並在 Scoped 對象被銷毀時自動解除該 Java 對象引用的機制,有點類似是針對 Java 對象的 scoped_refptr。

ScopedJavaLocalRef 對應 JNI 的 LocalRef,作為棧對象在函數內部使用,一般用於在函數結束時自動解除關聯的 Java 對象的引用,或者作為函數的返回值傳遞 Java 對象的引用給它的調用者:

 
  1. bool GetDatabaseDirectory(FilePath* result) {
  2. JNIEnv* env = AttachCurrentThread();
  3. ScopedJavaLocalRef<jstring> path =
  4. Java_PathUtils_getDatabaseDirectory(env, GetApplicationContext());
  5. FilePath data_path(ConvertJavaStringToUTF8(path));
  6. *result = data_path;
  7. return true;
  8. }

ScopedJavaGlobalRef 對應 JNI 的 GlobalRef,一般作為類的成員變量,或者在需要超過某個函數的調用生命周期去持有一個 Java 對象的狀況下使用。

下面的例子演示了一個異步回調的處理,我們需要一個 ScopedJavaGlobalRef 保證這個關聯的 Java 對象在回調函數被真正執行時任然存活而不會被銷毀,base::Owened 將 j_callback 的擁有權轉移給 base::Bind 創建的 Callback 對象。

 
  1. void GenerateMHTMLCallback(ScopedJavaGlobalRef<jobject>* callback,
  2. const base::FilePath& path, int64 size) {
  3. JNIEnv* env = AttachCurrentThread();
  4. // Android files are UTF8, so the path conversion below is safe.
  5. Java_AwContents_generateMHTMLCallback(
  6. env,
  7. ConvertUTF8ToJavaString(env, path.AsUTF8Unsafe()).obj(),
  8. size, callback->obj());
  9. }
  10. } // namespace
  11. void AwContents::GenerateMHTML(JNIEnv* env, jobject obj,
  12. jstring jpath, jobject callback) {
  13. DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  14. ScopedJavaGlobalRef<jobject>* j_callback = new ScopedJavaGlobalRef<jobject>();
  15. j_callback->Reset(env, callback);
  16. base::FilePath target_path(ConvertJavaStringToUTF8(env, jpath));
  17. web_contents_->GenerateMHTML(
  18. target_path,
  19. base::Bind(&GenerateMHTMLCallback, base::Owned(j_callback), target_path));
  20. }

base::JavaObjectWeakGlobalRef 用於持有一個 Java 對象的弱引用,對應 JNI 的 WeakGlobalRef,當需要使用這個 Java 對象時可以通過 JavaObjectWeakGlobalRef.get 返回一個 ScopedJavaLocalRef。

 

工具類型

這一節的內容包括一些比較零散,無法歸類的工具類型。

 

SupportsUserData

base::SupportsUserData 是用來給一個對象增加 UserData 支持的輔助類,User Data,也叫 Client Data,一般使用 Key-Value 的方式存儲,是這個對象的使用者將自己或者其它的使用者需要用到的一些數據附加在這個對象上面的一種機制,對象本身只是作為這些數據的一個載體。

需要承載 User Data 的類,需要繼承 SupportsUserData,比如 content::WebContents,而需要作為 User Data 存儲的類型,需要繼承 SupportsUserData::Data,使用 void* 指針做 key。SupportsUserData 是非線程安全的,如果跨線程使用,需要使用者自己保證線程安全。

 

LazyInstance

LazyInstance 提供了一種延遲創建全局靜態對象的方式,它的優點是:

  • 它預先在程序的靜態內存區分配了對象的內存,當對象創建時就不需要在堆上分配內存,加快了對象創建的速度和減少堆內存碎片;
  • 它的對象創建是線程安全的,不用擔心多線程競爭的狀況;
  • 它延遲對象的創建到第一次使用的時候,避免在程序啟動時創建,減少了啟動的時間開銷;

總的來說 LazyInstance 就像是函數內部的靜態對象的線程安全版本,下面是使用的例程:

 
  1. static LazyInstance<MyClass> my_instance = LAZY_INSTANCE_INITIALIZER;
  2. void SomeMethod() {
  3. my_instance.Get().SomeMethod(); // MyClass::SomeMethod()
  4. MyClass* ptr = my_instance.Pointer();
  5. ptr->DoDoDo(); // MyClass::DoDoDo
  6. }

如果明確不需要銷毀對象,不需要調用析構函數,可以使用 Leaky 類型定義(實際上在 CAW 上,用不用 Leaky 都一樣,參看下面的 Singleton):

 
  1. base::LazyInstance<GlobalTileManager>::Leaky g_tile_manager =
  2. LAZY_INSTANCE_INITIALIZER;
 

Singleton

一般方便自己的類型實現單例模式的輔助類,使用的例程如下:

 
  1. // In your header:
  2. template <typename T> struct DefaultSingletonTraits;
  3. class FooClass {
  4. public:
  5. static FooClass* GetInstance();
  6. void Bar() { ... }
  7. private:
  8. FooClass() { ... }
  9. friend struct DefaultSingletonTraits<FooClass>;
  10. DISALLOW_COPY_AND_ASSIGN(FooClass);
  11. };
  12. // In your source file:
  13. FooClass* FooClass::GetInstance() {
  14. return Singleton<FooClass>::get();
  15. }
  16. // And to call methods on FooClass:
  17. FooClass::GetInstance()->Bar();

需要注意的是:

  • Singleton::get() 的調用方法必須命名為 GetInstance;
  • GetInstance 不能是 inline 的,也就是說它的實現不能放在頭文件里面;
  • Singleton::get() 有一定的時間開銷,避免在循環的 inner loop 里面每次都調用;
  • 對於 CAW 來說,使用 Singleton 的類型的析構函數是不會被自動調用的,對於 Chrome for Android 來說,在子進程退出時,使用 Singleton 的類型的析構函數在進程退出時被自動被調用,另外 LazyInstance 的狀況也一樣;

總的來說,Chromium 並不鼓勵使用單例模式,所以能不用還是不用。

 

AutoReset

base::AutoReset 是一個很簡單的輔助類,它一般作為棧對象使用,用途是構造時保存變量原有的值並設置新的值,當生命周期結束,析構的時候恢復變量原有的值。

 
  1. {
  2. base::AutoReset<bool> frame_resetter(&viewport_clip_valid_for_dcheck_,
  3. true);
  4. layer_tree_host_->SetNeedsRedrawRect(clip_);
  5. layer_tree_host_->Composite(gfx::FrameTime::Now());
  6. }
 

ObserverList,ObserverListThreadSafe

base::ObserverList 是幫助實現觀察者模式的一個輔助類,顧名思義,它提供了一個觀察者列表容器。除此以外,使用 ObserverList 而不是直接使用 std::vector 或者 std::list 的原因還在於 ObserverList 提供了一個特定版本的迭代器實現,在迭代的過程中從容器中刪除自己或者其它的 Observer 是安全的,迭代器的 GetNext 方法會自動檢查容器是否被修改過,正確返回修改過后的容器的下一個元素。

一般的使用方式如下:

 
  1. class MyWidget {
  2. public:
  3. ...
  4. class Observer {
  5. public:
  6. virtual void OnFoo(MyWidget* w) = 0;
  7. virtual void OnBar(MyWidget* w, int x, int y) = 0;
  8. };
  9. void AddObserver(Observer* obs) {
  10. observer_list_.AddObserver(obs);
  11. }
  12. void RemoveObserver(Observer* obs) {
  13. observer_list_.RemoveObserver(obs);
  14. }
  15. void NotifyFoo() {
  16. FOR_EACH_OBSERVER(Observer, observer_list_, OnFoo(this));
  17. }
  18. void NotifyBar(int x, int y) {
  19. FOR_EACH_OBSERVER(Observer, observer_list_, OnBar(this, x, y));
  20. }
  21. private:
  22. ObserverList<Observer> observer_list_;
  23. };

base::ObserverListThreadSafe 相當於 base::ObserverList 的線程安全版本,通過 ObserverListThreadSafe.Notify 可以調用注冊的 Observer 的某一個指定的方法,並且這個方法是在這個 Observer 所屬的線程上被調用,所謂 Observer 所屬的線程就是指將 Observer 加入到 ObserverListThreadSafe 里面的那個調用線程。為了做到上述這一點,ObserverListThreadSafe 是通過 PostTask 到線程的消息循環來實現的,這也意味着跟 ObserverList 不同的是,Notify 和 Callback 被調用是異步的,而 ObserverList 是同步的,MemoryPressureListener 的內部實現就使用了 ObserverListThreadSafe。

 
  1. MemoryPressureListener::MemoryPressureListener(
  2. const MemoryPressureListener::MemoryPressureCallback& callback)
  3. : callback_(callback) {
  4. g_observers.Get().AddObserver(this);
  5. }
  6. MemoryPressureListener::~MemoryPressureListener() {
  7. g_observers.Get().RemoveObserver(this);
  8. }
  9. void MemoryPressureListener::Notify(MemoryPressureLevel memory_pressure_level) {
  10. callback_.Run(memory_pressure_level);
  11. }
  12. // static
  13. void MemoryPressureListener::NotifyMemoryPressure(
  14. MemoryPressureLevel memory_pressure_level) {
  15. TRACE_EVENT1("memory", "MemoryPressureListener::NotifyMemoryPressure",
  16. "level", memory_pressure_level);
  17. g_observers.Get().Notify(&MemoryPressureListener::Notify,
  18. memory_pressure_level);
  19. }

wtf::String to std::string

  std::string Ascii() const WARN_UNUSED_RESULT;
  std::string Latin1() const WARN_UNUSED_RESULT;
  std::string Utf8(UTF8ConversionMode = kLenientUTF8Conversion) const

 

sdt::string to wtf::String

 std::string output{"aaa"};
wtf::String skp(output.data(), output.size());

 

 


 

1.KURL:是WTF::String

 char * url= KURL.string().utf8().data(); 

And how does it come out?

    a KURL is defined in WebCore/Platform/KURL.h, it has a member function string()

    b KURL.string(), return a class of String, which was defined in wtf/text/WTFString.h, it has a member function utf8()

    c String.utf8() return a class of CString , which was defined in wtf/text/CString.h, it has a member function data().

    d CString.data() return the type of char* , which can be printed directly.

 

to be continued 

2. String in WTF 
a. StringImplBase 
defined in "WTF/text/StringImplBase.h" 
it has no concrete implementation 
b.StringImpl 
defined in "WTF/text/StringImpl.h" 
StringImplBase<--StringImpl 
it hold a member UChar (wchar_t) //16 or 32 bit. 
c.String 
defined in "WTF/text/WTFString.h" 
String hold a member of "StringImpl". 
it has function to return it's data in CString 
  1.  
    CString ascii() const;
  2.  
    CString latin1() const;
  3.  
    CString utf8(bool strict = false) const;
d. CString 
was defined in "WTF/text/CString.h" 
it has a function "data()" to return the type of "char*". 

how is the UChar come to char * 
the most imporant function was String.utf8() in "WTF/text/WTFString.h" 
using "convertUTF16ToUTF8". //so UChar is 16 bit. 
which was defined in "WTF/unicode/utf8.cpp" 
  1.  
    ConversionResult convertUTF16ToUTF8(
  2.  
    const UChar** sourceStart, const UChar* sourceEnd,
  3.  
    char** targetStart, char* targetEnd, bool strict)
  4.  
    {
  5.  
    ConversionResult result = conversionOK;
  6.  
    const UChar* source = *sourceStart;
  7.  
    char* target = *targetStart;
  8.  
    while (source < sourceEnd) {
  9.  
    UChar32 ch;
  10.  
    unsigned short bytesToWrite = 0;
  11.  
    const UChar32 byteMask = 0xBF;
  12.  
    const UChar32 byteMark = 0x80;
  13.  
    const UChar* oldSource = source; // In case we have to back up because of
  14.  
    //target overflow.
  15.  
    ch = static_cast<unsigned short>(*source++);
  16.  
    // If we have a surrogate pair, convert to UChar32 first.
  17.  
    if (ch >= 0xD800 && ch <= 0xDBFF) {
  18.  
    // If the 16 bits following the high surrogate are in the source buffer...
  19.  
    if (source < sourceEnd) {
  20.  
    UChar32 ch2 = static_cast<unsigned short>(*source);
  21.  
    // If it's a low surrogate, convert to UChar32.
  22.  
    if (ch2 >= 0xDC00 && ch2 <= 0xDFFF) {
  23.  
    ch = ((ch - 0xD800) << 10) + (ch2 - 0xDC00) + 0x0010000;
  24.  
    ++source;
  25.  
    } else if (strict) { // it's an unpaired high surrogate
  26.  
    --source; // return to the illegal value itself
  27.  
    result = sourceIllegal;
  28.  
    break;
  29.  
    }
  30.  
    } else { // We don't have the 16 bits following the high surrogate.
  31.  
    --source; // return to the high surrogate
  32.  
    result = sourceExhausted;
  33.  
    break;
  34.  
    }
  35.  
    } else if (strict) {
  36.  
    // UTF-16 surrogate values are illegal in UTF-32
  37.  
    if (ch >= 0xDC00 && ch <= 0xDFFF) {
  38.  
    --source; // return to the illegal value itself
  39.  
    result = sourceIllegal;
  40.  
    break;
  41.  
    }
  42.  
    }
  43.  
    // Figure out how many bytes the result will require
  44.  
    if (ch < (UChar32)0x80) {
  45.  
    bytesToWrite = 1;
  46.  
    } else if (ch < (UChar32)0x800) {
  47.  
    bytesToWrite = 2;
  48.  
    } else if (ch < (UChar32)0x10000) {
  49.  
    bytesToWrite = 3;
  50.  
    } else if (ch < (UChar32)0x110000) {
  51.  
    bytesToWrite = 4;
  52.  
    } else {
  53.  
    bytesToWrite = 3;
  54.  
    ch = 0xFFFD;
  55.  
    }
  56.  
     
  57.  
    target += bytesToWrite;
  58.  
    if (target > targetEnd) {
  59.  
    source = oldSource; // Back up source pointer!
  60.  
    target -= bytesToWrite;
  61.  
    result = targetExhausted;
  62.  
    break;
  63.  
    }
  64.  
    switch (bytesToWrite) { // note: everything falls through.
  65.  
    case 4: *--target = (char)((ch | byteMark) & byteMask); ch >>= 6;
  66.  
    case 3: *--target = (char)((ch | byteMark) & byteMask); ch >>= 6;
  67.  
    case 2: *--target = (char)((ch | byteMark) & byteMask); ch >>= 6;
  68.  
    case 1: *--target = (char)(ch | firstByteMark[bytesToWrite]);
  69.  
    }
  70.  
    target += bytesToWrite;
  71.  
    }
  72.  
    *sourceStart = source;
  73.  
    *targetStart = target;
  74.  
    return result;
  75.  
    }

so the most common type used in webcore is "UChar", 
the most common type we use to print is "char*" 
the most common way for the conversion is "UChar->utf8->char*". 


definition 
  1.  
    ConversionResult convertUTF8ToUTF16(
  2.  
    const char** sourceStart, const char* sourceEnd,
  3.  
    UChar** targetStart, UChar* targetEnd, bool strict = true);
  4.  
     
  5.  
    ConversionResult convertUTF16ToUTF8(
  6.  
    const UChar** sourceStart, const UChar* sourceEnd,
  7.  
    char** targetStart, char* targetEnd, bool strict = true);
example: 
  1.  
    ConversionResult result = convertUTF16ToUTF8(&characters, characters + length,
  2.  
    &buffer, buffer + bufferVector.size(), strict);


for 2.3 
  1.  
    #include "PlatformString.h"
  2.  
    #include "CString.h"
to include the head file 
 
 


免責聲明!

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



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