2013年1月10日 星期四
C++ enum member function
現有的程式碼:
class PrimitiveKind {
enum kind {
VOID,
BOOL,
INT8, INT16, INT32, INT64,
FLOAT32, FLOAT64,
FUNCTION, OBJECT, ...
...
};
static bool isBool();
static bool isInterger();
static bool isFloat();
};
整理了一下使用到的情況,大概可以分成三種:
1. 判斷 isBool, isInterger, isFloat...
void f(PrimitiveKind::kind v) {
if(PrimitiveKind::isBool(v)) {
...
} else if (PrimitiveKind::isInteger(v)) {
...
}
...
}
2. enum 的值互相比較
PrimitiveKind::kind promote(PrimitiveKind::kind a, PrimitiveKind::kind b) {
if (a <= FLOAT64 && b <= FLOAT64) {
if( a > b ) return a;
else return b;
} else {
...
}
}
3. switch case
void g(PrimitiveKind::kind v) {
switch(v) {
case PrimitiveKind::VOID: ...
case PrimitiveKind::INT8: ...
case PrimitiveKind::INT16: ...
}
}
這個 class PrimitiveKind 其實沒有任何 data member,她的角色比較像是 namespace。只是把跟 PrimitiveKind 有關的宣告集合起來。
我希望能避開 PrimitiveKind::isBool(v) 這種呼叫 static member function 的寫法,直接寫 v.isBool()。
第一個想法,很直覺想到可以讓 PrimitiveKind 變成一個 wrapper class,持有一個 PrimitiveKind::kind,並且把 static member function isBool() 改成 non-static member function isBool()。這樣我就可以滿足第一個狀況,可以直接呼叫 isBool(), isInteger(), isFloat()。但是卻沒辦法滿足後兩種狀況。因為 wrapper class 沒有 <, >, =, != 等等的 operator,也無法放進 switch。
第二個想法,我幫 PrimitiveKind 加上 operator overloading,讓他支援一整組的 relational operator,就可以也滿足第二個情況。
但這個時候我想了想,我應該先列出所有我需要滿足的需求,再來回顧我的設計。整理了一下,我希望我的 wrapper class 滿足以下條件:
1. 有 non-static member function 可以使用
2. wrapper 之間可以互相 assign,也可以被 raw enum value 直接 assign
3. wrapper 之間可以互相比較,也可以和 raw enum value 之間互相比較
4. 可以使用在 switch 裡面
其實最好是可以取代所有需要用到 raw enum 的地方。
其實前三條都很容易滿足(雖然很煩),但最後一條才是關鍵,為了讓他可以被用在 switch 裡面,我想我需要 overload 一個 cast operator(對,C++ 可以自己定義 cast operator,很少用到,但其實有這種功能 http://www.learncpp.com/cpp-tutorial/910-overloading-typecasts/ )
operator PrimitiveKind::kind() const { return kind_; }
讓他可以被 implicit 轉成 PrimitiveKind::kind,後來回頭一想,其實我前面 overoad 的那十幾個 relactional operator 好像都可以省下來,測試了一下,發現確實可以運作。
以下是最後的完成品。
class PrimitiveKind {
enum kind { VOID, BOOLEAN, ... };
operator kind() const { return kind_ ; }
bool isBool();
bool isInteger();
bool isFloat();
kind kind_;
}
使用簡單,用起來就像是 enum,可以放進 switch,可以直接比較,還可以有 member function,也封裝了 INT8 等等的 symbol(在 C++11 可以用 strong typed enum 達成)。
2012年11月9日 星期五
boost, clang, libc++
首先,目前的 boost 有幾個 library 跟 clang 有相容性問題,編譯的時候可以先關掉。
- signals
- python
- iostreams
- mpi
sudo ./bootstrap.sh --with-toolset=clang cxxflags="-I/usr/local/include/c++/v1 -stdlib=libc++" linkflags="-stdlib=libc++" --without-libraries=signals,python,iostreams,mpi
來設定。設定完成以後,使用
sudo ./b2 toolset=clang cxxflags="-I/usr/local/include/c++/v1 -stdlib=libc++" linkflags="-stdlib=libc++" install
進行編譯跟安裝,對了,如果有需要的話,記得要用 debug build。
2012年9月19日 星期三
Software Transactional Memory
Software Transactional Memory
正在 survey STM,看了一下各語言的支援情形:- C# 透過外掛修飾 class,讓這個 class 所有的 data member 都是 private,所有的 setter/getter 都是 virtual,看起來是要讓某個 class template 來 mock 那個 class,但是 setter/getter 是 atomic 的。可以作到 C++ 的
- Java 也是一樣,透過 library 完成。
- Golang 應該是沒打算提供 transactional memory,根據這篇討論,go 提供 message passing 作為取代。
- C++ 有兩個 compiler 提供了 atomic block 的實作,一個是 Intel C++ compiler(真是太強了),另外一個是 g++(嘖嘖),大概最後還是選擇以類似 C++ 的形式為目標。
stackoverflow 上面有一篇討論,C# 沒有語言層面的支援,可以透過 library 來完成。
找到 gcc 的 transactional memory 的 spec,正在讀,作一點筆記。
__transaction_relaxed and __transaction_atomic
有兩個關鍵字:
__transaction_relaxed
用來表示........ 其實我沒看懂。好,看到後面,懂了。這種 transaction 比較 weak,他只保證 transaction 「看起來」沒有跟別的 transaction 有交錯執行,但是,他還是可能跟別 thread 有交錯。舉例來說的話:
- 其他的 transaction 無法觀察到 __transaction_relaxed 的 partial result
- 其他的 thread 有可能觀察到 __transaction_relaxed 的 partial result
- __transaction_relaxed 有可能觀察到其他 thread 的 partial result,有的時候你會想要等待某一個 std::atomic 變數變化來作事情,所以 relaxed transaction 有其必要性
- 沒有限制 operation 的種類,什麼都可以放進去(如果我沒誤解的話,第 8 頁 45 行)
- 無法 cancel,因為(見規格書第 9 頁第 16 行)容許有 side effect 的動作(比方說IO)而這些動作無法撤銷
- Relaxed transaction 可以 nested 含有 relaxed transaction
__transaction_atomic
用來表示(跟 __transaction_relaxed 比起來)更嚴格的的 transaction。這大概是我們要做的,但不確定前一種要不要作 :P
- 只有 "safe" code 可以放在裡面(等一下在 4.2 節說明)
- 可以被 cancel
- 不能有 side effect(比方說 IO)
| Thread A | Thread B |
__transaction_relaxed {
a = 1;
b = a;
}
| a = 3; |
a = 3; 亂入了。但是呢,如果寫:
| Thread A | Thread B |
__transaction_relaxed {
a = 1;
b = a;
}
| __transaction_relaxed {
a = 3;
}
|
這邊一直強調「看起來」是很重要的,因為實際上,根據實作的細節(且為了效率的考量),執行的過程本身可能還是交錯並行的,只是透演算法的控制,讓執行的結果跟沒有交錯一樣,所以程式設計師就可以很直覺的認定這段程式碼的形為就如同一般 single thread 一樣。
特性:
這邊現在我也看懂了,也就是說,在 atomic transaction 之下,即使只寫
| Thread A | Thread B |
__transaction_relaxed {
a = 1;
b = a;
}
| a = 3; |
特性:
transaction_safe
為了確保 transaction 是「可能」的,atomic block 裡面能寫的程式碼必須要加上一些限制,也就是 atomic block 裡面只能有 "safe" statements 。safe satements 所呼叫的 function 裡面,也只能含有 safe statements(這感覺很像 pure function ,一個 pure function 裡面只能呼叫 pure function)。為了表明一個 function 是 safe function,需要在函數前面加上 attribute
transaction_safe 修飾這個 function(safe 屬性在某些狀況下也有由 compiler 推導出來,見後續)。 transaction_unsafe 用來表明一個 function 不 safe,compiler 就可以阻止他被呼叫(比方說雖然現在這個 function safe,但是未來改版以後可能不 safe,我們知道他未來有不 safe 的可能性,所以我們希望現在就禁止他被使用)。
也可以直接修飾 class,這樣這個 class 所有的 member function 都會被修飾,然後每一個函數也可以各自修飾來 override safety。
__transaction_cancel
可以用 __transaction_cancel 放棄一個 transaction。__transaction_cancel 也可以跟 throw 一起使用,發出一個 cancel-and-throw,也會放棄 transaction。outer
transaction 可以 nested,也可以用 outer 修飾來阻止被 nested-inside。另外 outer 也可以用來修飾 throw,讓最外圍的 transaction 被 cancle。只有被 outer 修飾的 transaction 可以被這樣 cancel(目前還沒看到為什麼這麼限制的原因,但應該有原因,後面可能會講到),且 function 也需要標上 transaction_may_cancel_outer 來修飾,不然不能發出 outer throw。exception 被丟出 transaction,且不是使用 cancel-and-cancel 的話,transaction 有效!(這點跟 wiki 上面提到的不一樣,看樣子這個設計不是普世價值)
Safe and Unsafe
單位是 statement。safe statement 或是 unsafe statement。只有 safe 的 function 可以被呼叫,這些 function 叫做 transaction-safe function,另外一類叫做 transaction-unsafe function。一個 function safe 不 safe 是型別的一部分,所有寫到這個函數的地方都必須一致,不能有的地方 safe 有的地方不 safe。
你可以用一個 unsafe 的 function pointer 指到一個 safe 的 function。但是不能用一個 safe 的 function pointer 指到一個 unsafe 的 function(有點類似 const 的觀念)。
Lambda 也有 safe/unsafe。
如果一個 statement 有以下任何一個特性,那他就是 unsafe:- 他是一個 relaxed transaction
- 他是一個 outer atomic transaction
- 初始化一個 volatile 或 assign to 一個 volatile 或 read from 一個 volatile 變數
- 他是一個 unsafe asm,但是什麼 asm 是 unsafe 則是看 implementation dep
- 呼叫一個函數,但是這個函數不是 transaction-safe 或是 may-cancel-outer(It contains a function call to a function not known to have a transaction-safe or may-cancel-outer function type)到底在講什麼...
一般的 function 宣告,如果你沒有特別指定,他 implicit 根據裡面有沒有含有 unsafe 的 statement 來決定自己 implicit 宣告成 safe 還是 unsafe。但是「直接呼叫自己」的 recursive 函數是 unsafe(應該是說 compiler 會偵測 directly recursive call,但是 indirectly call 就不管),但是你也可以 explicitly 指定 recursive funciton 是 safe。
template function 一樣也會自動推導則,但是,是具現化以後才推導,會看內含 statement safe 不 safe,如果有 unsafe 的 statement,那就 implicit unsafe,如果全部的 statement 都是 safe,那就 implicit safe。
Ctro 跟 Dtor,user defined 的當然就是作一般 (template) function,compiler generated 的,就看 base class 跟 non-static data member 的 Ctor/Dtor 是不是 safe。
Transaction Expression
SomeType myObj = __transaction_atomic ( expr ); SomeType myObj = __transaction_relaxed ( expr );
作用於 expression 求值的過程。也就這個求值的過程,會是 trasactional 的。
transaction expression 不能 nested。也不允許用 outer 修飾,也不能含有 transaction statement,也不能含有 cancel,也不能含有 cancel-and-throw,因為 C++ 不允許 expression 含有 statement。接下來還有一堆超饒舌的,先跳過,看了實在好想吐 orz 18 頁第 1 行。
Function Transaction Blocks
void f() __transaction_relaxed {
}
void f() __transaction_atomic {
}
意思就是這個 function body 內部的執行,會是 transactional。
如果這個 function 剛好是 Ctor/Dtor 的話,那個 base class 跟 member class 的初始化也都是 transactional 的。下面一個例子,比方說這個 class 的每個物件都有一個流水號,根據一個 global variable 來的。
class Foo {
static size_t _count = 0;
size_t _id;
Foo() __transaction_atomic {
_id = _count++;
}
}
function transaction block 也可以跟 try block 混在一起。
try __transaction_atomic : Foo() {
}
catch (...) {} // catch 不是 transaction 的一部分
__transaction_atomic try : Foo() {
}
catch (...) {} // catch 是 transaction 的一部分
__transaction_cancel
可以用來放棄一個 transaction,只能寫在 atomic transaction 裡面。transactional expression 不能 cancel,function transactional block 也不能 cancel。
__transaction_cancel [[outer]];outer cancel statement 只能寫在被標明為 transaction_may_cancel_outer 的 atomic transaction 或是 function block transaction 裡面。
幹……怎麼會這麼多……後面還有……好想哭……2012年4月5日 星期四
喜歡上一個漫畫家「冬目景」
喜歡上一個漫畫家「冬目景」。
最早是在,可能有十年了吧,在表弟家看到的一本漫畫叫作《黑鐵》。一個人被改造成機器人的劍客故事,聽起來既白癡又低能,不知道是搞笑漫畫還是熱血打鬥,但是實際上卻是一點點抑鬱的,很觸動人心深處的故事,跟劍客不劍客沒什麼關係。就像很多科幻小說,除去科幻的部份,卻還是很精彩,我想,好的故事,跟背景設定無關,而是故事本身的重點是在人身上,而黑鐵就是這樣的故事,重點在人身上。後來在清大念研究所,宿舍的交誼廳也放著一套黑鐵,但是並沒有全部,這次我很仔細的全部看了,更喜歡這個漫畫了,我發現他不只是故事好,畫面也總是帶一點點悲戚的美,歹勢阿,我文筆很爛,想不到什麼好詞描述,不過就是這樣。
過了幾年的現在,又想到這部漫畫,想起來,想去把結局也看完,上往搜索了一下,才發現這個漫畫連載中斷,所以沒有結局了。也知道了作者叫作「冬目景」,ㄟ,我發現我看過她的《白老鼠遊戲》但是卻沒什麼特別的印象。發現冬目景的作品很多都中斷連載,不知道原因,只覺得很可惜。看了一些作品的介紹,看到《昨日之歌》好像會是我喜歡的類型的故事,就去找來看了,結果……真的很喜歡,只是看的過程常常會不小心的掉下眼淚,冬目景的畫真的很漂亮 ^^ 應該說畫面看起來常常髒髒的,筆觸也斷斷續續的,但是看起來就是很漂亮,筆觸成熟,看起來很隨意,不會講,懶的寫了 @_@ 反正很喜歡,害我很想寫部落格,不過我寫不出什麼毛,所以還是停吧...
2011年11月4日 星期五
Vim 自動提示編譯錯
Vim error highlight
2013/08/12 更新[[[ 自從 YouCompleteMe 這個 plugin 出來之後,一堆 plugin 都變成垃圾了,包含這篇文章的所有內容,也變成垃圾了,請直接參照 https://github.com/Valloric/YouCompleteMe 然後不用再往下讀了。 ]]]一直想要一種功能,就是 vim 可以自動顯示錯誤的程式碼,像是 visual studio 或是 eclipse 那樣。最近摸索了一下終於讓我實現了這個功能 :P
事先準備
-
可以在背景執行動作,不會妨礙現在進行的編輯作業。
-
可以把 quick fix list 裡面的錯誤,轉而使用 syntex highlight 直接在編輯視窗內標示出來,看一下官方網站的 screensho:
進行修改
理論上這兩個東西都有安裝的話,好像就成功了,不但可以在背景編譯,還可以在編輯畫面表示錯誤。當然,實際上這兩個都安裝了以後,還是沒辦法直接達成目的的,不然我也不用寫這篇文了 XD
經過 trace 兩者的 script 以後,發現原因在於 errormarker 是透過 hook QuickFixCmdPost event 來呼叫 SetErrorMarkers() 函數來把 quick fix list 裡面的資料轉標到編輯畫面。當平常使用 :make 的時候,QuickFixCmdPost 會被觸發,但是當透過 AsyncCommand 進行 make 的時候,似乎不會觸發 QuickFixCmdPost 事件,於是 SetErrorMarkers() 也不會被呼叫,然後一切都完啦。所以我們的目標就是讓 AsyncCommand 「正確」的呼叫 SetErrorMarker() 就好。
先講一下 AsyncCommand 的機制,AsyncMake 其實是一個 wrapper,包住了 asynccommand#run(make, asynchandler#qf) 這個動作,後面那個 asynchandler#qf 是等到 make 結束以後,用來處理結果的 handler,而這個 handler 會在 asynccommand#done() 的時候被呼叫,所以我們只要在 asynccommand#done() 裡面呼叫 SetErrorMarkers() 就可以了。
asynccommand#done() 在 .vim/bundle/asynccommand/autoload/asynccommand.vim 這個檔案裡面,找到他,
2 " Called on completion of the task
3 let r = s:receivers[a:temp_file_name] "handlers are registered in s:receivers
4 if type(r.dict) == type({})
5 call call(r.func, [a:temp_file_name], r.dict)
6 else
7 call call(r.func, [a:temp_file_name])
8 endif
9 unlet s:receivers[a:temp_file_name]
10 delete a:temp_file_name
16 endfunction
在函數結束之前加上呼叫 SetErrorMarkers() 的程式碼,變成這樣:
2 " Called on completion of the task
3 let r = s:receivers[a:temp_file_name] "handlers are registered in s:receivers
4 if type(r.dict) == type({})
5 call call(r.func, [a:temp_file_name], r.dict)
6 else
7 call call(r.func, [a:temp_file_name])
8 endif
9 unlet s:receivers[a:temp_file_name]
10 delete a:temp_file_name
11
12 if exists("*ExposeSetErrorMarkers") " Check and
13 call ExposeSetErrorMarkers() " Call ExposeSetErrorMarkers()
14 endif " here
15
16 endfunction
注意到我們呼叫的不是 SetErrorMarkers() 而是 ExposeSetErrorMarkers(),這是因為 SetErrorMarkers() 是一個 script local 函數,只能在 script 內部被呼叫,所以我在 errormarker.vim 裡面加了一個函數來曝光 SetErrorMarkers():
2 call s:SetErrorMarkers()
3 endfunction
這樣以後只要輸入 AsyncMake 就可以在背景編譯,編譯完成以後自動 highlight 錯誤了。為了方便使用,我還加上了幾個 autocommand:
2 au BufWritePost *.c AsyncMake
3 au BufWritePost *.cc AsyncMake
4 au BufWritePost *.cpp AsyncMake
這樣編輯 c/c++ 的時候每次存檔都會自動編譯然後 highlight 錯誤。
其實我知道我的改法很醜啦,但是他 work XD 如果有人知道比較優雅的改法請跟我分享阿~
小記:我發現我 ubuntu 安裝 vim 的話,是沒辦法作這件事的,執行 script 的時候會發生錯誤,但是安裝 vim-gtk 的話就可以了,不知道是因為設定的時候哪個選項沒開,但是總之如果有人遇到一樣的問題的話請試試看安裝 vim-gtk。
2011年9月26日 星期一
眷村有個每天騎腳踏車巡邏的老人今天睡覺沒醒
這個老人每天都打著赤膊,騎著快解體的淑女車,大街小巷的到處跑,也沒有要去哪裡,反正就是一直到處騎,但是範圍非常的大,航跨十公里的範圍內都有機會看到他打著赤膊慢慢的騎著腳踏車,雖然沒有人從頭到尾跟著他走過一趟,但是我相信他每天的路線應該都是一樣的。
騎了幾十年了,大家都認識他了,他以前是一邊騎,一手抓著米酒頭在灌,臉紅紅的,身體也紅紅的。十幾年前開始,酒沒了,取而代之的是菸,一邊慢慢的騎車一邊抽菸。手上沒菸的時候如果遇見認識的人,就會先擋兩根來(也不會還 XD)。像我小舅舅每天晚上固定的時間就會在外面抽菸,也固定給經過的他兩根菸。
厲害的是他向來都只穿一條褲子,從來不穿上衣。除了有一年冬天超冷,某天他穿了一件短袖上衣出來,大家沒認出是他 XD
外婆說今天上午那人一樣到處騎腳踏車,遇見認識的人也一樣擋兩根菸,一樣都是活跳跳的,中午那人吃飽了以後去睡午覺,下午快五點的時候,眷村裡面其他的老人去他家要去找他吃晚餐的時候,發現他還在睡。
其實永和這個大陳義胞五和新村已經很老了,眷村裡面,老一輩的,小時候跟著兩蔣來台灣的,現在的年紀最起碼都六七十起跳,都是隨時就會回家的狀態。眷村裡面,巷弄間,總是家家戶戶的人都坐在門口的藤椅上聊天,誰家發生什麼事,誰家女兒嫁人了又離婚了,誰家曾孫出生了,大家都知道。這些年來,每兩三年總有一個兩個人就不見了,就不會再出現了。
所以當鄰居發現他還在睡的時候,也沒很驚訝。因為這也沒什麼,就是回家時候到了。通知了還在陽明醫學院唸書的女兒,救護車來就把人帶走了。
外婆:「早上騎車的時候還活跳跳的,怎麼中午睡覺就不醒了 -_-」
小舅:「本來每天都被要兩支菸還蠻賭爛的,久了也有感情了....」
至少是個走的安詳。