2014年8月26日 星期二

我是「媽寶級白目」

突然發現自己不知道從什麼時候開始,
已經變成所謂一直發政治文的「政治魔人」了……

還記得以前覺得政治很無聊、很髒、不該碰……
覺得會去看政論節目的人腦袋都有問題……

從學校畢業以後進入業界,
然後發現這個業界有著很病態的超時工作文化,
雖然我自己沒遭到壓榨,但我開始注意勞工權益,
參加了電子電機資訊工會,然後開始慢慢了解「爭取」是什麼。

然後第一次去了抗議現場,
發現即使我們工會人不多,
但每次活動總有很多其他的社運團體會來支援我們。

然後第一次去了遊行活動,
然後才知道原來早就有很多人已經在街頭上努力了這麼久,
然後知道我們也應該在別的活動去支援別人。
然後知道在這個支援別人的過程,
我們也了解了別人所面臨的,別的社會議題。

然後慢慢知道了一些歷史課本不會寫的東西,
然後慢慢知道了一些新聞媒體不會報的東西,
然後真正體會了當初曾華璧老師在課堂上講的東西。
然後突然之間,那是一個相變的過程,很快速,
我的想法在很短的時間裡面蛻變的很快。
我想那也許就是「公民覺醒」。

我還記得以前有一次看到李敖在電視上說:

  一個真正民主的國家,投票率不會太高,
  如果投票率很高,代表政治對人民生活的影響非常大,
  『我支持的候選人沒上,我要去自殺!』這是不對的!
  民主社會的話,誰當選,對百姓的生活都不應該差太多,
  如果一個社會裡面『誰當選』差很多的話,
  那這個社會就不民主。

當時我的想法是:

  說的真好,那些對政治很投入的人真是白痴,
  台灣就是因為有你們這些人,才顯得台灣不民主。

現在想想當時真是愚蠢的不可思議。
不是因為他們的投入,才顯得台灣的不民主,
而是因為台灣不民主,害得他們不得不投入。

想到讀 Game Theory 講選舉的時候,裡面提到兩種模型,
一種是候選人可以選擇要提出什麼政見的模型,
一種是候選人無法選擇要提出什麼政見的模型。
第二種是因為政見不一定會履行,候選人完全可以胡說亂騙,
所以他的政見應該是他過去作了什麼,不是現在說了什麼。
而理智的公民是有記憶的,我們會記得你過去的為人處事,
所以實際上,候選人無須提出政見,
我們看你過去作了什麼,就知道你未來想幹麻。

兩種模型當然都跟現實有些距離,但還是很有啟發性,
特別是第二種,實質是遠比第一種好的多,
現實若偏向第一種,代表選民都是健忘的,
現實若偏向第二種,而候選人就無法欺瞞選民。
但要作到第二種的前提,是選民必須要長期的關注政治,
知道那些人到底在幹麻,而不是等到快要選舉的時候,
才看看選舉公報、甚至不看選舉公報就投票了。

如果是這種狀況,
那那些選民不過就是四年投一次票政治植物人罷了,
他們根本不會思考,也沒有記憶,
他們有民主的權利,卻沒有民主的能力。
他們胡亂的投票,結果卻是大家一起背負的。
讓所有人一起承擔選出惡劣候選人的責任。

這是不對的。

所以,如果你有投票權,
那麼,關注政治,就是你的社會責任。
因為你的那一票會影響別人,
放空亂投,是讓別人在背負你本來應該背負的責任。

請面對這個事實:「台灣,不是一個民主國家」,
選錯人,很嚴重,請關注政治,負起你的社會責任。

「太辛苦了,而且很沈重」你說

所以大家要看我的臉書。

每天都有又酸、又靠北、又北七的苦中作樂政治文,
把沈重跟嚴肅寄託在低能跟歡樂當中,
我發現我的文章越來越像國寶級白目了,
我真的有夠白目,不過我不到國寶等級,那就媽寶好了,
以後請大家叫我「媽寶級白目」。

2014年5月2日 星期五

幫 BBS client 加上黑名單的功能

一開始嘗試從 PCMan 下手去改,但遇到幾個問題:
  1. PCMan 要 MFC,MFC 要錢,我沒有 MFC
  2. PCMan 的 code 有點難懂,看不懂很懂 Q_Q
  3. PCMan 只能在 windows 上面
於是放棄。

接著嘗試 PCManX,但發現 windows 上面要跑 GTK+ 也有點麻煩 >"<~
放棄。

然後嘗試自己從頭手動打造,為了跨平台,決定用 Qt,就開始動手,寫著寫著,突然想到「不對,應該有人作過同樣的事情…」於是再繼續 survey,發現果然有個叫做 Qelly 的 telnet client,看了一下 code,用 Qt,有 open source 放在 github 上面!程式碼品質也不錯,似乎不難修改。於是就開始動手 :P

弄了 24 小時以後,有一點初步的結果 XD

目前可以在「文章選單」跟「串接」的時候把特定作者的文章抹黑,但「文章內容」還沒辦法過濾,這樣就沒辦法在用 =, [, ] 瀏覽文章的時候時候過濾文章。希望這個週末可以完成

2014年3月19日 星期三

3/18深夜 ~ 3/19早晨服貿立法院公民運動第一線經歷

3/18 夜晚,已經要上床睡覺了,收到朋友的消息說立法院有事。上網看了一下,氣到睡不著,3/19 一點半,決定衝去現場。

零、地圖

一、初到現場,濟南路

停好車,走了一段路,聽到有人在喊話,我猜是現場,走過去,看到很多人。坐下來聽了快半個小時,不知道自己來幹嘛的,就有點想要走了,因為還要上班。這時候突然聽到募集「要找五十個人去青島東路支援」。我想說好吧,好歹有個目標,就趕快舉手跑去,結果發現帶隊的人是黃國昌耶!我因為跑很快,所以就在他正後方,一直跟著他走,聽他聲音,他嗓子已經完全啞了。

二、主戰場,青島東路

到了之後,發現很多人都在馬路上坐著,一樣是聽講話,聽聽聽聽聽聽聽,過程一直強調「警察不動,我們不動」但警察如果動了,我們就會行動,一直聽,聽了差不多一兩個小時,快四點吧,一直被蚊子咬,我又想要回家了,就一邊按耐住蚊子咬,一邊睡覺,睡著睡著……突然聽到「警察攻堅了!

「警察不動,我們不動!現在警察動了,我們要不要去救我們的夥伴?」
「要!」
「好,大家翻過去。」

幹!頓時睡意全消!包包往背上一甩,三兩步就跳上去,因為我的位置就在牆邊,翻牆進去的很早,一下就衝到最前線!被警察擋住,但警察因為被我們衝,所以也放棄攻堅了,我們就 hold 住。 但這個時候還沒衝到樓梯上。樓梯是警察的地盤。樓梯下面被警察團團圍住。就坐下來開始睡覺,這個時候已經不想走了,決定呆到天亮。

睡著睡著,又聽到「警察拖人了!」

看過去,從窗戶看到裡面警察正在拖人,一個小女生被一群警察抓住!動作很大,他媽的很粗暴,而且因為就在窗邊,看得很清楚,女生往窗戶邊掙扎,一直想要掙脫,但是被警察抓住!這時候窗戶邊的民眾衝上去也把警察抓住!兩邊拉扯很久!現場超級火爆的!非常吵鬧!那時候所有的聲音都混在一起!不時可以聽到「警察打人了!!」然後就會聽到更多的怒吼聲,非常可怕。透過窗戶可以看到裡面的警察不時有拖人的動作,好幾個人被抓。

後來警察又幹了一件很蠢的事「把窗簾拉上」才拉一半,就被人發現

「警察拉窗簾!!」
「拉個屁阿!怕人看嗎!!」

結果警察也隔著窗戶跟民眾對罵!立刻有人衝上去把窗簾抓住,不讓警察關上,警察也把窗簾抓住!結果整條窗簾被扯下來,看光光了。

結果過沒多久,警察又攻堅了,幹!馬上爬起來!衝!警察被沖退!退到門口裡面樓梯被我們攻下來了,但進不了門。

這時候開始就隨時都很緊張,根本無法休息,警察一直有動作,一直有動作,每次警察有動作,我們就往裡面擠,但裡面警察密度太高,根本擠不進去

突然有人喊「把警察拉出來」
警察一聽到這句,立馬有反應「敢拉警察!我就抓人!」
看樣子警察真的會怕拉,不怕推。

但說要抓人這句,有嚇到人,嚇到大概安靜了一秒鐘吧

「抓就抓阿!」
「抓我阿!」
「抓我!我第一個!」
「我第一個!」
「把警察拉出來!!!!!!!!!」

大家衝上去拉了,天哪…門口那個警察一看,靠北,這些人不怕!立馬兩手兩腳撐住門框,結果大家就開始拉警察!警察後面兩個人抱住他,兩個人後面又好幾個抱住他們,就像是一串葡萄,然後你摘最下面的那一顆。

「拉!拉!拉!」結果就拉出一顆警察,然後警察就被接力的拖出去,就像滑板一路滑出去一樣。一片混亂當中還有人偷打警察,結果偷打警察的人也立馬被旁邊的人打! 「打什麼警察阿!人民是警察的保母!」

最後發現不管推拉都沒用,太難把警察清除了。且那時候警察已經停止攻堅了,黃國昌就叫大家冷靜,休息。

以上這樣衝突、休息重複了好幾次。不知不覺竟然已經七八點了。

黃國昌老師現場開了記者會,描述了訴求:

  1. 馬英九道歉、江宜樺下台
  2. 服貿退回
  3. 警察撤出立法院

三、中山南路,立法院正門口

然後我就離開準備去洗澡上班了,從青島東路,經過中山南路,準備要去牽車的時候,經過立法院大門口,看到門口也有警察跟民眾對峙。警察在立法院門口裡面,民眾在外面,看起來沒什麼事,很和平 我就繼續往前走,走過去之後,突然聽到很大聲的騷動。轉頭一看!幹!警察衝出來了!!好多!!好多!!!!!!!!兩群人直接衝撞在一起!!!!!!!

媽啦!衝上去再說!就跟拿盾牌的警察在那邊對衝!擠在第一排!死命的推!!!幹!可是警察人真的超多,力氣真的超大的!!被推的一直後推!我面前那個拿盾牌的,是一個蠻年輕的警察,臉色感覺已經很差了。我不管怎麼推都推不動,突然想到剛剛「用推的沒用」我就抓住他的盾牌,有幾個人也跟著我抓盾牌!那警察瞬間暴怒!整個眼神馬上改變,動作超大的!整個就是真的殺紅眼的眼神!抓住盾牌就大力往眼前的民眾身上揮,差點打到一個媽媽!我一看,他媽的!

我操你媽的你在幹嘛阿?你他媽的真的殺紅眼了阿?!
幹!你演演戲也就算了,你現在是他媽的真的要打人阿?!
你現在到底在幹嘛!?你在幹嘛!?你知不知道自己在幹嘛?!

我把他盾牌剝開,衝到他面前,只著他一直罵,罵到他好像有點龜了……
盾牌放下了……然後我們就衝上去繼續抓他的盾牌 = =

另外一個警察,盾牌一被另外一個人抓,立馬就「不准抓我的盾牌」超兇的!我一看到,就跑過去「怎樣!抓我逆?」然後就一起搶他盾牌,但都沒搶到 = =

後來持續對衝了幾分鐘以後,雙腳快軟掉了,我就想說「幹,我要回家洗澡上班了」就轉身離開。

才一轉身,手就被抓住,轉身一看,是一個女生,因為氣力放盡,累到臉都扭曲了。
一手推著警察的盾牌,一手抓住我:「你是男生…力氣大…」

……………幹!!!!!!!
只好轉過身再衝!!!!!!
又衝了不知道多久,警察停止推進了,我猜我的臉也跟女生剛剛一樣扭曲

終於可以回家了,結果忘記車停在哪,找了十五分鐘才找到,腳都快走不動了。

強烈建議大家一定要站到第一排搖滾區,體驗一下

感想

沒寫到感想?感想就是很想哭。

現場絕大部分的時間都很和平,但偶而會有非常火爆的場面出現。每次發生推擠的時候,我都很想哭,很生氣,氣為什麼同樣都是老百姓,卻要站在陣線的兩邊。

可以的話,民眾也想在這個時間躲在棉被裡面睡覺,而不是衝到這個地方,用盡全力的跟本來應該是保護自己的警察對峙跟衝撞。

可以的話,警察也想躲在家裡面睡覺,陪伴老婆跟孩子,而不是在死命的去把正在行使公民義務的學生拉出來,但又害怕傷害到任何人,而且如果被打還不能還手。

而真正該為這些事情負責的人,卻躲在棉被裡面睡覺,睡得很開心。

但為什麼民眾還是要站出去,因為如果我們不站出去,那些人永遠都可以作他們想做的事情且不用付出任何代價

2013年8月17日 星期六

最近浪費很多時間打魔力寶貝 = =||

最近又跑回去玩魔力寶貝

因為很懶惰,所以不想自己練等級等技能,就寫了 bot,讓 bot 自己練等級、戰鬥技能、採集材料、製作裝備跟料理、打魔石賣 npc 賺錢等等。

單一 bot


我發現有 bot 以後,遊戲變得好玩多了(不要戰我 XD)我下午把 bot 開好,人跑出去找未來的配偶欄約會,晚上回到家以後,錢多了十幾萬,就可以拿去買裝備、料理、藥水。然後就可以出任務去打魔王了,遊戲真好玩阿(好像有點諷刺?)

一開始我的 bot 只能自己一個人活動,沒辦法組隊,但是魔力寶貝不組隊,能去的地方就很有限,因為比方說 50 級的人物,不組隊的話,一個人頂多走到怪物等級平均 30 的地方,再上去就沒辦法了,會死。但 50 級的人物打 30 的怪物,其實幾乎沒有什麼經驗值,而且 30 的地方,收入也很有限。講有限其實也不對,因為他全自動,我去睡覺十個小時,他就打了十個小時,每小時的收入雖然低,但重點是「持續不斷」,後來我發現這「持續不斷」是很恐怖的事情,因為不管你在睡覺還是在上班,他都在賺錢。基本上賺錢的速度是超過花錢的速度的,沒幾天,我的金錢就到達單一人物的上限了,開始放到別人的身上去。

三隻 bot 組隊

錢可以一直增加,但等級還是原地踏步。

接下來我嘗試改進 bot,希望我的 bot 可以彼此溝通,可以組隊。這個開發的過程,當初在交大修的「分散式演算法」在這邊…完全沒有派上用場!因為即使多隻 bot,也都還是跑在同一台電腦上,架構很簡單,並不是分散式系統。我有一隻中央的 Mastermind 監控所有人活動的,由他統一規劃協調所有人的動作。經過一陣子的開發以後,我可以成功組隊,協同動作了。現在 50 級的人物,三隻組隊,我可以去到有 4x 級怪物的地方練等級。

為什麼三隻?因為遊戲限制了多重開啟遊戲的上限,就是三個。

但遊戲內容的設計,一個隊伍最多五個人,也就是說,如果你能五個人組隊,基本上,遊戲內所有的場景你都可以去了。因為他的難度不會設計成五個人都還沒辦法打。那限制你只能三開,你就只能某個程度的「自己玩自己」,因為凡是重要的任務,沒有五個人是沒辦法解的。這樣就強迫你跟別人互動了,個人認為這是很正確的設計,線上遊戲就是要跟別人一起玩嘛(最早只能一開,強迫你要跟另外四個人互動 XD)。

五隻 bot 組隊


問題是我是 bot,我沒辦法跟別人互動。所以凡是重要的任務我都不能解 XD

而且只有三個人的話,能去的地方還是比較受限,只能挑怪物等級稍低的地方,等級經驗值的提昇還是很慢的,但想要突破這點的話,接下來就真的要靠破解的了。

執行檔 instance 三開上限


首先,我發現他開第四隻程式的時候,會跳出一個對話框


經過在強者雲集的 plurk 上面請教以後,決定使用有免費版本的 OllyDbg 來進行破解的動作。雖然還有更強的 debugger,但要收錢,因為我沒有錢,所以我決定暫時先尊重一下智慧財產權。使用 OllyDbg 執行遊戲主程式,當然,馬上就跳出了上面這個對話框。回到 OllyDbg 的視窗,按下快速鍵  F12 ,中斷遊戲的執行。然後看看 call stack。根據我的想像,遊戲的程式碼應該是有一段類似這樣的

if(check_exceed_multi_inst_limit()) {
    MessageBox("已經達到最高限制多開");
}

這個時候,CPU 會執行到 MessageBox 這個 system api 裡面,然後在裡面等待,理論上,我只要往 call stack 往上走幾步,應該就可以看到條件判斷的地方。於是我在 OllyDbg 裡面按下  ALT-K  叫出 call stack 視窗。看到如下


看到  [Called from]  那欄,可以看得出來,灰色那條,還屬於遊戲的部份,再追進去就是 system api 的部份了,於是我就雙點灰色 Called from 那欄,程式跳到了呼叫 MessageBox 的地方。




我已經加上註解了,可以看到呼叫檢查函數的地方,還有 if 的點, JE  就是 jump if equal,如果成立的話,就會跳過 MessageBox() 的部份,所以我猜我要改的就是他,把他從  JE  改成  JMP ,無條件跳躍。雙點那段程式碼 OllyDbg 就會跳出組譯視窗,讓你修改那段組合語言。


改成


然後把改過的東西存起來



然後再執行一次程式!成功了!這個對話框不再出現了!但下一個跳出來!


可惡,不知道他是怎麼曉得我沒有透過領航員執行程式的,不管,照剛剛的方法繼續破解他,成功破除了這個檢查,這邊不寫細節了,因為這個方法是錯的。程式之後繼續跑,跑到一半會說找不到檔案,然後 segfault。我猜想可能是領航員有 pass 某些參數給遊戲主程式。但如果用滑鼠雙點遊戲主程式就拿不到這些參數,所以無法執行。但我要怎麼知道他下了什麼參數?

我寫了一隻程式,來取代遊戲主程式,他什麼都不作,只會把收到的參數印出來。

#include
#include

int main(int argc, char* argv[])
{
    std::ofstream fout("args.txt");
    fout << argc << std::endl;
    for(int i = 0; i < argc; ++i) fout << argv[i] << std::endl;
}


抽換遊戲主程式以後,使用領航員執行遊戲,發現竟然無法執行!天哪,人間慘事。原來領航員會去檢查主程式的 hash code,我測了一下,發現他用的是 md5,但是 hash code 是當下從網路上下載的,而且也會檢查領航員自己的 hash,這樣麻煩了,即使我竄改了主程式,我也無法透過領航員執行他,除非……我破解領航員檢查 hash code 的機制……

接下來這邊真的是很恐怖的組合語言追蹤地獄,我已經不想再重複一次了,直接點重點,破解最重要的就是抓到關鍵的中斷點,不然程式這麼大,你不可能慢慢追。我花了很多時間,發現他是把 md5 下載存到一個檔案裡面,但這個檔案一瞬間就會被砍掉,我想了想,應該要把中斷點下在「建立檔案」的時候,就可以抓到他寫檔的那瞬間,然後看看檔案內容是什麼。

按下  ALT-E  可以看到所有使用到的 module,選擇遊戲的主程式


然後列出所有「跨模組的函數呼叫」


我們要找 CreateFile 這個 api,打上中斷點。



稍微跑幾步,讓她把檔案寫好,然後打開檔案來看,發現格式竟然是 INI,太好了,這是一個很大的提示,因為 Windows 有現成的 api 讀取 INI 格式,叫做 GetPrivateProfileXXX()。馬上在 inter-modular-call 裡面找到  GetPrivateProfileString()  這個人,對他打中斷點,然後接下來就是無盡的追蹤地獄,最後終於成功找到三個關鍵點,統統改成 JMP。細節不詳述。




現在終於可以成功的跳過 md5 的檢查,順利執行第四個 instance,來登入遊戲!

「連線失敗。」
「連線失敗。」
「連線失敗。」
「連線失敗。」
「連線失敗。」
「連線失敗。」
「連線失敗。」
「連線失敗。」
「……………………」

我太天真了,他在 server 端有作檢查!

一旦發現來自你這台電腦的連線數目超過三個,就不讓你登入。

該死,server 是檢查連線來自的 ip 嗎?那我要怎麼生出白花花的 ip?就算我能用別的電腦生出別的 ip,而且一旦跨 ip,就是跨電腦,我就沒辦法讓我的 bot 都跑在同一台機器上,那 bot 的協同部份就必須要加上網路部份的程式碼,複雜度瞬間高很多。

跟強者我同事 runastorm 討教過後,決定寫一隻 proxy,架在另外一台電腦上,然後遊戲主程式的封包都走這個 proxy,這樣 server 端看到的來源,就是另外一台電腦,也就是 proxy 的 ip。survey 了一下,用 python 寫出一個簡單的 proxy。

接下來的是要作三件事情:
  1. 要知道 server 的 ip 到底是多少,這樣 proxy 才能連過去。我使用  netstat -n -b  以後,順利看到 server 的 ip。
  2. 要竄改主程式,讓他連到我跑 proxy 的主機去。
  3. 要一個 dispatcher 在前端,當 instance 只有三個的時候,使用本來的執行檔,超過三個時候,使用竄改過 server 位置的執行檔。
以上三件事情都不困難,我花了點時間分析到怎麼改他連線的位置。成功,執行第四個 instance!

「連線失敗。」
「連線失敗。」
「連線失敗。」
「連線失敗。」
「連線失敗。」
「連線失敗。」
「連線失敗。」
「連線失敗。」

可惡阿!竟然不是看 ip!

浪費我好多時間,可惡,到底看什麼?如果不是看「連線的ip」,難道是遊戲主程式在執行以後,還有把自己的 ip 送過去?server 看的是連線以後送去的資料裡面的嗎?是的話我就完了,因為遊戲封包有加密,我死定了,我試過很多次了,但無法看出他加密的規則,更別說要破解了。看了一下 proxy 的 log,確定連線有成功,來往兩個封包以後連線就被切斷了。看樣子的確是這幾個封包裡面有送資料,檢查不過以後才斷線。但就像剛剛講的,我破不了加密,如果能破的話,我就可以解開,竄改封包,然後再送,但破不了,就沒辦法。

怎麼辦…

釜底抽薪

既然無法竄改已經加密的封包,我竄改還沒加密的總可以吧!遊戲主程式要得到自己的 ip 一定也要呼叫一些系統 api,但不知道是哪個呢?突然想起來!當初在追蹤遊戲主程式的時候,曾經看到他的字串表裡面有一個  "ipconfig /all > mac.txt"  ,哈!九成是用 ipconfig 拿 MAC 碼,而且我發現他還是透過 system() 這個函數來呼叫 shell。這樣的話,裡論上我只要抽掉這個文字檔就可以了。

一樣,使用中斷點,把 system() 打住,偷看一下 mac.txt 檔案內容,確定如我所想,裡面只是很單純的 ipconfig /all 的輸出,偷偷把 MAC 的地方改掉幾個字,存檔。

開啟第四個 instance……
登入……

「請選擇角色」


孝心感動天!!!!!!!

成果來一張



五個視窗,關鍵資訊都馬賽克掉了 ^O^

終於,可以來擴充我 bot 組隊的 script 了 :D

架站的看到不要擋我阿,我只有自己用 Q_Q

2013年1月10日 星期四

C++ enum member function

之前重構公司的程式碼,發現一個需求:希望 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 用 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++ 的 一樣的事情,但是沒有 transactional block 的能力。
    stackoverflow 上面有一篇討論,C# 沒有語言層面的支援,可以透過 library 來完成。
  • Java
  • 也是一樣,透過 library 完成。
  • Golang
  • 應該是沒打算提供 transactional memory,根據這篇討論,go 提供 message passing 作為取代。
  • C++
  • 有兩個 compiler 提供了 atomic block 的實作,一個是 Intel C++ compiler(真是太強了),另外一個是 g++(嘖嘖),大概最後還是選擇以類似 C++ 的形式為目標。

找到 gcc 的 transactional memory 的 spec,正在讀,作一點筆記。

__transaction_relaxed and __transaction_atomic

有兩個關鍵字:
  1. __transaction_relaxed
  2. 用來表示........ 其實我沒看懂。好,看到後面,懂了。這種 transaction 比較 weak,他只保證 transaction 「看起來」沒有跟別的 transaction 有交錯執行,但是,他還是可能跟別 thread 有交錯。舉例來說的話:
    Thread AThread B
    __transaction_relaxed {
        a = 1;
        b = a;
    }
    
    a = 3;
    
    這樣 Thread A 的 b 有可能會是 4。因為被另外一個 Thread B 的 a = 3; 亂入了。但是呢,如果寫:
    Thread AThread B
    __transaction_relaxed {
        a = 1;
        b = a;
    }
    
    __transaction_relaxed {
        a = 3;
    }
    
    那 b 就保證會是 1 了。因為 relaxed transaction 有保證這個 transaction 看起來不會跟別的 transaction 交錯執行。
    這邊一直強調「看起來」是很重要的,因為實際上,根據實作的細節(且為了效率的考量),執行的過程本身可能還是交錯並行的,只是透演算法的控制,讓執行的結果跟沒有交錯一樣,所以程式設計師就可以很直覺的認定這段程式碼的形為就如同一般 single 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

  3. __transaction_atomic
  4. 用來表示(跟 __transaction_relaxed 比起來)更嚴格的的 transaction。這大概是我們要做的,但不確定前一種要不要作 :P
    這邊現在我也看懂了,也就是說,在 atomic transaction 之下,即使只寫
    Thread AThread B
    __transaction_relaxed {
        a = 1;
        b = a;
    }
    
    a = 3;
    
    我們也可以保證 b 百分之一是對的,會是 1。哇勒~!這種東西要怎麼作阿!!!
    特性:
    • 只有 "safe" code 可以放在裡面(等一下在 4.2 節說明)
    • 可以被 cancel
    • 不能有 side effect(比方說 IO)

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 裡面。

幹……怎麼會這麼多……後面還有……好想哭……