在StackOverFlow看到的一個範例。
1 | struct Functor { |
同樣的code,在gcc和clang編譯失敗,不過VC可以,同樣根據StackOvewflow的說法,這是VC的一個非標準Extension。
要模擬這個方案,可以靠function overloading來做。
1 | template <typename T> |
在StackOverFlow看到的一個範例。
1 | struct Functor { |
同樣的code,在gcc和clang編譯失敗,不過VC可以,同樣根據StackOvewflow的說法,這是VC的一個非標準Extension。
要模擬這個方案,可以靠function overloading來做。
1 | template <typename T> |
找了一下在Linux底下寫OpenGL程式的方法,覺得有必要繞過glut,直接跟XWindow打交道,才能拿到更大的控制權。
終於找到一個可以用的骨架。
1 | #include <X11/Xlib.h> |
需要連結X11 library才能順利成功
1 | $ g++ demo.cpp -o demo -lX11 |
不過奇怪的是當關閉視窗時,總會有以下的錯誤訊息出現
XIO: fatal IO error 11 (Resource temporarily unavailable) on X server “:0.0”
after 12 requests (10 known processed) with 0 events remaining.
Success
Google了一下也沒什麼正確的解法,就不管他。
程式的骨架跟Win32的架構很像,因此不會太難了解。
在CodeProject看到這篇文章之後,參考其他文章而發表的。
這個Extension只支援GNU C,Clang或者VC++都不行。
這種技術有其名稱,叫做trampoline。
這個Hack可模擬Javasciprt等語言使用Closure的特性
1 | typedef int (*func_t)(int); |
原文提供了更變態的使用方式,不過很難閱讀。
1 | int main(int argc, char *argv[]) { |
在Linux常常遇到這種情況,記錄一下。
使用sudo
執行指令的時候,常常會xxx is not in the sudoers file. This incident will be reported.
表示你的用戶沒有權限使用sudo,必須修改/etc/sudoers
這個文件,修改方式如下。
進入Super user mode
1 | $ su - |
修改文件權限
1 | $ chmod u+x /etc/sudoers |
修改/etc/sudoers
內容,在root ALL=(ALL) ALL底下加上一行且存檔。
1 | xxx ALL=(ALL) ALL |
這裡的xxx就是你的用戶名稱
回復文件權限
1 | $ chmod u-x /etc/sudoers |
最近在看Dynamic programming language的時候,注意到了這個特性。
根據Wiki給的定義
When I see a bird that walks like a duck and swims like a duck and quacks like a duck, I call that bird a duck.
一般OOP的觀念,就是定義一組Interface為公約數。不受Interface的約束,而動態語言是在執行時檢查是否滿足所需條件。
以下是Ruby的範例
1 | class Duck |
動態語言的特性,就是在執行的時刻檢查類別的完整性,如果把上面Person的feathers拿掉,也必須在執行時才會發現錯誤。
如果是傳統競泰語言的話,如何顯示這種特性
C++有兩種方式。
一者是上面介紹的Interface contract
1 | class IAnimal { |
這種方式可以在Runtime的時候傳入某個滿足此Contract的物件,因此改變其行為。
不過缺點也是顯而易見的,一旦介面修改之後,所有有關的程式碼都要更著修改。所以一般的建議就是一旦介面固定之後就不要更改。
因此衍生了第二種方法,Template。
1 | class Duck { |
C++的Template會在C編譯時檢查所有約束條件,因此叫做Static Polymorphism
,跟用Interface的Dynamic Polymorphism
不同,Runtime沒有擴展性。
這次介紹另一個C++11重要的特性Variadic templates
相信每個寫程式的人,就算沒用過printf,也聽過printf的名字,printf的徒子徒孫大概跟Unix的子孫一樣多。而一般的printf使用方式就類似如此。
1 | int my_printf(const char * format, ...); |
在設計一個通用函數的時候,無法知道後面參數有多少個,因此需要一個支持不定參數的機制。
從上面的程式碼看出,我們支援不定參數的語法就是...
來表示。
而在C語言如何實做這樣的機制,可以參考MSDN上的範例。
在C99標準裡,Macro支持不定參數,不過Visual Studio至今不支援C99。
我們可以寫類似這樣的Macro
1 | #define dprintf(enable, ...) dprintf_impl(__FILE__, __LINE__, enable, __VA_ARGS__) |
在參數列的最後面寫 …,然後就可以用 VA_ARGS 代表 … 所傳入的參數。
在實作Command Design Pattern的時候,常常需要把外部函數的參數原封不動的傳遞至內部函數,解決方法大概就像這樣。
1 | #define P1 typename T1 |
這方案的缺點大概有以下幾點
來個最簡單的範例
1 | template <typename... Args> |
在Args左邊出現...
時,表示Args是一個Template type parameter pack,如上面的最後一行,T就是double,而Args就是int, string,除了類別之外,非類別的template paramter也可以這樣使用。如下
1 | template <unsigned ...dims> |
而Function template也可以像Class Template一樣使用不定參數
1 | template <typename ...Args> |
這裡的Args不是Type,args也不是一個value,所以以下的程式碼會出問題
1 | typedef Args MyList; |
而sizeof也跟著Variadic templates而新增新特性,sizeof...
可以印出Args到底有多少個參數
1 | template <typename... Args> |
用新的特性同時解決perfect forwarding跟Variadic templates
1 | template <typename ...Ts> |
透過Template specification來實作
1 | template <typename T> |
還有其他未介紹到的特性,基於所知有限<無法完全說明,可以參考Variadic Templates (Revision 3) Draft跟Variadic Templates are Funadic
前一陣子都在忙C++ Grandmaster的比賽,沒什麼時間紀錄一些東西,最近對Functional Programming感到興趣,找Haskell當作的入門的開始。做些紀錄。
以Ubuntu為例,先安裝Haskell。
1 | $ apt-get install ghc |
接著就能用ghci
進入直譯器了,用Ctrl+D
退出ghci。
有兩種方式可以達成
首先我們先來寫一個test.hs
文件
1 | $ cat > test.hs << EOF |
在ghci中載入test.hs
1 | $ghci |
這個方案是要加上 #!/usr/bin/runghc
且Script當中需要有main
存在
以下是個範例
1 | $ cat > hello.hs << EOF |
同樣的,這個方案也需要有main
存在
1 | $ ghc hello.hs -o hello |
網路上可以找到不少,先以這幾個當入門教材
這篇文章主要是Rvalue References: C++0x Features in VC10, Part 2的閱讀筆記,在原文撰寫過程當中,規格有所變動,因此根據最新的規則做補充說明。
C++98/03的時候,最令人詬病的問題,就是建立太多臨時物件,Value Semantics的意思就是複製出來的物件跟原先是獨立的,不會互相干擾
在C++98/03時期,有這麼一條規則Every C++ expression is either an lvalue or an rvalue.
,Lvalue是在運算過後留下來的續存物件,而Rvalue是運算過後生命期就結束的臨時物件。
殂此之外,C++98/03裡還有一條規則A function call is an lvalue if and only if the result type is a reference
1 | string one("one"); |
而在C++11之後,引進了Rvalue reference
,解決了這個問題。
每個reference都有一個名字,所以Bind到Rvalue的refernce,他是一個Lvalue,因此以下的程式碼。
1 | void print(const string&) { cout << "print(const string&)" << endl; } |
在RValueTest執行結束之前,str是個合法的物件,因此被當作Lvalue,會執行第一個print。
為了學習Rvalue的觀念,自行打造move跟forward函數。
先看C++11之後引進的Reference Collapsing Rules
而Move的用途就是明確指出不管物件是Lvalue或Rvalue,一律轉成Rvalue就是了。
而Forward的用途把外面的參數跟語意原封不動的傳進去內部,是Lvalue就是Lvalue,而Rvalue就是Rvalue。
先從比較簡單的Move開始看起,我門打造的第一版Move大概像這樣。
由於Move是個template function,必須進行Template Argument Deduction,此時引進了一條新的規則。
如果傳進來的是Lvalue的話,將會推導成T&,反之如果是Rvalue的話,就會推導成T(根據Reference Collapsing Rules,T和T&&都符合要求,為了解決歧異性,這邊強制要求推導成T)
1 | template <typename T> |
這邊先來決定如何傳遞參數,Call by value第一個被否決,接著就是T&
跟T&&
的選擇,根據前面的Reference Collapsing Rules,如果是T&
的話一律會摺疊成T&
,而T&無法繫結至modifiable rvalue,而如果是T&&
的話,不管Lvalue跟Rvalue都可以順利繫結。
接著我們來測試第一版的程式碼
1 | quark(Move(up)); |
印出的結果是
1 | t: up |
顯然結果錯了,原因在於實際參數是Lvalue的話,T會被推導成U&,而T&&的結果依然是U&,變成Move傳回去的語意是個Lvalue,因此導致上面的結果。
所以我們要做的,就是把U&或U&&一律轉成U&&,也就是std::remove_refernce存在的理由。改寫我們的程式,先用RemoveRefenece取得Primitive Type, 然後加上&&之後就可以得到正確的值。
1 | template <typename T> |
gcc的實作就類似於這樣。
接著來討論forward該怎麼做,從上面我們可以知道,我們只能用T&&來傳遞參數。
先來一組helper function來驗證程式的正確性。
1 | void inner(std::string &str) |
而我們第一版的Forward的實作
1 | template <typename T> |
輸出結果則是
1 | inner(std::string&& str) |
從上面知道,value是個左值,所以Type是U&,T被推導成U,T&&被強制轉換成右值,所以輸出的結果如上。避免的方法就是強迫加上template參數。
1 | template <typename T> |
重新執行程式,這下結果符合我們的需求了
1 | inner(std::string &str) |
這邊還有幾點要說明的
之前的範例,value是左值,而T可能是U&或是U,T&的結果是U&,可以繫結住左值沒有問題。萬一Forward的參數是個右值怎麼辦?
1 | inner(Forward<T>(string())); |
我們需要另外一個function,解決function resolution的問題。
1 | template <typename T> |
在傳進來的是個右值時,T被推導成U。T&&正好可以綁定一個Rvalue,解決上面的問題。不過問題又來了,如果是左值的話,T是U&,T&是U&,T&&還是U&,變成兩個function擁有兩個一模一樣的參數型態,Compiler不知道該選哪個。
解決方案就是套用上面Move所引進的RemoveReference,還原成Primitive Type。
1 | template <typename T> |
這樣子有另外一個好處,這個方案禁止了型別推導,不會再有Forward(value)
的存在,編譯時期就能指出錯誤。
C++0x引進了Lambda Expression
,這功能其實也不新鮮了,在其他語言都有類似的觀念。
為了處理Callback function,做了很多的改進
同樣的,我們從C語言時期看起。
C語言只有一種方式處理callback function,function pointer。
像這個樣子
1 | int add2(int a, int b) { return a + b; } |
Function Pointer的方式處理Callback function非常經典,幾乎可以在所有純C語言開發的程式碼看到他。不過還是有可以改進的地方,例如
C++的Functor(仿凾式)就是因此而生的,透過overload operator()
,來改進上述所無法缺憾。搭配template的使用,使其應用更具彈性。可以同時支援Callback function跟Functor兩種形式。
1 | struct Add2 { |
之後有人想要將class member method跟static class method也包裝成Functor的形式,有Loki
跟Boost
兩大主流為主。這裡就不多談了,在C++0x中也將boost的bind跟function列入TR1裡,可以很方便的產生Functor。
Functor雖好,不過每要新增一個新功能,就要寫一個function object,如果是只被使用一次的functor,維護這段程式碼需要付出成本,
因此就產生了一個暱名函數(anonymous function),像這樣。
1 | for_each(v.begin(), v.end(), [](int n) { |
詳細的語言規範可以參考MSDN的資料。透過跟程式碼上下文互動,達到非常有趣的效果。
之前我們介紹過unique_ptr,用來管理動態記憶體,非memory的Resource該怎麼處理,此時就是ScopeGuard登場的時候
1 | class ScopeGuard |
或者將不同function signature,由於要經過相同的處理流程,於是用lambda expression隱藏了細節,以下是DirectX ToolKit的部分程式碼。
1 | template<typename T, typename TCreateFunc> |
可以看得出來,DemandCreate處理的流程相同,但是船進去的type跟Callback function不同,就能達到一般化的效果,這也是我目前看到最有趣的例子。
在C語言中,最常遇到的情況就是忘了釋放記憶體,然後造成Memory Leak的問題。
例如以下這段程式
1 | void LeakDemo(char path1, char path2) |
在上面這段程式裡面,path2就忘了釋放記憶體,然後造成Leak。
這是語言上的侷限,只能靠多檢查source code跟使用工具來減少這種問題。
由於C++有RAII idiom之後,對於釋放記憶體的事情就變得簡單很多了。
1 | struct StackObject { |
而為了一般化,STL裡面時做了一個auto_ptr
,利用Template跟RAII的觀念來管理記憶體。
如果把auto_ptr侷限於上面的用法,不會遇上什麼問題,一旦要搭配現有的程式碼,當參數傳來傳去,問題就出現了其中最嚴重的問題莫過於
1 | void Test(auto_ptr<int> v) {} |
以上的程式碼,看起來沒什麼問題,不過實際執行就Crash了。
auto_ptr的問題是沒有Copy Semantics,他的Copy Constructur裡面做的是Move的動作。
以下是auto_ptr的Conpy Constructor實作簡化版
1 | template <typename _Tp> |
於是在執行完Test之後,main中的v裡面的pointer就被清空,無法正常使用。
由於這個緣故,STL的各種容器跟演算法,搭配上auto_ptr或多或少都有問題。在經過多次修改之後還是無法修復,於是auto_ptr在C++0x之後就標繼承deprecated了,不建議使用。
由於C++0x引進了Rvalue reference,因此新的unique_ptr就此登場。
unique_ptr能做的事幾乎跟auto_ptr一樣,除了少數例外。
1 | auto_ptr<int> a(new int); |
不過你也可以利用C++0x新增的Move Semantics,手動進行Move動作。
1 | unique_ptr<int> c(new int); |
靠著C++0x新增的Rvalue Reference,區分出Copy跟Move的差異。看著unique_ptr的實作,他只允許Move Constructor,而不允許Copy Constructor。
1 | template<class _Ty, class _Dx> |
如果要得到更多的Rvalue Refenece的相關內容,請參考這篇Rvalue References: C++0x Features in VC10, Part 2。
由於C++0x引進了Move Semantics,連帶的STL所有Container跟Algorithmer都支援Move Semantics了,因此這樣的程式碼就變得可行了。
1 | typedef unique_ptr<char> UniCharPtr; |
在這裡同樣用上了lambda expression,留待有空再寫。
有個手法稱作source and sink idiom
因為有了Move Semantics更容易的實現。
1 | unique_ptr<int> Source() { return unique_ptr<int>(new int); } |
除此之外,還能夠自訂Destructor。
1 | auto del = [](int *p) { |
以及透過partial specialization
來管理一個動態產生的Array。
1 | auto del = [](int p[]) { delete [] p; }; |