[讀書筆記]Universal References in C++11 part 6: 儘量避免overloading
本篇主要參考effective modern C++ : Item 17: Understand special member function generation. Item 26: Avoid overloading on universal references. Item 27: Familiarize yourself with alternatives to overloading on universal references. 本文開始 在理解完Universal Reference(UR)的強大之處後,應該有很多人躍躍欲試的想把這個概念導入到自己撰寫的程式碼裡面吧?不過這東西也不是沒有它的限制。由於template編譯期動態產生程式碼的特性,因此編譯器會針對傳入的參數,非常積極的去產生對應的程式碼。 雖然上一篇我們談到了該怎麼樣用Type Trait在編譯期防止使用者傳入非預期的參數型態,但那其實只是一種防呆機制,實際上的問題在於別處:若是我們試圖去overload 該function的話,那麼將會有高達八成的使用情境程式會呼叫到UR版本。為什麼會這樣呢?讓我們先來看看以下這個正常運作的例子: class Resource{ public: … Continued
一個由443 port所引起的爆肝血案
以下故事由真實故事改編,如有雷同純屬巧合。(?) 為保護當事人,以下將會做適度的改編。 本文開始 回想起來,距今大概也有幾年的時間了吧。 在產品經歷了無數次的alpha與beta測試,最後即將上線的前夕的時候曾經發生這麼樣的一個事件:這個事件造成數十個工程師與QA不眠不休的努力,經歷無數小時的苦悶掙扎、有歡笑有淚水(只有最後),最終才得以解脫的悲慘事件。如果當初早點知道443 port背後所代表的意義,或許我們這些苦命的攻城獅全部都可以早點回家洗洗睡了…… 首先,在這個事件當中主要有三個單位涉及其中:營運方RD人員、營運方QA人員與我們這些負責研發的攻城獅。 事件的難解有很大一部份的原因就在這裡,因為,我們完全沒有任何權限去調查與更動已佈署在營運方的任何程式碼與設定。 我們必須要憑空想像出所有可能的原因,並依賴營運方的QA人員進行對應的操作。 死線就在眼前。 原本的產品預定的上線時間就在BUG發生的前幾天;這個BUG所涉及的功能非常重要,可以說是整個產品的生命線。而這個功能不管在alpha或beta測試環境都非常完美的運作,可是,卻在實際要運作的上線環境發生問題。 到底發生了什麼事?我們猜想是營運方某個對應api的設定檔設置錯誤,然而對方也在懷疑是我們的程式寫錯……於是,鬼打牆的戲碼就這樣開始上演了。 三方溝通是一件非常耗費心神的事,想要確認任何的東西都需要透過大量的陳述,想辦法說服對方說確認這件事是有助於解決問題的;有時第一次的要求並不會很快的就得到回應,必須一而再、再而三的重複強調之後事情才有可能得到答覆。這期間的無力感是非常重的,因為必須要想辦法提出正確的問題與『問法』,才有辦法得到希望的答案。 我們不斷的比較沙箱環境與上線環境的log,最後發現了下面這個可疑的api: 沙箱環境:http://xxx-alpha.com/api 正式環境:http://xxx..com:443/api 為什麼正式環境要帶個443 port?而把這串api直接丟上瀏灠器上卻直接回應一個bad request(400)。至少該給我一個權限不足(403)之類的訊息吧?正式環境的網址是對的嗎?當我把這個疑問向營運方提出的時候,對方卻說那是正常的,內部機制會自動加上443,我們不用去關心細節。 自動加上443?為什麼要做這件事?有什麼意義嗎? 嗯……不過被回答不用去關心他們內部細節的話,當然我也無話可說,也只能乖乖的繼續尋找其他可能的線索。 就當大家在接近要放棄狀態,準備延後產品上線時間的時候,我們內部的一位伺服器專家突然冒出一句話:「一般443走https … Continued
[讀書筆記]Universal References in C++11 part 5: 使用標準庫中的Type Trait來檢查傳入型別錯誤
使用標準庫中的Type Trait來檢查傳入型別錯誤
[Linux學習日誌] OS中的記憶體快取
最近在玩cloudatcost賣的便宜vps主機,發現明明我什麼程式都沒開,結果記憶體卻用到了73%,如下圖: 查了一下,發現下面這篇文章寫的蠻好的: http://blogger.gtwang.org/2013/07/linux-cache-memory-linux.html 原來linux會把沒用到memory拿來當做memdisk來用阿……第一次知道有這種事。 另外發現自己不太懂buffer跟cache的差別,稍微查了一下: http://stackoverflow.com/questions/6345020/linux-memory-buffer-vs-cache 簡單來說: A buffer is something that has yet to be “written” to disk. A cache is something that has been “read” from the disk and stored for later use.
[讀書筆記]Universal References in C++11 part 4: Reference Collapsing, Perfect Forwarding and Universal Reference
Universal,這個字在其實是一種抽象的表達。任何抽象的表達,在下面大多會有一套自動化的機制,來將本該由使用者操作的複雜概念隱藏起來。Universal References可以說是一種抽象的介面,它提供了一個統一的窗口給lvalue reference以及rvalue reference。但是從上面幾篇文章我們不是才知道,將這二個reference的概念區分出來就是為了要減少暫存物件記憶體操作的成本嗎?現在說要把它們又合在一起不是本末倒置了嗎?──當然不會,這裡所說提供的統一窗口,是為了給在設計功能時,還不知道傳入物件型態的,需要在編譯期推導出來的情況下使用。 想想看,在什麼情況下會有這種情形?其實這種實作在標準函式中到處都是。應該呼之欲出了吧?答案是template。 template又稱為編譯期靜態多型,跟使用繼承方式的執型期動態多型的方式不同,它會在我們按下編譯的時候才將需要的程式碼產生出來。雖然介面上看起來只有一個,但實際上它在底下可是產生了lvalue reference跟rvalue reference二個版本。這種性質可以說是一種拯救;它讓我們程序猿們省下了不小維護程式碼的心力,因為,這樣我們就不需要對每個member variable製作二個不同的版本的setter。例如,若沒使用universal reference的程式碼會是下面的情況: class Widget { public: Widget(){}; void setName(const std::string& newName) { m_name = newName; } // const lvalue void setName(std::string&& newName) { m_name = std::move(newName); } // … Continued
[讀書筆記]Universal References in C++11 part 3: 記憶體管理以及Rule of Three(Five)
[insert page=’338′ display=’link’] 上一篇主要介紹了rvalue reference的概念,以及它導入用來解決無法區分lvalue reference以及暫存物件之間的copy問題。基本上這些操作的本質,其實也就是怎麼用更有效率、更簡單的方法去控管我們所使用的資源而已。──記憶體管理,一直是C++程式設計中一個非常重要的課題。 一個最常被提到的準則便是RAII的概念。何謂RAII?全名Resource Acquisition Is Initialisation,意即所有接下來這個物件所會用到的資源,在建構階段就已經全部建立完成。例如檔案的開啟、變數的初始化或記憶體配置。但這句話只說了一半,另外一個對應概念是Resource Release Is Destruction (RRID),意即在解構階段必須將所有要來的資源還回去。 自己new出來的要記得delete,之前開檔案要記得關。它是一種資源所有權管理的概念,每個物件要在自己的生命週期內管理自己擁有的資源;它也是一個設計的大原則,用來提醒程式設計師要記得將不要的資源釋放出去。不然帶來的後果,便是系統的不穩定或是直接崩潰。 可是我們該怎麼在C++中正確的實作RAII、RRID呢?只要在建構子跟解構子中做完對應的初始化以及釋放的動作就行了嗎?答案是,對,可是也不對。正確的解答應該要看我們對於這個類別在功能性上的設計與期待。若是該物件有將別的物件拷貝一份到自己身上的需求,則需要思考的是我們要進行的是deep copy、shallow copy還是將別人身上所管理的資源move到自己身上。嗯?這個不是上一篇所提到東西嗎?──沒錯,只是上一篇在程式碼的實作上並沒有符合我們對於一個類別在資源管理機制上的期待。一份完整的實作,必須要遵循 Rule of Three的設計準則:若是設計師有實作下列任何一個function,則其他的function都必須要有合適的實作: class Widget{ public: /** Copy constructor */ Widget(const Widget& other){ memberwiseCopy(other); } /** Destructor */ ~Widget(){} /** … Continued
[讀書筆記]Universal References in C++11 part 2: Move semantics, lvalue reference and rvalue reference
上一篇: [insert page=’309′ display=’link’] C++11以前,效能上最大的瓶頸就是程式執行過程中,會產生許多不必要的臨時物件,並進行許多昂貴的複制操作。從上一篇的文章中我們知道,C++11導入『T&&』語意來解決這個問題。或許有人會問,這個問題不是早就有相關機制能夠解決了嗎?將function的傳入參數指定為reference或pointer不是就能避掉無謂複制操作?例如下面實作的三種myCopyFunction,這個函式的主要用途是將傳入的參數copy給global 物件myBackupWidget: class Resource{ public: std::string m_data; }; class Widget{ public: Widget() : m_pHeapStorageResource(nullptr){ } public: Resource* m_pHeapStorageResource; Resource m_stackStorageResource; int m_i; }; static Widget g_myBackupWidget; void myCopyFunc1(Widget param){ g_myBackupWidget.m_pHeapStorageResource = param.m_pHeapStorageResource; g_myBackupWidget.m_stackStorageResource = param.m_stackStorageResource; g_myBackupWidget.m_i … Continued
[讀書筆記]Universal References in C++11 part 1: rvalue and lvalue
參考文章: C++ Rvalue References Explained By Thomas Becker Universal References in C++11 by Scott Meyers 寫在前面: 這個系列的程式碼與例子大多出自以上二篇文章,再加上自己本身的理解所描述而成,因此可能會有謬誤的地方還請見諒。若有什麼意見或想法,非常歡迎討論與指教。 本文開始 首先讓我們來看看下面這一段code: template <typename T> void f(T&& param); 我們知道T&指的pass by eference,但T&&到底是?pass by reference的reference?就語法的理解上,似乎有點不太明確的知道它到底在做什麼,因為C++並沒有這種概念。為了跳脫這個語法表示上理解的困境,我們並不需要糾結於「&」這個語法的意義上,「&&」這東西並不是字面上是pass by reference的reference,就把「&&」他當成新的語意來理解吧! 其實T&&這個語法,是在C++11的時候才導入的,它主要可以解決以下幾個問題: Implementing move semantics Perfect forwarding 這裡先還不用急著理解這二個問題到底是什麼,下面的文章將會入深入的討論。但我們只要先知道:「在解決這二個問題之後,在程式中傳遞參數將能夠省下許多記憶體copy的時間」ーー這件事就行了。簡單來說,就是你的程式會變得更快。它能夠保證讓任何type的參數傳入可以用pass … Continued
[UnrealEngine4] Plugin System學習筆記
在進行本文之前,推薦先閱讀以下官方文檔: https://wiki.unrealengine.com/An_Introduction_to_UE4_Plugins https://docs.unrealengine.com/latest/INT/Programming/Modules/API/index.html 本文開始: 對於UnrealEngine而言,其實Plugin也是歸於FModuleManager的管理之下。 跟一般Module不同的地方在於其多了${PluginName}.uplugin, 用來描述並管理這個Module的用途,一個簡單的Plugin的結構如下: 其中MyPlugin.uplugin中的檔案內容如下 我們可以看到,在這個MyPlugin之中我建立了3個Module,並在MyPlugin.uplugin之中將『MyPlugin』的Type設為Runtime、『MyDeveloperPlugin』設為Developer、『MyEditorPlugin』設為Editor。 其中Runtime會在任何情況下都會載入、Developer是除了Shipping的build之外都載入、 而Editor如其名,只會在編輯器模式下載入(通常是在做工具時使用)。 而LoadingPhase則是在描述該Module的載入時機點,若沒指明則為Default。如下圖,詳細行為請參考源碼: Engine/Source/Runtime/Projects/Public/ModuleDescriptor.h。 另外,值得注意的是一個跟Plugin同名的Module是必要的,不然該Plugin將會無法編譯。 (在上面的Case中我們可以看到MyPlugin/Source/下面有一個MyPlugin,再提醒一次,同名Module是必需的。) 在導入完Plugin之後,接著想會要在我們的遊戲中使用。 首先,必須要在我們專案的的Build.cs中加入以下指令,不然會找不到該module的標頭檔: 請注意,在使用任何外部Module之前,必須要先檢查該類別有沒有export給外部的module使用,在unreal engine中,是用以下的宣告型式: class ${UPPERCASE_MODULE_NAME}_API 下面則為本文中MyPlugin的例子: class MYPLUGIN_API FMyPlugin : public IModuleInterface class MYDEVELOPERPLUGIN_API FMyDeveloperPlugin : public IModuleInterface class MYEDITORPLUGIN_API FMyEditorPlugin : public … Continued
[C/C++]移除編譯依賴的小技巧
相信有不少人有這種經驗:如果所處理的是一個非常大型的專案,常常更改一些小東西就要等個10幾20分的編譯時間。那麼我們該怎麼處理這個問題呢? 以下內容見exceptional c++ item26 ~ item 28: // file x.h class X { // public and protected members private: struct XImpl;// a pointer to a forward-declared class XImpl* pimpl_; // a pointer to a forward-declared class }; // file x.cpp … Continued